diff --git a/daemon/execdriver/native/configuration/parse.go b/daemon/execdriver/native/configuration/parse.go index 3767f5a866..f18a60f797 100644 --- a/daemon/execdriver/native/configuration/parse.go +++ b/daemon/execdriver/native/configuration/parse.go @@ -109,12 +109,19 @@ func memorySwap(container *libcontainer.Container, context interface{}, value st } func addCap(container *libcontainer.Container, context interface{}, value string) error { - container.CapabilitiesMask[value] = true + container.Capabilities = append(container.Capabilities, value) return nil } func dropCap(container *libcontainer.Container, context interface{}, value string) error { - container.CapabilitiesMask[value] = false + // If the capability is specified multiple times, remove all instances. + for i, capability := range container.Capabilities { + if capability == value { + container.Capabilities = append(container.Capabilities[:i], container.Capabilities[i+1:]...) + } + } + + // The capability wasn't found so we will drop it anyways. return nil } diff --git a/daemon/execdriver/native/configuration/parse_test.go b/daemon/execdriver/native/configuration/parse_test.go index 1b0316b688..5524adb857 100644 --- a/daemon/execdriver/native/configuration/parse_test.go +++ b/daemon/execdriver/native/configuration/parse_test.go @@ -4,8 +4,19 @@ import ( "testing" "github.com/dotcloud/docker/daemon/execdriver/native/template" + "github.com/dotcloud/docker/pkg/libcontainer" ) +// Checks whether the expected capability is specified in the capabilities. +func hasCapability(expected string, capabilities []string) bool { + for _, capability := range capabilities { + if capability == expected { + return true + } + } + return false +} + func TestSetReadonlyRootFs(t *testing.T) { var ( container = template.New() @@ -39,10 +50,10 @@ func TestConfigurationsDoNotConflict(t *testing.T) { t.Fatal(err) } - if !container1.CapabilitiesMask["NET_ADMIN"] { + if !hasCapability("NET_ADMIN", container1.Capabilities) { t.Fatal("container one should have NET_ADMIN enabled") } - if container2.CapabilitiesMask["NET_ADMIN"] { + if hasCapability("NET_ADMIN", container2.Capabilities) { t.Fatal("container two should not have NET_ADMIN enabled") } } @@ -138,10 +149,10 @@ func TestAddCap(t *testing.T) { t.Fatal(err) } - if !container.CapabilitiesMask["MKNOD"] { + if !hasCapability("MKNOD", container.Capabilities) { t.Fatal("container should have MKNOD enabled") } - if !container.CapabilitiesMask["SYS_ADMIN"] { + if !hasCapability("SYS_ADMIN", container.Capabilities) { t.Fatal("container should have SYS_ADMIN enabled") } } @@ -154,14 +165,12 @@ func TestDropCap(t *testing.T) { } ) // enabled all caps like in privileged mode - for key := range container.CapabilitiesMask { - container.CapabilitiesMask[key] = true - } + container.Capabilities = libcontainer.GetAllCapabilities() if err := ParseConfiguration(container, nil, opts); err != nil { t.Fatal(err) } - if container.CapabilitiesMask["MKNOD"] { + if hasCapability("MKNOD", container.Capabilities) { t.Fatal("container should not have MKNOD enabled") } } diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 5adae30db5..76816e0b9c 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -98,9 +98,7 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver. } func (d *driver) setPrivileged(container *libcontainer.Container) error { - for key := range container.CapabilitiesMask { - container.CapabilitiesMask[key] = true - } + container.Capabilities = libcontainer.GetAllCapabilities() container.Cgroups.DeviceAccess = true delete(container.Context, "restrictions") diff --git a/daemon/execdriver/native/template/default_template.go b/daemon/execdriver/native/template/default_template.go index 32d901d87b..66cfa88a3a 100644 --- a/daemon/execdriver/native/template/default_template.go +++ b/daemon/execdriver/native/template/default_template.go @@ -9,28 +9,13 @@ import ( // New returns the docker default configuration for libcontainer func New() *libcontainer.Container { container := &libcontainer.Container{ - CapabilitiesMask: map[string]bool{ - "SETPCAP": false, - "SYS_MODULE": false, - "SYS_RAWIO": false, - "SYS_PACCT": false, - "SYS_ADMIN": false, - "SYS_NICE": false, - "SYS_RESOURCE": false, - "SYS_TIME": false, - "SYS_TTY_CONFIG": false, - "AUDIT_WRITE": false, - "AUDIT_CONTROL": false, - "MAC_OVERRIDE": false, - "MAC_ADMIN": false, - "NET_ADMIN": false, - "MKNOD": true, - "SYSLOG": false, - "SETUID": true, - "SETGID": true, - "CHOWN": true, - "NET_RAW": true, - "DAC_OVERRIDE": true, + Capabilities: []string{ + "MKNOD", + "SETUID", + "SETGID", + "CHOWN", + "NET_RAW", + "DAC_OVERRIDE", }, Namespaces: map[string]bool{ "NEWNS": true, diff --git a/pkg/libcontainer/container.go b/pkg/libcontainer/container.go index 7328f5544e..0ea8d37c20 100644 --- a/pkg/libcontainer/container.go +++ b/pkg/libcontainer/container.go @@ -11,19 +11,19 @@ type Context map[string]string // Container defines configuration options for how a // container is setup inside a directory and how a process should be executed type Container struct { - Hostname string `json:"hostname,omitempty"` // hostname - ReadonlyFs bool `json:"readonly_fs,omitempty"` // set the containers rootfs as readonly - NoPivotRoot bool `json:"no_pivot_root,omitempty"` // this can be enabled if you are running in ramdisk - User string `json:"user,omitempty"` // user to execute the process as - WorkingDir string `json:"working_dir,omitempty"` // current working directory - Env []string `json:"environment,omitempty"` // environment to set - Tty bool `json:"tty,omitempty"` // setup a proper tty or not - Namespaces map[string]bool `json:"namespaces,omitempty"` // namespaces to apply - CapabilitiesMask map[string]bool `json:"capabilities_mask,omitempty"` // capabilities to drop - Networks []*Network `json:"networks,omitempty"` // nil for host's network stack - Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"` // cgroups - Context Context `json:"context,omitempty"` // generic context for specific options (apparmor, selinux) - Mounts Mounts `json:"mounts,omitempty"` + Hostname string `json:"hostname,omitempty"` // hostname + ReadonlyFs bool `json:"readonly_fs,omitempty"` // set the containers rootfs as readonly + NoPivotRoot bool `json:"no_pivot_root,omitempty"` // this can be enabled if you are running in ramdisk + User string `json:"user,omitempty"` // user to execute the process as + WorkingDir string `json:"working_dir,omitempty"` // current working directory + Env []string `json:"environment,omitempty"` // environment to set + Tty bool `json:"tty,omitempty"` // setup a proper tty or not + Namespaces map[string]bool `json:"namespaces,omitempty"` // namespaces to apply + Capabilities []string `json:"capabilities,omitempty"` // capabilities given to the container + Networks []*Network `json:"networks,omitempty"` // nil for host's network stack + Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"` // cgroups + Context Context `json:"context,omitempty"` // generic context for specific options (apparmor, selinux) + Mounts Mounts `json:"mounts,omitempty"` } // Network defines configuration for a container's networking stack diff --git a/pkg/libcontainer/container.json b/pkg/libcontainer/container.json index 33d79600d4..07950fe58a 100644 --- a/pkg/libcontainer/container.json +++ b/pkg/libcontainer/container.json @@ -24,24 +24,9 @@ "mtu": 1500 } ], - "capabilities_mask": { - "SYSLOG": false, - "MKNOD": true, - "NET_ADMIN": false, - "MAC_ADMIN": false, - "MAC_OVERRIDE": false, - "AUDIT_CONTROL": false, - "AUDIT_WRITE": false, - "SYS_TTY_CONFIG": false, - "SETPCAP": false, - "SYS_MODULE": false, - "SYS_RAWIO": false, - "SYS_PACCT": false, - "SYS_ADMIN": false, - "SYS_NICE": false, - "SYS_RESOURCE": false, - "SYS_TIME": false - }, + "capabilities": [ + "MKNOD" + ], "cgroups": { "name": "docker-koye", "parent": "docker" diff --git a/pkg/libcontainer/container_test.go b/pkg/libcontainer/container_test.go index c02385af3f..b3f240740c 100644 --- a/pkg/libcontainer/container_test.go +++ b/pkg/libcontainer/container_test.go @@ -6,6 +6,16 @@ import ( "testing" ) +// Checks whether the expected capability is specified in the capabilities. +func hasCapability(expected string, capabilities []string) bool { + for _, capability := range capabilities { + if capability == expected { + return true + } + } + return false +} + func TestContainerJsonFormat(t *testing.T) { f, err := os.Open("container.json") if err != nil { @@ -37,22 +47,17 @@ func TestContainerJsonFormat(t *testing.T) { t.Fail() } - if _, exists := container.CapabilitiesMask["SYS_ADMIN"]; !exists { - t.Log("capabilities mask should contain SYS_ADMIN") - t.Fail() - } - - if container.CapabilitiesMask["SYS_ADMIN"] { + if hasCapability("SYS_ADMIN", container.Capabilities) { t.Log("SYS_ADMIN should not be enabled in capabilities mask") t.Fail() } - if !container.CapabilitiesMask["MKNOD"] { + if !hasCapability("MKNOD", container.Capabilities) { t.Log("MKNOD should be enabled in capabilities mask") t.Fail() } - if container.CapabilitiesMask["SYS_CHROOT"] { + if hasCapability("SYS_CHROOT", container.Capabilities) { t.Log("capabilities mask should not contain SYS_CHROOT") t.Fail() } diff --git a/pkg/libcontainer/security/capabilities/capabilities.go b/pkg/libcontainer/security/capabilities/capabilities.go index 107417ad7d..ba72070f50 100644 --- a/pkg/libcontainer/security/capabilities/capabilities.go +++ b/pkg/libcontainer/security/capabilities/capabilities.go @@ -26,14 +26,12 @@ func DropCapabilities(container *libcontainer.Container) error { return nil } -// getCapabilitiesMask returns the capabilities that should not be dropped by the container. +// getEnabledCapabilities returns the capabilities that should not be dropped by the container. func getEnabledCapabilities(container *libcontainer.Container) []capability.Cap { keep := []capability.Cap{} - for key, enabled := range container.CapabilitiesMask { - if enabled { - if c := libcontainer.GetCapability(key); c != nil { - keep = append(keep, c.Value) - } + for _, capability := range container.Capabilities { + if c := libcontainer.GetCapability(capability); c != nil { + keep = append(keep, c.Value) } } return keep diff --git a/pkg/libcontainer/types.go b/pkg/libcontainer/types.go index 4c8f60c477..ed5cce4836 100644 --- a/pkg/libcontainer/types.go +++ b/pkg/libcontainer/types.go @@ -123,6 +123,14 @@ func GetCapability(key string) *Capability { return nil } +func GetAllCapabilities() []string { + output := make([]string, len(capabilityList)) + for i, capability := range capabilityList { + output[i] = capability.String() + } + return output +} + // Contains returns true if the specified Capability is // in the slice func (c Capabilities) Contains(capp string) bool {