From 8569ed03056ce39e0dc163747089ed4b60b1b9b1 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Sun, 22 Jul 2018 17:45:36 +0200 Subject: AppArmor: runtime check if it's enabled on the host Check at runtime if AppArmor is enabled on the host. Signed-off-by: Valentin Rothberg Closes: #1128 Approved by: mheon --- cmd/podman/create.go | 8 +++- hack/apparmor_tag.sh | 5 +- pkg/apparmor/aaparser_test.go | 3 ++ pkg/apparmor/apparmor_linux.go | 7 +++ pkg/apparmor/apparmor_unsupported.go | 5 ++ .../runc/libcontainer/apparmor/apparmor.go | 54 ++++++++++++++++++++++ .../libcontainer/apparmor/apparmor_disabled.go | 20 ++++++++ 7 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor_disabled.go diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 6a70e3f43..f147081d4 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -196,7 +196,7 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { } } - if config.ApparmorProfile == "" { + if config.ApparmorProfile == "" && apparmor.IsEnabled() { // Unless specified otherwise, make sure that the default AppArmor // profile is installed. To avoid redundantly loading the profile // on each invocation, check if it's loaded before installing it. @@ -231,7 +231,11 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { logrus.Infof("Sucessfully loaded AppAmor profile '%s'", profile) config.ApparmorProfile = profile } - } else { + } else if config.ApparmorProfile != "" { + if !apparmor.IsEnabled() { + return fmt.Errorf("profile specified but AppArmor is disabled on the host") + } + isLoaded, err := apparmor.IsLoaded(config.ApparmorProfile) if err != nil { switch err { diff --git a/hack/apparmor_tag.sh b/hack/apparmor_tag.sh index 6d4bec91b..0fd222210 100755 --- a/hack/apparmor_tag.sh +++ b/hack/apparmor_tag.sh @@ -1,7 +1,4 @@ #!/bin/bash if pkg-config libapparmor 2> /dev/null ; then - # Travis CI does not support AppArmor, so we cannot run tests there. - if [ -z "$TRAVIS" ]; then - echo apparmor - fi + echo apparmor fi diff --git a/pkg/apparmor/aaparser_test.go b/pkg/apparmor/aaparser_test.go index 9d97969c7..296c101ed 100644 --- a/pkg/apparmor/aaparser_test.go +++ b/pkg/apparmor/aaparser_test.go @@ -12,6 +12,9 @@ type versionExpected struct { } func TestParseVersion(t *testing.T) { + if !IsEnabled() { + t.Skip("AppArmor disabled: skipping tests") + } versions := []versionExpected{ { output: `AppArmor parser version 2.10 diff --git a/pkg/apparmor/apparmor_linux.go b/pkg/apparmor/apparmor_linux.go index 6e8b7f312..a09c5fc44 100644 --- a/pkg/apparmor/apparmor_linux.go +++ b/pkg/apparmor/apparmor_linux.go @@ -10,8 +10,15 @@ import ( "path" "strings" "text/template" + + runcaa "github.com/opencontainers/runc/libcontainer/apparmor" ) +// IsEnabled returns true if AppArmor is enabled on the host. +func IsEnabled() bool { + return runcaa.IsEnabled() +} + // profileData holds information about the given profile for generation. type profileData struct { // Name is profile name. diff --git a/pkg/apparmor/apparmor_unsupported.go b/pkg/apparmor/apparmor_unsupported.go index 0f1ab9464..df1336b07 100644 --- a/pkg/apparmor/apparmor_unsupported.go +++ b/pkg/apparmor/apparmor_unsupported.go @@ -2,6 +2,11 @@ package apparmor +// IsEnabled returns true if AppArmor is enabled on the host. +func IsEnabled() bool { + return false +} + // InstallDefault generates a default profile in a temp directory determined by // os.TempDir(), then loads the profile into the kernel using 'apparmor_parser'. func InstallDefault(name string) error { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor.go b/vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor.go new file mode 100644 index 000000000..7fff0627f --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor.go @@ -0,0 +1,54 @@ +// +build apparmor,linux + +package apparmor + +import ( + "fmt" + "io/ioutil" + "os" +) + +// IsEnabled returns true if apparmor is enabled for the host. +func IsEnabled() bool { + if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" { + if _, err = os.Stat("/sbin/apparmor_parser"); err == nil { + buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled") + return err == nil && len(buf) > 1 && buf[0] == 'Y' + } + } + return false +} + +func setprocattr(attr, value string) error { + // Under AppArmor you can only change your own attr, so use /proc/self/ + // instead of /proc// like libapparmor does + path := fmt.Sprintf("/proc/self/attr/%s", attr) + + f, err := os.OpenFile(path, os.O_WRONLY, 0) + if err != nil { + return err + } + defer f.Close() + + _, err = fmt.Fprintf(f, "%s", value) + return err +} + +// changeOnExec reimplements aa_change_onexec from libapparmor in Go +func changeOnExec(name string) error { + value := "exec " + name + if err := setprocattr("exec", value); err != nil { + return fmt.Errorf("apparmor failed to apply profile: %s", err) + } + return nil +} + +// ApplyProfile will apply the profile with the specified name to the process after +// the next exec. +func ApplyProfile(name string) error { + if name == "" { + return nil + } + + return changeOnExec(name) +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor_disabled.go b/vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor_disabled.go new file mode 100644 index 000000000..d4110cf0b --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/apparmor/apparmor_disabled.go @@ -0,0 +1,20 @@ +// +build !apparmor !linux + +package apparmor + +import ( + "errors" +) + +var ErrApparmorNotEnabled = errors.New("apparmor: config provided but apparmor not supported") + +func IsEnabled() bool { + return false +} + +func ApplyProfile(name string) error { + if name != "" { + return ErrApparmorNotEnabled + } + return nil +} -- cgit v1.2.3-54-g00ecf