Merge pull request #5863 from vieux/pr5862

Make libcontainer's CapabilitiesMask into a []string (Capabilities). + 	add support for CAP_FOWNER
This commit is contained in:
Michael Crosby 2014-05-16 18:23:50 -07:00
commit cc84dd0967
9 changed files with 76 additions and 80 deletions

View File

@ -109,12 +109,19 @@ func memorySwap(container *libcontainer.Container, context interface{}, value st
} }
func addCap(container *libcontainer.Container, context interface{}, value string) error { func addCap(container *libcontainer.Container, context interface{}, value string) error {
container.CapabilitiesMask[value] = true container.Capabilities = append(container.Capabilities, value)
return nil return nil
} }
func dropCap(container *libcontainer.Container, context interface{}, value string) error { 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 return nil
} }

View File

@ -4,8 +4,19 @@ import (
"testing" "testing"
"github.com/dotcloud/docker/daemon/execdriver/native/template" "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) { func TestSetReadonlyRootFs(t *testing.T) {
var ( var (
container = template.New() container = template.New()
@ -39,10 +50,10 @@ func TestConfigurationsDoNotConflict(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if !container1.CapabilitiesMask["NET_ADMIN"] { if !hasCapability("NET_ADMIN", container1.Capabilities) {
t.Fatal("container one should have NET_ADMIN enabled") 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") t.Fatal("container two should not have NET_ADMIN enabled")
} }
} }
@ -138,10 +149,10 @@ func TestAddCap(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if !container.CapabilitiesMask["MKNOD"] { if !hasCapability("MKNOD", container.Capabilities) {
t.Fatal("container should have MKNOD enabled") 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") t.Fatal("container should have SYS_ADMIN enabled")
} }
} }
@ -154,14 +165,12 @@ func TestDropCap(t *testing.T) {
} }
) )
// enabled all caps like in privileged mode // enabled all caps like in privileged mode
for key := range container.CapabilitiesMask { container.Capabilities = libcontainer.GetAllCapabilities()
container.CapabilitiesMask[key] = true
}
if err := ParseConfiguration(container, nil, opts); err != nil { if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if container.CapabilitiesMask["MKNOD"] { if hasCapability("MKNOD", container.Capabilities) {
t.Fatal("container should not have MKNOD enabled") t.Fatal("container should not have MKNOD enabled")
} }
} }

View File

@ -98,9 +98,7 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
} }
func (d *driver) setPrivileged(container *libcontainer.Container) error { func (d *driver) setPrivileged(container *libcontainer.Container) error {
for key := range container.CapabilitiesMask { container.Capabilities = libcontainer.GetAllCapabilities()
container.CapabilitiesMask[key] = true
}
container.Cgroups.DeviceAccess = true container.Cgroups.DeviceAccess = true
delete(container.Context, "restrictions") delete(container.Context, "restrictions")

View File

@ -9,28 +9,13 @@ import (
// New returns the docker default configuration for libcontainer // New returns the docker default configuration for libcontainer
func New() *libcontainer.Container { func New() *libcontainer.Container {
container := &libcontainer.Container{ container := &libcontainer.Container{
CapabilitiesMask: map[string]bool{ Capabilities: []string{
"SETPCAP": false, "MKNOD",
"SYS_MODULE": false, "SETUID",
"SYS_RAWIO": false, "SETGID",
"SYS_PACCT": false, "CHOWN",
"SYS_ADMIN": false, "NET_RAW",
"SYS_NICE": false, "DAC_OVERRIDE",
"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,
}, },
Namespaces: map[string]bool{ Namespaces: map[string]bool{
"NEWNS": true, "NEWNS": true,

View File

@ -11,19 +11,19 @@ type Context map[string]string
// Container defines configuration options for how a // Container defines configuration options for how a
// container is setup inside a directory and how a process should be executed // container is setup inside a directory and how a process should be executed
type Container struct { type Container struct {
Hostname string `json:"hostname,omitempty"` // hostname Hostname string `json:"hostname,omitempty"` // hostname
ReadonlyFs bool `json:"readonly_fs,omitempty"` // set the containers rootfs as readonly 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 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 User string `json:"user,omitempty"` // user to execute the process as
WorkingDir string `json:"working_dir,omitempty"` // current working directory WorkingDir string `json:"working_dir,omitempty"` // current working directory
Env []string `json:"environment,omitempty"` // environment to set Env []string `json:"environment,omitempty"` // environment to set
Tty bool `json:"tty,omitempty"` // setup a proper tty or not Tty bool `json:"tty,omitempty"` // setup a proper tty or not
Namespaces map[string]bool `json:"namespaces,omitempty"` // namespaces to apply Namespaces map[string]bool `json:"namespaces,omitempty"` // namespaces to apply
CapabilitiesMask map[string]bool `json:"capabilities_mask,omitempty"` // capabilities to drop Capabilities []string `json:"capabilities,omitempty"` // capabilities given to the container
Networks []*Network `json:"networks,omitempty"` // nil for host's network stack Networks []*Network `json:"networks,omitempty"` // nil for host's network stack
Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"` // cgroups Cgroups *cgroups.Cgroup `json:"cgroups,omitempty"` // cgroups
Context Context `json:"context,omitempty"` // generic context for specific options (apparmor, selinux) Context Context `json:"context,omitempty"` // generic context for specific options (apparmor, selinux)
Mounts Mounts `json:"mounts,omitempty"` Mounts Mounts `json:"mounts,omitempty"`
} }
// Network defines configuration for a container's networking stack // Network defines configuration for a container's networking stack

View File

@ -24,24 +24,9 @@
"mtu": 1500 "mtu": 1500
} }
], ],
"capabilities_mask": { "capabilities": [
"SYSLOG": false, "MKNOD"
"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
},
"cgroups": { "cgroups": {
"name": "docker-koye", "name": "docker-koye",
"parent": "docker" "parent": "docker"

View File

@ -6,6 +6,16 @@ import (
"testing" "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) { func TestContainerJsonFormat(t *testing.T) {
f, err := os.Open("container.json") f, err := os.Open("container.json")
if err != nil { if err != nil {
@ -37,22 +47,17 @@ func TestContainerJsonFormat(t *testing.T) {
t.Fail() t.Fail()
} }
if _, exists := container.CapabilitiesMask["SYS_ADMIN"]; !exists { if hasCapability("SYS_ADMIN", container.Capabilities) {
t.Log("capabilities mask should contain SYS_ADMIN")
t.Fail()
}
if container.CapabilitiesMask["SYS_ADMIN"] {
t.Log("SYS_ADMIN should not be enabled in capabilities mask") t.Log("SYS_ADMIN should not be enabled in capabilities mask")
t.Fail() t.Fail()
} }
if !container.CapabilitiesMask["MKNOD"] { if !hasCapability("MKNOD", container.Capabilities) {
t.Log("MKNOD should be enabled in capabilities mask") t.Log("MKNOD should be enabled in capabilities mask")
t.Fail() t.Fail()
} }
if container.CapabilitiesMask["SYS_CHROOT"] { if hasCapability("SYS_CHROOT", container.Capabilities) {
t.Log("capabilities mask should not contain SYS_CHROOT") t.Log("capabilities mask should not contain SYS_CHROOT")
t.Fail() t.Fail()
} }

View File

@ -26,14 +26,12 @@ func DropCapabilities(container *libcontainer.Container) error {
return nil 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 { func getEnabledCapabilities(container *libcontainer.Container) []capability.Cap {
keep := []capability.Cap{} keep := []capability.Cap{}
for key, enabled := range container.CapabilitiesMask { for _, capability := range container.Capabilities {
if enabled { if c := libcontainer.GetCapability(capability); c != nil {
if c := libcontainer.GetCapability(key); c != nil { keep = append(keep, c.Value)
keep = append(keep, c.Value)
}
} }
} }
return keep return keep

View File

@ -60,6 +60,7 @@ var (
{Key: "CHOWN", Value: capability.CAP_CHOWN}, {Key: "CHOWN", Value: capability.CAP_CHOWN},
{Key: "NET_RAW", Value: capability.CAP_NET_RAW}, {Key: "NET_RAW", Value: capability.CAP_NET_RAW},
{Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE}, {Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE},
{Key: "FOWNER", Value: capability.CAP_FOWNER},
} }
) )
@ -123,6 +124,14 @@ func GetCapability(key string) *Capability {
return nil 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 // Contains returns true if the specified Capability is
// in the slice // in the slice
func (c Capabilities) Contains(capp string) bool { func (c Capabilities) Contains(capp string) bool {