diff --git a/daemon/apparmor_default.go b/daemon/apparmor_default.go index 790e14b656..09dd0541b8 100644 --- a/daemon/apparmor_default.go +++ b/daemon/apparmor_default.go @@ -3,7 +3,8 @@ package daemon import ( - "github.com/Sirupsen/logrus" + "fmt" + aaprofile "github.com/docker/docker/profiles/apparmor" "github.com/opencontainers/runc/libcontainer/apparmor" ) @@ -13,18 +14,23 @@ const ( defaultApparmorProfile = "docker-default" ) -func installDefaultAppArmorProfile() { +func ensureDefaultAppArmorProfile() error { if apparmor.IsEnabled() { - if err := aaprofile.InstallDefault(defaultApparmorProfile); err != nil { - apparmorProfiles := []string{defaultApparmorProfile} + loaded, err := aaprofile.IsLoaded(defaultApparmorProfile) + if err != nil { + return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", defaultApparmorProfile, err) + } - // Allow daemon to run if loading failed, but are active - // (possibly through another run, manually, or via system startup) - for _, policy := range apparmorProfiles { - if loaded, err := aaprofile.IsLoaded(policy); err != nil || !loaded { - logrus.Errorf("AppArmor enabled on system but the %s profile could not be loaded.", policy) - } - } + // Nothing to do. + if loaded { + return nil + } + + // Load the profile. + if err := aaprofile.InstallDefault(defaultApparmorProfile); err != nil { + return fmt.Errorf("AppArmor enabled on system but the %s profile could not be loaded.", defaultApparmorProfile) } } + + return nil } diff --git a/daemon/apparmor_default_unsupported.go b/daemon/apparmor_default_unsupported.go index f186a68af9..cd2dd9702e 100644 --- a/daemon/apparmor_default_unsupported.go +++ b/daemon/apparmor_default_unsupported.go @@ -2,5 +2,6 @@ package daemon -func installDefaultAppArmorProfile() { +func ensureDefaultAppArmorProfile() error { + return nil } diff --git a/daemon/daemon.go b/daemon/daemon.go index 7c2ce50559..6a339a2870 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -524,7 +524,10 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot logrus.Warnf("Failed to configure golang's threads limit: %v", err) } - installDefaultAppArmorProfile() + if err := ensureDefaultAppArmorProfile(); err != nil { + logrus.Errorf(err.Error()) + } + daemonRepo := filepath.Join(config.Root, "containers") if err := idtools.MkdirAllAs(daemonRepo, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { return nil, err diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index bf21df86c4..1daefc587b 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -733,12 +733,27 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { } if apparmor.IsEnabled() { - appArmorProfile := "docker-default" - if len(c.AppArmorProfile) > 0 { + var appArmorProfile string + if c.AppArmorProfile != "" { appArmorProfile = c.AppArmorProfile } else if c.HostConfig.Privileged { appArmorProfile = "unconfined" + } else { + appArmorProfile = "docker-default" } + + if appArmorProfile == "docker-default" { + // Unattended upgrades and other fun services can unload AppArmor + // profiles inadvertently. Since we cannot store our profile in + // /etc/apparmor.d, nor can we practically add other ways of + // telling the system to keep our profile loaded, in order to make + // sure that we keep the default profile enabled we dynamically + // reload it if necessary. + if err := ensureDefaultAppArmorProfile(); err != nil { + return nil, err + } + } + s.Process.ApparmorProfile = appArmorProfile } s.Process.SelinuxLabel = c.GetProcessLabel()