Docker-DCO-1.1-Signed-off-by: Victor Marmol <vmarmol@google.com> (github: vmarmol)
| ... | ... |
@@ -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 {
|