daemon: switch to 'ensure' workflow for AppArmor profiles
In certain cases (unattended upgrades), system services can disable loaded AppArmor profiles. However, since /etc being read-only is a supported setup we cannot just write a copy of the profile to /etc/apparmor.d. Instead, dynamically load the docker-default AppArmor profile if a container is started with that profile set. This code will short-cut if the profile is already loaded. Fixes:2f7596aaef
("apparmor: do not save profile to /etc/apparmor.d") Signed-off-by: Aleksa Sarai <asarai@suse.de> (cherry picked from commit567ef8e785
) Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
parent
20d6f23b55
commit
80c3ed1c0c
5 changed files with 41 additions and 16 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
|
||||
package daemon
|
||||
|
||||
func installDefaultAppArmorProfile() {
|
||||
func ensureDefaultAppArmorProfile() error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -1057,7 +1057,7 @@ func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *check.C) {
|
|||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
||||
// make sure task has been deployed.
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
|
||||
waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
|
||||
|
||||
out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo")
|
||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||
|
|
Loading…
Reference in a new issue