From 6994271ca487210eafc867230c6f29177bba130c Mon Sep 17 00:00:00 2001 From: kernelmethod <17100608+kernelmethod@users.noreply.github.com> Date: Fri, 11 Mar 2022 13:57:50 -0700 Subject: [PATCH] Allow rootless containers to use AppArmor profiles Previously, Podman would print an error if you tried to run a container with an AppArmor profile as a non-root user, e.g. $ podman run --security-opt apparmor=my-profile ... Error: Apparmor profile "my-profile" specified, but Apparmor is not enabled on this system In fact, the only thing that Podman needs root privileges for is reading /sys/kernel/security/apparmor/profiles to see if the profile is already loaded, which isn't strictly necessary. This commit removes the 'IsLoaded()' check that occurs when you try to specify an AppArmor profile as a non-root user, as well as the other checks in pkg/apparmor/ for whether the program is running as UID 0. The check for whether the AppArmor profile is loaded should now be deferred to the container runtime at the point where it writes to either /proc/self/attr/exec or /proc/self/attr/apparmor/exec, since the write should fail if the profile is not loaded. Closes #958. Signed-off-by: kernelmethod <17100608+kernelmethod@users.noreply.github.com> --- common/pkg/apparmor/apparmor.go | 2 -- common/pkg/apparmor/apparmor_linux.go | 35 +++---------------- .../apparmor/internal/supported/supported.go | 3 -- 3 files changed, 4 insertions(+), 36 deletions(-) diff --git a/common/pkg/apparmor/apparmor.go b/common/pkg/apparmor/apparmor.go index 146280df2f..c50f87af78 100644 --- a/common/pkg/apparmor/apparmor.go +++ b/common/pkg/apparmor/apparmor.go @@ -17,6 +17,4 @@ const ( var ( // 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/common/pkg/apparmor/apparmor_linux.go b/common/pkg/apparmor/apparmor_linux.go index 735d194932..270c480b53 100644 --- a/common/pkg/apparmor/apparmor_linux.go +++ b/common/pkg/apparmor/apparmor_linux.go @@ -14,7 +14,6 @@ import ( "text/template" "github.com/containers/common/pkg/apparmor/internal/supported" - "github.com/containers/storage/pkg/unshare" runcaa "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -77,10 +76,6 @@ 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 unshare.IsRootless() { - return ErrApparmorRootless - } - p := profileData{ Name: name, } @@ -137,12 +132,9 @@ func DefaultContent(name string) ([]byte, error) { } // IsLoaded checks if a profile with the given name has been loaded into the -// kernel. +// kernel. This function checks for the existence of a profile by reading +// /sys/kernel/security/apparmor/profiles, and hence requires root permissions. func IsLoaded(name string) (bool, error) { - if name != "" && unshare.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) { @@ -238,25 +230,13 @@ func parseAAParserVersion(output string) (int, error) { // 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. +// default AppArmor profile. If it's a default profile, return +// DefaultLipodProfilePrefix, otherwise the specified one. func CheckProfileAndLoadDefault(name string) (string, error) { if name == "unconfined" { return name, nil } - // AppArmor is not supported in rootless mode as it requires root - // privileges. Return an error in case a specific profile is specified. - if unshare.IsRootless() { - if name != "" { - return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) - } else { - logrus.Debug("Skipping loading default AppArmor profile (rootless mode)") - return "", nil - } - } - // Check if AppArmor is disabled and error out if a profile is to be set. if !runcaa.IsEnabled() { if name == "" { @@ -271,13 +251,6 @@ func CheckProfileAndLoadDefault(name string) (string, error) { } else if !strings.HasPrefix(name, ProfilePrefix) { // If the specified name is not a default one, ignore it and return the // name. - isLoaded, err := IsLoaded(name) - if err != nil { - return "", errors.Wrapf(err, "verify if profile %s is loaded", name) - } - if !isLoaded { - return "", errors.Errorf("AppArmor profile %q specified but not loaded", name) - } return name, nil } diff --git a/common/pkg/apparmor/internal/supported/supported.go b/common/pkg/apparmor/internal/supported/supported.go index 778f4e3a20..3fd48043c6 100644 --- a/common/pkg/apparmor/internal/supported/supported.go +++ b/common/pkg/apparmor/internal/supported/supported.go @@ -40,9 +40,6 @@ func NewAppArmorVerifier() *ApparmorVerifier { // - AppArmor is disabled by the host system // - the `apparmor_parser` binary is not discoverable func (a *ApparmorVerifier) IsSupported() error { - if a.impl.UnshareIsRootless() { - return errors.New("AppAmor is not supported on rootless containers") - } if !a.impl.RuncIsEnabled() { return errors.New("AppArmor not supported by the host system") }