Browse code

Fix AppArmor not being applied to Exec processes

Exec processes do not automatically inherit AppArmor
profiles from the container.

This patch sets the AppArmor profile for the exec
process.

Before this change:

apparmor_parser -q -r <<EOF
#include <tunables/global>
profile deny-write flags=(attach_disconnected) {
#include <abstractions/base>
file,
network,
deny /tmp/** w,
capability,
}
EOF

docker run -dit --security-opt "apparmor=deny-write" --name aa busybox

docker exec aa sh -c 'mkdir /tmp/test'
(no error)

With this change applied:

docker exec aa sh -c 'mkdir /tmp/test'
mkdir: can't create directory '/tmp/test': Permission denied

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2018/03/02 21:17:56
Showing 2 changed files
... ...
@@ -34,6 +34,8 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config
34 34
 		if c.AppArmorProfile != "" {
35 35
 			appArmorProfile = c.AppArmorProfile
36 36
 		} else if c.HostConfig.Privileged {
37
+			// `docker exec --privileged` does not currently disable AppArmor
38
+			// profiles. Privileged configuration of the container is inherited
37 39
 			appArmorProfile = "unconfined"
38 40
 		} else {
39 41
 			appArmorProfile = "docker-default"
... ...
@@ -50,6 +52,7 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config
50 50
 				return err
51 51
 			}
52 52
 		}
53
+		p.ApparmorProfile = appArmorProfile
53 54
 	}
54 55
 	daemon.setRlimits(&specs.Spec{Process: p}, c)
55 56
 	return nil
56 57
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+// +build linux
1
+
2
+package daemon
3
+
4
+import (
5
+	"testing"
6
+
7
+	containertypes "github.com/docker/docker/api/types/container"
8
+	"github.com/docker/docker/container"
9
+	"github.com/docker/docker/daemon/exec"
10
+	"github.com/gotestyourself/gotestyourself/assert"
11
+	"github.com/opencontainers/runc/libcontainer/apparmor"
12
+	"github.com/opencontainers/runtime-spec/specs-go"
13
+)
14
+
15
+func TestExecSetPlatformOpt(t *testing.T) {
16
+	if !apparmor.IsEnabled() {
17
+		t.Skip("requires AppArmor to be enabled")
18
+	}
19
+	d := &Daemon{}
20
+	c := &container.Container{AppArmorProfile: "my-custom-profile"}
21
+	ec := &exec.Config{}
22
+	p := &specs.Process{}
23
+
24
+	err := d.execSetPlatformOpt(c, ec, p)
25
+	assert.NilError(t, err)
26
+	assert.Equal(t, "my-custom-profile", p.ApparmorProfile)
27
+}
28
+
29
+// TestExecSetPlatformOptPrivileged verifies that `docker exec --privileged`
30
+// does not disable AppArmor profiles. Exec currently inherits the `Privileged`
31
+// configuration of the container. See https://github.com/moby/moby/pull/31773#discussion_r105586900
32
+//
33
+// This behavior may change in future, but test for the behavior to prevent it
34
+// from being changed accidentally.
35
+func TestExecSetPlatformOptPrivileged(t *testing.T) {
36
+	if !apparmor.IsEnabled() {
37
+		t.Skip("requires AppArmor to be enabled")
38
+	}
39
+	d := &Daemon{}
40
+	c := &container.Container{AppArmorProfile: "my-custom-profile"}
41
+	ec := &exec.Config{Privileged: true}
42
+	p := &specs.Process{}
43
+
44
+	err := d.execSetPlatformOpt(c, ec, p)
45
+	assert.NilError(t, err)
46
+	assert.Equal(t, "my-custom-profile", p.ApparmorProfile)
47
+
48
+	c.HostConfig = &containertypes.HostConfig{Privileged: true}
49
+	err = d.execSetPlatformOpt(c, ec, p)
50
+	assert.NilError(t, err)
51
+	assert.Equal(t, "unconfined", p.ApparmorProfile)
52
+}