Do not try to enable AppArmor in rootless mode

When in rootless mode it's not possible to load profiles or
check which profiles are loaded.
Added a few baseline tests to check all possible cases.

Signed-off-by: Marco Vedovati <mvedovati@suse.com>

Closes: #1250
Approved by: mheon
This commit is contained in:
Marco Vedovati
2018-08-09 13:09:59 +02:00
committed by Atomic Bot
parent af9f83f11c
commit 72e41c81aa
2 changed files with 150 additions and 52 deletions

View File

@ -15,6 +15,7 @@ import (
ann "github.com/containers/libpod/pkg/annotations" ann "github.com/containers/libpod/pkg/annotations"
"github.com/containers/libpod/pkg/apparmor" "github.com/containers/libpod/pkg/apparmor"
"github.com/containers/libpod/pkg/inspect" "github.com/containers/libpod/pkg/inspect"
"github.com/containers/libpod/pkg/rootless"
cc "github.com/containers/libpod/pkg/spec" cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util" "github.com/containers/libpod/pkg/util"
libpodVersion "github.com/containers/libpod/version" libpodVersion "github.com/containers/libpod/version"
@ -148,6 +149,79 @@ func createCmd(c *cli.Context) error {
return nil return nil
} }
// Checks if a user-specified AppArmor profile is loaded, or loads the default profile if
// AppArmor is enabled.
// Any interaction with AppArmor requires root permissions.
func loadAppArmor(config *cc.CreateConfig) error {
if rootless.IsRootless() {
noAAMsg := "AppArmor security is not available in rootless mode"
switch config.ApparmorProfile {
case "":
logrus.Warn(noAAMsg)
case "unconfined":
default:
return fmt.Errorf(noAAMsg)
}
return nil
}
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.
// Suffix the profile with the current libpod version to allow
// loading the new, potentially updated profile after an update.
profile := fmt.Sprintf("%s-%s", apparmor.DefaultLibpodProfile, libpodVersion.Version)
loadProfile := func() error {
isLoaded, err := apparmor.IsLoaded(profile)
if err != nil {
return err
}
if !isLoaded {
err = apparmor.InstallDefault(profile)
if err != nil {
return err
}
}
return nil
}
if err := loadProfile(); err != nil {
switch err {
case apparmor.ErrApparmorUnsupported:
// do not set the profile when AppArmor isn't supported
logrus.Debugf("AppArmor is not supported: setting empty profile")
default:
return err
}
} else {
logrus.Infof("Sucessfully loaded AppAmor profile '%s'", profile)
config.ApparmorProfile = profile
}
} else if config.ApparmorProfile != "" && config.ApparmorProfile != "unconfined" {
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 {
case apparmor.ErrApparmorUnsupported:
return fmt.Errorf("Profile specified but AppArmor is not supported")
default:
return fmt.Errorf("Error checking if AppArmor profile is loaded: %v", err)
}
}
if !isLoaded {
return fmt.Errorf("The specified AppArmor profile '%s' is not loaded", config.ApparmorProfile)
}
}
return nil
}
func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error { func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
var ( var (
labelOpts []string labelOpts []string
@ -196,58 +270,8 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
} }
} }
if config.ApparmorProfile == "" && apparmor.IsEnabled() { if err := loadAppArmor(config); err != nil {
// Unless specified otherwise, make sure that the default AppArmor return err
// profile is installed. To avoid redundantly loading the profile
// on each invocation, check if it's loaded before installing it.
// Suffix the profile with the current libpod version to allow
// loading the new, potentially updated profile after an update.
profile := fmt.Sprintf("%s-%s", apparmor.DefaultLibpodProfile, libpodVersion.Version)
loadProfile := func() error {
isLoaded, err := apparmor.IsLoaded(profile)
if err != nil {
return err
}
if !isLoaded {
err = apparmor.InstallDefault(profile)
if err != nil {
return err
}
}
return nil
}
if err := loadProfile(); err != nil {
switch err {
case apparmor.ErrApparmorUnsupported:
// do not set the profile when AppArmor isn't supported
logrus.Debugf("AppArmor is not supported: setting empty profile")
default:
return err
}
} else {
logrus.Infof("Sucessfully loaded AppAmor profile '%s'", profile)
config.ApparmorProfile = profile
}
} else if config.ApparmorProfile != "" && config.ApparmorProfile != "unconfined" {
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 {
case apparmor.ErrApparmorUnsupported:
return fmt.Errorf("profile specified but AppArmor is not supported")
default:
return fmt.Errorf("error checking if AppArmor profile is loaded: %v", err)
}
}
if !isLoaded {
return fmt.Errorf("specified AppArmor profile '%s' is not loaded", config.ApparmorProfile)
}
} }
if config.SeccompProfilePath == "" { if config.SeccompProfilePath == "" {

View File

@ -372,3 +372,77 @@ podman run whale-says
podman rm --all podman rm --all
podman rmi --all podman rmi --all
rm ./Dockerfile* rm ./Dockerfile*
########
# Run AppArmor rootless tests
########
if aa-enabled >/dev/null && getent passwd 1000 >/dev/null; then
# Expected to succeed
sudo -u "#1000" podman run alpine echo hello
rc=$?
echo -n "rootless with no AppArmor profile "
if [ $rc == 0 ]; then
echo "passed"
else
echo "failed"
fi
# Expected to succeed
sudo -u "#1000" podman run --security-opt apparmor=unconfined alpine echo hello
rc=$?
echo -n "rootless with unconfined AppArmor profile "
if [ $rc == 0 ]; then
echo "passed"
else
echo "failed"
fi
aaFile="/tmp/aaProfile"
aaProfile="aa-demo-profile"
cat > $aaFile << EOF
#include <tunables/global>
profile aa-demo-profile flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
deny /sys/fs/[^c]*/** wklx,
deny /sys/fs/c[^g]*/** wklx,
deny /sys/fs/cg[^r]*/** wklx,
deny /sys/firmware/efi/efivars/** rwklx,
deny /sys/kernel/security/** rwklx,
}
EOF
apparmor_parser -Kr $aaFile
#Expected to pass (as root)
podman run --security-opt apparmor=$aaProfile alpine echo hello
rc=$?
echo -n "root with specified AppArmor profile: "
if [ $rc == 0 ]; then
echo "passed"
else
echo "failed"
fi
#Expected to fail (as rootless)
sudo -u "#1000" podman run --security-opt apparmor=$aaProfile alpine echo hello
rc=$?
echo -n "rootless with specified AppArmor profile: "
if [ $rc != 0 ]; then
echo "passed"
else
echo "failed"
fi
########
# Clean up Podman and $aaFile
########
apparmor_parser -R $aaFile
podman rm --all
podman rmi --all
sudo -u "#1000" podman rm --all
sudo -u "#1000" podman rmi --all
rm -f $aaFile
fi