mirror of
https://github.com/containers/podman.git
synced 2025-10-25 18:25:59 +08:00
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:
committed by
Atomic Bot
parent
af9f83f11c
commit
72e41c81aa
@ -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 == "" {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user