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: 2f7596aaef3a ("apparmor: do not save profile to /etc/apparmor.d")
Signed-off-by: Aleksa Sarai <asarai@suse.de>
| ... | ... |
@@ -3,7 +3,8 @@ |
| 3 | 3 |
package daemon |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
- "github.com/Sirupsen/logrus" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ |
|
| 7 | 8 |
aaprofile "github.com/docker/docker/profiles/apparmor" |
| 8 | 9 |
"github.com/opencontainers/runc/libcontainer/apparmor" |
| 9 | 10 |
) |
| ... | ... |
@@ -13,18 +14,23 @@ const ( |
| 13 | 13 |
defaultApparmorProfile = "docker-default" |
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 |
-func installDefaultAppArmorProfile() {
|
|
| 16 |
+func ensureDefaultAppArmorProfile() error {
|
|
| 17 | 17 |
if apparmor.IsEnabled() {
|
| 18 |
+ loaded, err := aaprofile.IsLoaded(defaultApparmorProfile) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", defaultApparmorProfile, err)
|
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ // Nothing to do. |
|
| 24 |
+ if loaded {
|
|
| 25 |
+ return nil |
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+ // Load the profile. |
|
| 18 | 29 |
if err := aaprofile.InstallDefault(defaultApparmorProfile); err != nil {
|
| 19 |
- apparmorProfiles := []string{defaultApparmorProfile}
|
|
| 20 |
- |
|
| 21 |
- // Allow daemon to run if loading failed, but are active |
|
| 22 |
- // (possibly through another run, manually, or via system startup) |
|
| 23 |
- for _, policy := range apparmorProfiles {
|
|
| 24 |
- if loaded, err := aaprofile.IsLoaded(policy); err != nil || !loaded {
|
|
| 25 |
- logrus.Errorf("AppArmor enabled on system but the %s profile could not be loaded.", policy)
|
|
| 26 |
- } |
|
| 27 |
- } |
|
| 30 |
+ return fmt.Errorf("AppArmor enabled on system but the %s profile could not be loaded.", defaultApparmorProfile)
|
|
| 28 | 31 |
} |
| 29 | 32 |
} |
| 33 |
+ |
|
| 34 |
+ return nil |
|
| 30 | 35 |
} |
| ... | ... |
@@ -524,7 +524,10 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot |
| 524 | 524 |
logrus.Warnf("Failed to configure golang's threads limit: %v", err)
|
| 525 | 525 |
} |
| 526 | 526 |
|
| 527 |
- installDefaultAppArmorProfile() |
|
| 527 |
+ if err := ensureDefaultAppArmorProfile(); err != nil {
|
|
| 528 |
+ logrus.Errorf(err.Error()) |
|
| 529 |
+ } |
|
| 530 |
+ |
|
| 528 | 531 |
daemonRepo := filepath.Join(config.Root, "containers") |
| 529 | 532 |
if err := idtools.MkdirAllAs(daemonRepo, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) {
|
| 530 | 533 |
return nil, err |
| ... | ... |
@@ -733,12 +733,27 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
|
| 733 | 733 |
} |
| 734 | 734 |
|
| 735 | 735 |
if apparmor.IsEnabled() {
|
| 736 |
- appArmorProfile := "docker-default" |
|
| 737 |
- if len(c.AppArmorProfile) > 0 {
|
|
| 736 |
+ var appArmorProfile string |
|
| 737 |
+ if c.AppArmorProfile != "" {
|
|
| 738 | 738 |
appArmorProfile = c.AppArmorProfile |
| 739 | 739 |
} else if c.HostConfig.Privileged {
|
| 740 | 740 |
appArmorProfile = "unconfined" |
| 741 |
+ } else {
|
|
| 742 |
+ appArmorProfile = "docker-default" |
|
| 743 |
+ } |
|
| 744 |
+ |
|
| 745 |
+ if appArmorProfile == "docker-default" {
|
|
| 746 |
+ // Unattended upgrades and other fun services can unload AppArmor |
|
| 747 |
+ // profiles inadvertently. Since we cannot store our profile in |
|
| 748 |
+ // /etc/apparmor.d, nor can we practically add other ways of |
|
| 749 |
+ // telling the system to keep our profile loaded, in order to make |
|
| 750 |
+ // sure that we keep the default profile enabled we dynamically |
|
| 751 |
+ // reload it if necessary. |
|
| 752 |
+ if err := ensureDefaultAppArmorProfile(); err != nil {
|
|
| 753 |
+ return nil, err |
|
| 754 |
+ } |
|
| 741 | 755 |
} |
| 756 |
+ |
|
| 742 | 757 |
s.Process.ApparmorProfile = appArmorProfile |
| 743 | 758 |
} |
| 744 | 759 |
s.Process.SelinuxLabel = c.GetProcessLabel() |