interfaces/apparmor: add function for probing / mocking
This patch adds a new function for probing for supported apparmor features as well as for mocking that. Unlike the current code in the release module (where it doesn't really belong as it is no longer based on looking at the /etc/os-release file). The improvement from the old code is that the "feature level" is not a boolean, there are three values and more may be added if desired. They are "none" (apparmor not enabled at all), "partial" (apparmor enabled but some features are missing) and "full" (all required features availalble). In the next few patches I will transition the old release interface over to this and will start using it to make better decisions as to how apparmor backend should be loaded and how it should operate. Signed-off-by: Zygmunt Krynicki <me@zygoon.pl>
- Loading branch information...
- +109 −0 interfaces/apparmor/probe.go
- +38 −0 interfaces/apparmor/probe_test.go
| @@ -0,0 +1,109 @@ | ||
| +// -*- Mode: Go; indent-tabs-mode: t -*- | ||
| + | ||
| +/* | ||
| + * Copyright (C) 2017 Canonical Ltd | ||
| + * | ||
| + * This program is free software: you can redistribute it and/or modify | ||
| + * it under the terms of the GNU General Public License version 3 as | ||
| + * published by the Free Software Foundation. | ||
| + * | ||
| + * This program is distributed in the hope that it will be useful, | ||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| + * GNU General Public License for more details. | ||
| + * | ||
| + * You should have received a copy of the GNU General Public License | ||
| + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| + * | ||
| + */ | ||
| + | ||
| +package apparmor | ||
| + | ||
| +import ( | ||
| + "io/ioutil" | ||
| + "os" | ||
| + "path/filepath" | ||
| +) | ||
| + | ||
| +// FeatureLevel encodes the kind of support for apparmor found on this system. | ||
| +type FeatureLevel int | ||
| + | ||
| +const ( | ||
| + // None indicates that apparmor is not enabled. | ||
| + None FeatureLevel = iota | ||
| + // Partial indicates that apparmor is enabled but some features are missing. | ||
| + Partial | ||
| + // Full indicates that all features are supported. | ||
| + Full | ||
| +) | ||
| + | ||
| +var ( | ||
| + // featureSysPath points to the sysfs directory where apparmor features are listed. | ||
| + featuresSysPath = "/sys/kernel/security/apparmor/features" | ||
zyga
Owner
|
||
| + // requiredFeatures are the apparmor features needed for strict confinement. | ||
| + requiredFeatures = []string{ | ||
| + "caps", | ||
| + "dbus", | ||
| + "domain", | ||
| + "file", | ||
| + "mount", | ||
| + "namespaces", | ||
| + "network", | ||
| + "ptrace", | ||
| + "signal", | ||
jdstrand
|
||
| + } | ||
| +) | ||
| + | ||
| +// Probe checks which apparmor features are available. | ||
| +func Probe() FeatureLevel { | ||
| + _, err := os.Stat(featuresSysPath) | ||
| + if err != nil { | ||
| + return None | ||
| + } | ||
jdstrand
|
||
| + for _, feature := range requiredFeatures { | ||
| + p := filepath.Join(featuresSysPath, feature) | ||
| + if _, err := os.Stat(p); err != nil { | ||
| + return Partial | ||
| + } | ||
| + } | ||
| + return Full | ||
jdstrand
|
||
| +} | ||
| + | ||
| +// MockFeatureLevel fakes the desired apparmor feature level. | ||
| +func MockFeatureLevel(level FeatureLevel) (restore func()) { | ||
| + oldFeaturesSysPath := featuresSysPath | ||
| + | ||
| + temp, err := ioutil.TempDir("", "mock-apparmor-feature-level") | ||
| + if err != nil { | ||
| + panic(err) | ||
| + } | ||
| + fakeFeaturesSysPath := filepath.Join(temp, "apparmor") | ||
| + | ||
| + switch level { | ||
| + case None: | ||
| + // create no directory at all (apparmor not available). | ||
| + break | ||
| + case Partial: | ||
| + // create just the empty directory with no features. | ||
| + if err := os.MkdirAll(fakeFeaturesSysPath, 0755); err != nil { | ||
| + panic(err) | ||
| + } | ||
| + break | ||
| + case Full: | ||
| + // create all the feature directories. | ||
| + for _, feature := range requiredFeatures { | ||
| + if err := os.MkdirAll(filepath.Join(fakeFeaturesSysPath, feature), 0755); err != nil { | ||
| + panic(err) | ||
| + } | ||
| + } | ||
| + break | ||
| + } | ||
| + | ||
| + featuresSysPath = fakeFeaturesSysPath | ||
| + return func() { | ||
| + if err := os.RemoveAll(temp); err != nil { | ||
| + panic(err) | ||
| + } | ||
| + featuresSysPath = oldFeaturesSysPath | ||
| + } | ||
| +} | ||
| @@ -0,0 +1,38 @@ | ||
| +// -*- Mode: Go; indent-tabs-mode: t -*- | ||
| + | ||
| +/* | ||
| + * Copyright (C) 2017 Canonical Ltd | ||
| + * | ||
| + * This program is free software: you can redistribute it and/or modify | ||
| + * it under the terms of the GNU General Public License version 3 as | ||
| + * published by the Free Software Foundation. | ||
| + * | ||
| + * This program is distributed in the hope that it will be useful, | ||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| + * GNU General Public License for more details. | ||
| + * | ||
| + * You should have received a copy of the GNU General Public License | ||
| + * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| + * | ||
| + */ | ||
| + | ||
| +package apparmor_test | ||
| + | ||
| +import ( | ||
| + . "gopkg.in/check.v1" | ||
| + | ||
| + "github.com/snapcore/snapd/interfaces/apparmor" | ||
| +) | ||
| + | ||
| +type probeSuite struct{} | ||
| + | ||
| +var _ = Suite(&probeSuite{}) | ||
| + | ||
| +func (s *probeSuite) TestProbe(c *C) { | ||
| + for _, l := range []apparmor.FeatureLevel{apparmor.None, apparmor.Partial, apparmor.Full} { | ||
| + restore := apparmor.MockFeatureLevel(l) | ||
| + defer restore() | ||
| + c.Assert(apparmor.Probe(), Equals, l, Commentf("was hoping for %q", l)) | ||
| + } | ||
| +} |
1 comment
on commit ef40971
jdstrand
commented on ef40971
Aug 24, 2017
|
The direction of this commit is fine. Couple of small comments. |
Elsewhere we use dirs/dirs.go for this sort of thing.