Skip to content

Commit 072400f

Browse files
committed
Make cgroup namespaces configurable
This adds both a daemon-wide flag and a container creation property: - Set the `CgroupnsMode: "host|private"` HostConfig property at container creation time to control what cgroup namespace the container is created in - Set the `--default-cgroupns-mode=host|private` daemon flag to control what cgroup namespace containers are created in by default - Set the default if the daemon flag is unset to "host", for backward compatibility - Default to CgroupnsMode: "host" for client versions < 1.40 Signed-off-by: Rob Gulewich <[email protected]>
1 parent 256eb04 commit 072400f

28 files changed

Lines changed: 525 additions & 165 deletions

api/server/router/container/container_routes.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,11 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
489489
if hostConfig.IpcMode.IsEmpty() {
490490
hostConfig.IpcMode = container.IpcMode("shareable")
491491
}
492+
493+
// Older clients expect the default to be "host"
494+
if hostConfig.CgroupnsMode.IsEmpty() {
495+
hostConfig.CgroupnsMode = container.CgroupnsMode("host")
496+
}
492497
}
493498

494499
if hostConfig != nil && hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 {

api/swagger.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,19 @@ definitions:
707707
description: "A list of kernel capabilities to drop from the container. Conflicts with option 'Capabilities'"
708708
items:
709709
type: "string"
710+
CgroupnsMode:
711+
type: "string"
712+
enum:
713+
- "private"
714+
- "host"
715+
description: |
716+
cgroup namespace mode for the container. Possible values are:
717+
718+
- `"private"`: the container runs in its own private cgroup namespace
719+
- `"host"`: use the host system's cgroup namespace
720+
721+
If not specified, the daemon default is used, which can either be `"private"`
722+
or `"host"`, depending on daemon version, kernel support and configuration.
710723
Dns:
711724
type: "array"
712725
description: "A list of DNS servers for the container to use."

api/types/container/host_config.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,29 @@ import (
1010
"github.com/docker/go-units"
1111
)
1212

13+
// CgroupnsMode represents the cgroup namespace mode of the container
14+
type CgroupnsMode string
15+
16+
// IsPrivate indicates whether the container uses its own private cgroup namespace
17+
func (c CgroupnsMode) IsPrivate() bool {
18+
return c == "private"
19+
}
20+
21+
// IsHost indicates whether the container shares the host's cgroup namespace
22+
func (c CgroupnsMode) IsHost() bool {
23+
return c == "host"
24+
}
25+
26+
// IsEmpty indicates whether the container cgroup namespace mode is unset
27+
func (c CgroupnsMode) IsEmpty() bool {
28+
return c == ""
29+
}
30+
31+
// Valid indicates whether the cgroup namespace mode is valid
32+
func (c CgroupnsMode) Valid() bool {
33+
return c.IsEmpty() || c.IsPrivate() || c.IsHost()
34+
}
35+
1336
// Isolation represents the isolation technology of a container. The supported
1437
// values are platform specific
1538
type Isolation string
@@ -382,9 +405,10 @@ type HostConfig struct {
382405
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
383406
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container
384407
Capabilities []string `json:"Capabilities"` // List of kernel capabilities to be available for container (this overrides the default set)
385-
DNS []string `json:"Dns"` // List of DNS server to lookup
386-
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
387-
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
408+
CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container
409+
DNS []string `json:"Dns"` // List of DNS server to lookup
410+
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
411+
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
388412
ExtraHosts []string // List of extra hosts
389413
GroupAdd []string // List of additional groups that the container process will run as
390414
IpcMode IpcMode // IPC namespace to use for the container

cmd/dockerd/config_unix.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
6464
// rootless needs to be explicitly specified for running "rootful" dockerd in rootless dockerd (#38702)
6565
// Note that defaultUserlandProxyPath and honorXDG are configured according to the value of rootless.RunningWithRootlessKit, not the value of --rootless.
6666
flags.BoolVar(&conf.Rootless, "rootless", rootless.RunningWithRootlessKit(), "Enable rootless mode; typically used with RootlessKit (experimental)")
67+
flags.StringVar(&conf.CgroupNamespaceMode, "default-cgroupns-mode", config.DefaultCgroupNamespaceMode, `Default mode for containers cgroup namespace ("host" | "private")`)
6768
return nil
6869
}

daemon/config/config_unix.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
)
1212

1313
const (
14+
// DefaultCgroupNamespaceMode is the default for a container's CgroupnsMode, if not set otherwise
15+
DefaultCgroupNamespaceMode = "host" // TODO: change to private
1416
// DefaultIpcMode is default for container's IpcMode, if not set otherwise
1517
DefaultIpcMode = "private"
1618
)
@@ -37,6 +39,7 @@ type Config struct {
3739
ShmSize opts.MemBytes `json:"default-shm-size,omitempty"`
3840
NoNewPrivileges bool `json:"no-new-privileges,omitempty"`
3941
IpcMode string `json:"default-ipc-mode,omitempty"`
42+
CgroupNamespaceMode string `json:"default-cgroupns-mode,omitempty"`
4043
// ResolvConf is the path to the configuration of the host resolver
4144
ResolvConf string `json:"resolv-conf,omitempty"`
4245
Rootless bool `json:"rootless,omitempty"`
@@ -84,9 +87,22 @@ func verifyDefaultIpcMode(mode string) error {
8487
return nil
8588
}
8689

90+
func verifyDefaultCgroupNsMode(mode string) error {
91+
cm := containertypes.CgroupnsMode(mode)
92+
if !cm.Valid() {
93+
return fmt.Errorf("Default cgroup namespace mode (%v) is invalid. Use \"host\" or \"private\".", cm) // nolint: golint
94+
}
95+
96+
return nil
97+
}
98+
8799
// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid.
88100
func (conf *Config) ValidatePlatformConfig() error {
89-
return verifyDefaultIpcMode(conf.IpcMode)
101+
if err := verifyDefaultIpcMode(conf.IpcMode); err != nil {
102+
return err
103+
}
104+
105+
return verifyDefaultCgroupNsMode(conf.CgroupNamespaceMode)
90106
}
91107

92108
// IsRootless returns conf.Rootless

daemon/daemon.go

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,27 +81,26 @@ var (
8181

8282
// Daemon holds information about the Docker daemon.
8383
type Daemon struct {
84-
ID string
85-
repository string
86-
containers container.Store
87-
containersReplica container.ViewDB
88-
execCommands *exec.Store
89-
imageService *images.ImageService
90-
idIndex *truncindex.TruncIndex
91-
configStore *config.Config
92-
statsCollector *stats.Collector
93-
defaultLogConfig containertypes.LogConfig
94-
RegistryService registry.Service
95-
EventsService *events.Events
96-
netController libnetwork.NetworkController
97-
volumes *volumesservice.VolumesService
98-
discoveryWatcher discovery.Reloader
99-
root string
100-
seccompEnabled bool
101-
apparmorEnabled bool
102-
cgroupNamespacesEnabled bool
103-
shutdown bool
104-
idMapping *idtools.IdentityMapping
84+
ID string
85+
repository string
86+
containers container.Store
87+
containersReplica container.ViewDB
88+
execCommands *exec.Store
89+
imageService *images.ImageService
90+
idIndex *truncindex.TruncIndex
91+
configStore *config.Config
92+
statsCollector *stats.Collector
93+
defaultLogConfig containertypes.LogConfig
94+
RegistryService registry.Service
95+
EventsService *events.Events
96+
netController libnetwork.NetworkController
97+
volumes *volumesservice.VolumesService
98+
discoveryWatcher discovery.Reloader
99+
root string
100+
seccompEnabled bool
101+
apparmorEnabled bool
102+
shutdown bool
103+
idMapping *idtools.IdentityMapping
105104
// TODO: move graphDrivers field to an InfoService
106105
graphDrivers map[string]string // By operating system
107106

@@ -1021,7 +1020,6 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
10211020
d.idMapping = idMapping
10221021
d.seccompEnabled = sysInfo.Seccomp
10231022
d.apparmorEnabled = sysInfo.AppArmor
1024-
d.cgroupNamespacesEnabled = sysInfo.CgroupNamespaces
10251023

10261024
d.linkIndex = newLinkIndex()
10271025

daemon/daemon_unix.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,15 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
356356
hostConfig.IpcMode = containertypes.IpcMode(m)
357357
}
358358

359+
// Set default cgroup namespace mode, if unset for container
360+
if hostConfig.CgroupnsMode.IsEmpty() {
361+
m := config.DefaultCgroupNamespaceMode
362+
if daemon.configStore != nil {
363+
m = daemon.configStore.CgroupNamespaceMode
364+
}
365+
hostConfig.CgroupnsMode = containertypes.CgroupnsMode(m)
366+
}
367+
359368
adaptSharedNamespaceContainer(daemon, hostConfig)
360369

361370
var err error
@@ -675,6 +684,19 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
675684
}
676685
}
677686

687+
if !hostConfig.CgroupnsMode.Valid() {
688+
return warnings, fmt.Errorf("invalid cgroup namespace mode: %v", hostConfig.CgroupnsMode)
689+
}
690+
if hostConfig.CgroupnsMode.IsPrivate() {
691+
if !sysInfo.CgroupNamespaces {
692+
warnings = append(warnings, "Your kernel does not support cgroup namespaces. Cgroup namespace setting discarded.")
693+
}
694+
695+
if hostConfig.Privileged {
696+
return warnings, fmt.Errorf("privileged mode is incompatible with private cgroup namespaces. You must run the container in the host cgroup namespace when running privileged mode")
697+
}
698+
}
699+
678700
return warnings, nil
679701
}
680702

daemon/info.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInf
178178
if daemon.Rootless() {
179179
securityOptions = append(securityOptions, "name=rootless")
180180
}
181+
if daemon.cgroupNamespacesEnabled(sysInfo) {
182+
securityOptions = append(securityOptions, "name=cgroupns")
183+
}
184+
181185
v.SecurityOptions = securityOptions
182186
}
183187

daemon/info_unix.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"github.com/docker/docker/api/types"
13+
containertypes "github.com/docker/docker/api/types/container"
1314
"github.com/docker/docker/dockerversion"
1415
"github.com/docker/docker/pkg/sysinfo"
1516
"github.com/pkg/errors"
@@ -247,6 +248,10 @@ func parseRuncVersion(v string) (version string, commit string, err error) {
247248
return version, commit, err
248249
}
249250

251+
func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool {
252+
return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(daemon.configStore.CgroupNamespaceMode).IsPrivate()
253+
}
254+
250255
// Rootless returns true if daemon is running in rootless mode
251256
func (daemon *Daemon) Rootless() bool {
252257
return daemon.configStore.Rootless

daemon/info_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ func (daemon *Daemon) fillPlatformVersion(v *types.Version) {}
1414
func fillDriverWarnings(v *types.Info) {
1515
}
1616

17+
func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool {
18+
return false
19+
}
20+
1721
// Rootless returns true if daemon is running in rootless mode
1822
func (daemon *Daemon) Rootless() bool {
1923
return false

0 commit comments

Comments
 (0)