diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2019-01-09 14:54:58 +0100 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2019-01-09 22:18:11 +0100 |
commit | edb285d17675061832aceaf72021b87aba149438 (patch) | |
tree | 332f020dfc754a2a2ecaa80bd2891392c81305f1 /pkg/apparmor | |
parent | c37f73159609b203545ca6fe54c86b9deacfca21 (diff) | |
download | podman-edb285d17675061832aceaf72021b87aba149438.tar.gz podman-edb285d17675061832aceaf72021b87aba149438.tar.bz2 podman-edb285d17675061832aceaf72021b87aba149438.zip |
apparmor: apply default profile at container initialization
Apply the default AppArmor profile at container initialization to cover
all possible code paths (i.e., podman-{start,run}) before executing the
runtime. This allows moving most of the logic into pkg/apparmor.
Also make the loading and application of the default AppArmor profile
versio-indepenent by checking for the `libpod-default-` prefix and
over-writing the profile in the run-time spec if needed.
The intitial run-time spec of the container differs a bit from the
applied one when having started the container, which results in
displaying a potentially outdated AppArmor profile when inspecting
a container. To fix that, load the container config from the file
system if present and use it to display the data.
Fixes: #2107
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'pkg/apparmor')
-rw-r--r-- | pkg/apparmor/apparmor.go | 7 | ||||
-rw-r--r-- | pkg/apparmor/apparmor_linux.go | 66 | ||||
-rw-r--r-- | pkg/apparmor/apparmor_unsupported.go | 16 |
3 files changed, 83 insertions, 6 deletions
diff --git a/pkg/apparmor/apparmor.go b/pkg/apparmor/apparmor.go index 8b9f99477..45c029c07 100644 --- a/pkg/apparmor/apparmor.go +++ b/pkg/apparmor/apparmor.go @@ -2,11 +2,16 @@ package apparmor import ( "errors" + libpodVersion "github.com/containers/libpod/version" ) var ( + // DefaultLipodProfilePrefix is used for version-independent presence checks. + DefaultLipodProfilePrefix = "libpod-default" + "-" // DefaultLibpodProfile is the name of default libpod AppArmor profile. - DefaultLibpodProfile = "libpod-default" + DefaultLibpodProfile = DefaultLipodProfilePrefix + libpodVersion.Version // ErrApparmorUnsupported indicates that AppArmor support is not supported. ErrApparmorUnsupported = errors.New("AppArmor is not supported") + // ErrApparmorRootless indicates that AppArmor support is not supported in rootless mode. + ErrApparmorRootless = errors.New("AppArmor is not supported in rootless mode") ) diff --git a/pkg/apparmor/apparmor_linux.go b/pkg/apparmor/apparmor_linux.go index b0e3ca0fd..0787b3fa5 100644 --- a/pkg/apparmor/apparmor_linux.go +++ b/pkg/apparmor/apparmor_linux.go @@ -13,7 +13,10 @@ import ( "strings" "text/template" + "github.com/containers/libpod/pkg/rootless" runcaa "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // profileDirectory is the file store for apparmor profiles and macros. @@ -21,6 +24,9 @@ var profileDirectory = "/etc/apparmor.d" // IsEnabled returns true if AppArmor is enabled on the host. func IsEnabled() bool { + if rootless.IsRootless() { + return false + } return runcaa.IsEnabled() } @@ -71,6 +77,10 @@ func macroExists(m string) bool { // InstallDefault generates a default profile and loads it into the kernel // using 'apparmor_parser'. func InstallDefault(name string) error { + if rootless.IsRootless() { + return ErrApparmorRootless + } + p := profileData{ Name: name, } @@ -97,6 +107,10 @@ func InstallDefault(name string) error { // IsLoaded checks if a profile with the given name has been loaded into the // kernel. func IsLoaded(name string) (bool, error) { + if name != "" && rootless.IsRootless() { + return false, errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + } + file, err := os.Open("/sys/kernel/security/apparmor/profiles") if err != nil { if os.IsNotExist(err) { @@ -188,3 +202,55 @@ func parseAAParserVersion(output string) (int, error) { return numericVersion, nil } + +// CheckProfileAndLoadDefault checks if the specified profile is loaded and +// loads the DefaultLibpodProfile if the specified on is prefixed by +// DefaultLipodProfilePrefix. This allows to always load and apply the latest +// default AppArmor profile. Note that AppArmor requires root. If it's a +// default profile, return DefaultLipodProfilePrefix, otherwise the specified +// one. +func CheckProfileAndLoadDefault(name string) (string, error) { + if name == "unconfined" { + return name, nil + } + + if name != "" && rootless.IsRootless() { + return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + } + + if name != "" && !runcaa.IsEnabled() { + return "", fmt.Errorf("profile %q specified but AppArmor is disabled on the host", name) + } + + // If the specified name is not empty or is not a default libpod one, + // ignore it and return the name. + if name != "" && !strings.HasPrefix(name, DefaultLipodProfilePrefix) { + isLoaded, err := IsLoaded(name) + if err != nil { + return "", err + } + if !isLoaded { + return "", fmt.Errorf("AppArmor profile %q specified but not loaded") + } + return name, nil + } + + name = DefaultLibpodProfile + // To avoid expensive redundant loads on each invocation, check + // if it's loaded before installing it. + isLoaded, err := IsLoaded(name) + if err != nil { + return "", err + } + if !isLoaded { + err = InstallDefault(name) + if err != nil { + return "", err + } + logrus.Infof("successfully loaded AppAmor profile %q", name) + } else { + logrus.Infof("AppAmor profile %q is already loaded", name) + } + + return name, nil +} diff --git a/pkg/apparmor/apparmor_unsupported.go b/pkg/apparmor/apparmor_unsupported.go index df1336b07..b2b4de5f5 100644 --- a/pkg/apparmor/apparmor_unsupported.go +++ b/pkg/apparmor/apparmor_unsupported.go @@ -2,19 +2,25 @@ package apparmor -// IsEnabled returns true if AppArmor is enabled on the host. +// IsEnabled dummy. 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'. +// InstallDefault dummy. func InstallDefault(name string) error { return ErrApparmorUnsupported } -// IsLoaded checks if a profile with the given name has been loaded into the -// kernel. +// IsLoaded dummy. func IsLoaded(name string) (bool, error) { return false, ErrApparmorUnsupported } + +// CheckProfileAndLoadDefault dummy. +func CheckProfileAndLoadDefault(name string) (string, error) { + if name == "" { + return "", nil + } + return "", ErrApparmorUnsupported +} |