Browse code

Make libcontainer's CapabilitiesMask into a []string (Capabilities).

Docker-DCO-1.1-Signed-off-by: Victor Marmol <vmarmol@google.com> (github: vmarmol)

Victor Marmol authored on 2014/05/17 09:44:10
Showing 9 changed files
... ...
@@ -109,12 +109,19 @@ func memorySwap(container *libcontainer.Container, context interface{}, value st
109 109
 }
110 110
 
111 111
 func addCap(container *libcontainer.Container, context interface{}, value string) error {
112
-	container.CapabilitiesMask[value] = true
112
+	container.Capabilities = append(container.Capabilities, value)
113 113
 	return nil
114 114
 }
115 115
 
116 116
 func dropCap(container *libcontainer.Container, context interface{}, value string) error {
117
-	container.CapabilitiesMask[value] = false
117
+	// If the capability is specified multiple times, remove all instances.
118
+	for i, capability := range container.Capabilities {
119
+		if capability == value {
120
+			container.Capabilities = append(container.Capabilities[:i], container.Capabilities[i+1:]...)
121
+		}
122
+	}
123
+
124
+	// The capability wasn't found so we will drop it anyways.
118 125
 	return nil
119 126
 }
120 127
 
... ...
@@ -4,8 +4,19 @@ import (
4 4
 	"testing"
5 5
 
6 6
 	"github.com/dotcloud/docker/daemon/execdriver/native/template"
7
+	"github.com/dotcloud/docker/pkg/libcontainer"
7 8
 )
8 9
 
10
+// Checks whether the expected capability is specified in the capabilities.
11
+func hasCapability(expected string, capabilities []string) bool {
12
+	for _, capability := range capabilities {
13
+		if capability == expected {
14
+			return true
15
+		}
16
+	}
17
+	return false
18
+}
19
+
9 20
 func TestSetReadonlyRootFs(t *testing.T) {
10 21
 	var (
11 22
 		container = template.New()
... ...
@@ -39,10 +50,10 @@ func TestConfigurationsDoNotConflict(t *testing.T) {
39 39
 		t.Fatal(err)
40 40
 	}
41 41
 
42
-	if !container1.CapabilitiesMask["NET_ADMIN"] {
42
+	if !hasCapability("NET_ADMIN", container1.Capabilities) {
43 43
 		t.Fatal("container one should have NET_ADMIN enabled")
44 44
 	}
45
-	if container2.CapabilitiesMask["NET_ADMIN"] {
45
+	if hasCapability("NET_ADMIN", container2.Capabilities) {
46 46
 		t.Fatal("container two should not have NET_ADMIN enabled")
47 47
 	}
48 48
 }
... ...
@@ -138,10 +149,10 @@ func TestAddCap(t *testing.T) {
138 138
 		t.Fatal(err)
139 139
 	}
140 140
 
141
-	if !container.CapabilitiesMask["MKNOD"] {
141
+	if !hasCapability("MKNOD", container.Capabilities) {
142 142
 		t.Fatal("container should have MKNOD enabled")
143 143
 	}
144
-	if !container.CapabilitiesMask["SYS_ADMIN"] {
144
+	if !hasCapability("SYS_ADMIN", container.Capabilities) {
145 145
 		t.Fatal("container should have SYS_ADMIN enabled")
146 146
 	}
147 147
 }
... ...
@@ -154,14 +165,12 @@ func TestDropCap(t *testing.T) {
154 154
 		}
155 155
 	)
156 156
 	// enabled all caps like in privileged mode
157
-	for key := range container.CapabilitiesMask {
158
-		container.CapabilitiesMask[key] = true
159
-	}
157
+	container.Capabilities = libcontainer.GetAllCapabilities()
160 158
 	if err := ParseConfiguration(container, nil, opts); err != nil {
161 159
 		t.Fatal(err)
162 160
 	}
163 161
 
164
-	if container.CapabilitiesMask["MKNOD"] {
162
+	if hasCapability("MKNOD", container.Capabilities) {
165 163
 		t.Fatal("container should not have MKNOD enabled")
166 164
 	}
167 165
 }
... ...
@@ -98,9 +98,7 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
98 98
 }
99 99
 
100 100
 func (d *driver) setPrivileged(container *libcontainer.Container) error {
101
-	for key := range container.CapabilitiesMask {
102
-		container.CapabilitiesMask[key] = true
103
-	}
101
+	container.Capabilities = libcontainer.GetAllCapabilities()
104 102
 	container.Cgroups.DeviceAccess = true
105 103
 
106 104
 	delete(container.Context, "restrictions")
... ...
@@ -9,28 +9,13 @@ import (
9 9
 // New returns the docker default configuration for libcontainer
10 10
 func New() *libcontainer.Container {
11 11
 	container := &libcontainer.Container{
12
-		CapabilitiesMask: map[string]bool{
13
-			"SETPCAP":        false,
14
-			"SYS_MODULE":     false,
15
-			"SYS_RAWIO":      false,
16
-			"SYS_PACCT":      false,
17
-			"SYS_ADMIN":      false,
18
-			"SYS_NICE":       false,
19
-			"SYS_RESOURCE":   false,
20
-			"SYS_TIME":       false,
21
-			"SYS_TTY_CONFIG": false,
22
-			"AUDIT_WRITE":    false,
23
-			"AUDIT_CONTROL":  false,
24
-			"MAC_OVERRIDE":   false,
25
-			"MAC_ADMIN":      false,
26
-			"NET_ADMIN":      false,
27
-			"MKNOD":          true,
28
-			"SYSLOG":         false,
29
-			"SETUID":         true,
30
-			"SETGID":         true,
31
-			"CHOWN":          true,
32
-			"NET_RAW":        true,
33
-			"DAC_OVERRIDE":   true,
12
+		Capabilities: []string{
13
+			"MKNOD",
14
+			"SETUID",
15
+			"SETGID",
16
+			"CHOWN",
17
+			"NET_RAW",
18
+			"DAC_OVERRIDE",
34 19
 		},
35 20
 		Namespaces: map[string]bool{
36 21
 			"NEWNS":  true,
... ...
@@ -11,19 +11,19 @@ type Context map[string]string
11 11
 // Container defines configuration options for how a
12 12
 // container is setup inside a directory and how a process should be executed
13 13
 type Container struct {
14
-	Hostname         string          `json:"hostname,omitempty"`          // hostname
15
-	ReadonlyFs       bool            `json:"readonly_fs,omitempty"`       // set the containers rootfs as readonly
16
-	NoPivotRoot      bool            `json:"no_pivot_root,omitempty"`     // this can be enabled if you are running in ramdisk
17
-	User             string          `json:"user,omitempty"`              // user to execute the process as
18
-	WorkingDir       string          `json:"working_dir,omitempty"`       // current working directory
19
-	Env              []string        `json:"environment,omitempty"`       // environment to set
20
-	Tty              bool            `json:"tty,omitempty"`               // setup a proper tty or not
21
-	Namespaces       map[string]bool `json:"namespaces,omitempty"`        // namespaces to apply
22
-	CapabilitiesMask map[string]bool `json:"capabilities_mask,omitempty"` // capabilities to drop
23
-	Networks         []*Network      `json:"networks,omitempty"`          // nil for host's network stack
24
-	Cgroups          *cgroups.Cgroup `json:"cgroups,omitempty"`           // cgroups
25
-	Context          Context         `json:"context,omitempty"`           // generic context for specific options (apparmor, selinux)
26
-	Mounts           Mounts          `json:"mounts,omitempty"`
14
+	Hostname     string          `json:"hostname,omitempty"`      // hostname
15
+	ReadonlyFs   bool            `json:"readonly_fs,omitempty"`   // set the containers rootfs as readonly
16
+	NoPivotRoot  bool            `json:"no_pivot_root,omitempty"` // this can be enabled if you are running in ramdisk
17
+	User         string          `json:"user,omitempty"`          // user to execute the process as
18
+	WorkingDir   string          `json:"working_dir,omitempty"`   // current working directory
19
+	Env          []string        `json:"environment,omitempty"`   // environment to set
20
+	Tty          bool            `json:"tty,omitempty"`           // setup a proper tty or not
21
+	Namespaces   map[string]bool `json:"namespaces,omitempty"`    // namespaces to apply
22
+	Capabilities []string        `json:"capabilities,omitempty"`  // capabilities given to the container
23
+	Networks     []*Network      `json:"networks,omitempty"`      // nil for host's network stack
24
+	Cgroups      *cgroups.Cgroup `json:"cgroups,omitempty"`       // cgroups
25
+	Context      Context         `json:"context,omitempty"`       // generic context for specific options (apparmor, selinux)
26
+	Mounts       Mounts          `json:"mounts,omitempty"`
27 27
 }
28 28
 
29 29
 // Network defines configuration for a container's networking stack
... ...
@@ -24,24 +24,9 @@
24 24
       "mtu": 1500
25 25
     }
26 26
   ],
27
-  "capabilities_mask": {
28
-    "SYSLOG": false,
29
-    "MKNOD": true,
30
-    "NET_ADMIN": false,
31
-    "MAC_ADMIN": false,
32
-    "MAC_OVERRIDE": false,
33
-    "AUDIT_CONTROL": false,
34
-    "AUDIT_WRITE": false,
35
-    "SYS_TTY_CONFIG": false,
36
-    "SETPCAP": false,
37
-    "SYS_MODULE": false,
38
-    "SYS_RAWIO": false,
39
-    "SYS_PACCT": false,
40
-    "SYS_ADMIN": false,
41
-    "SYS_NICE": false,
42
-    "SYS_RESOURCE": false,
43
-    "SYS_TIME": false
44
-  },
27
+  "capabilities": [
28
+    "MKNOD"
29
+  ],
45 30
   "cgroups": {
46 31
     "name": "docker-koye",
47 32
     "parent": "docker"
... ...
@@ -6,6 +6,16 @@ import (
6 6
 	"testing"
7 7
 )
8 8
 
9
+// Checks whether the expected capability is specified in the capabilities.
10
+func hasCapability(expected string, capabilities []string) bool {
11
+	for _, capability := range capabilities {
12
+		if capability == expected {
13
+			return true
14
+		}
15
+	}
16
+	return false
17
+}
18
+
9 19
 func TestContainerJsonFormat(t *testing.T) {
10 20
 	f, err := os.Open("container.json")
11 21
 	if err != nil {
... ...
@@ -37,22 +47,17 @@ func TestContainerJsonFormat(t *testing.T) {
37 37
 		t.Fail()
38 38
 	}
39 39
 
40
-	if _, exists := container.CapabilitiesMask["SYS_ADMIN"]; !exists {
41
-		t.Log("capabilities mask should contain SYS_ADMIN")
42
-		t.Fail()
43
-	}
44
-
45
-	if container.CapabilitiesMask["SYS_ADMIN"] {
40
+	if hasCapability("SYS_ADMIN", container.Capabilities) {
46 41
 		t.Log("SYS_ADMIN should not be enabled in capabilities mask")
47 42
 		t.Fail()
48 43
 	}
49 44
 
50
-	if !container.CapabilitiesMask["MKNOD"] {
45
+	if !hasCapability("MKNOD", container.Capabilities) {
51 46
 		t.Log("MKNOD should be enabled in capabilities mask")
52 47
 		t.Fail()
53 48
 	}
54 49
 
55
-	if container.CapabilitiesMask["SYS_CHROOT"] {
50
+	if hasCapability("SYS_CHROOT", container.Capabilities) {
56 51
 		t.Log("capabilities mask should not contain SYS_CHROOT")
57 52
 		t.Fail()
58 53
 	}
... ...
@@ -26,14 +26,12 @@ func DropCapabilities(container *libcontainer.Container) error {
26 26
 	return nil
27 27
 }
28 28
 
29
-// getCapabilitiesMask returns the capabilities that should not be dropped by the container.
29
+// getEnabledCapabilities returns the capabilities that should not be dropped by the container.
30 30
 func getEnabledCapabilities(container *libcontainer.Container) []capability.Cap {
31 31
 	keep := []capability.Cap{}
32
-	for key, enabled := range container.CapabilitiesMask {
33
-		if enabled {
34
-			if c := libcontainer.GetCapability(key); c != nil {
35
-				keep = append(keep, c.Value)
36
-			}
32
+	for _, capability := range container.Capabilities {
33
+		if c := libcontainer.GetCapability(capability); c != nil {
34
+			keep = append(keep, c.Value)
37 35
 		}
38 36
 	}
39 37
 	return keep
... ...
@@ -123,6 +123,14 @@ func GetCapability(key string) *Capability {
123 123
 	return nil
124 124
 }
125 125
 
126
+func GetAllCapabilities() []string {
127
+	output := make([]string, len(capabilityList))
128
+	for i, capability := range capabilityList {
129
+		output[i] = capability.String()
130
+	}
131
+	return output
132
+}
133
+
126 134
 // Contains returns true if the specified Capability is
127 135
 // in the slice
128 136
 func (c Capabilities) Contains(capp string) bool {