mirror of
https://github.com/containerd/containerd.git
synced 2026-08-08 00:51:50 +00:00
Enable Writable cgroups for unprivileged containers
Currently, cgroupfs is only writable when containers are running in privileged mode. For unprivileged containers with cgroup v2 enabled, the cgroup interface (/sys/fs/cgroup) is mounted read-only by default, preventing containers from managing their own cgroup hierarchies. This PR enables the support for writable cgroups in unprivileged containers via CgroupWritable field. Signed-off-by: Divya <ranidivya063@gmail.com>
This commit is contained in:
@@ -32,6 +32,7 @@ import (
|
||||
runcoptions "github.com/containerd/containerd/api/types/runc/options"
|
||||
runtimeoptions "github.com/containerd/containerd/api/types/runtimeoptions/v1"
|
||||
"github.com/containerd/containerd/v2/internal/cri/annotations"
|
||||
"github.com/containerd/containerd/v2/internal/cri/opts"
|
||||
"github.com/containerd/containerd/v2/pkg/deprecation"
|
||||
"github.com/containerd/containerd/v2/plugins"
|
||||
)
|
||||
@@ -102,6 +103,8 @@ type Runtime struct {
|
||||
// to the runtime spec when the container when PrivilegedWithoutHostDevices is already enabled. Requires
|
||||
// PrivilegedWithoutHostDevices to be enabled. Defaults to false.
|
||||
PrivilegedWithoutHostDevicesAllDevicesAllowed bool `toml:"privileged_without_host_devices_all_devices_allowed" json:"privileged_without_host_devices_all_devices_allowed"`
|
||||
// CgroupWritable enables writable cgroups in non-privileged containers
|
||||
CgroupWritable bool `toml:"cgroup_writable" json:"cgroupWritable"`
|
||||
// BaseRuntimeSpec is a json file with OCI spec to use as base spec that all container's will be created from.
|
||||
BaseRuntimeSpec string `toml:"base_runtime_spec" json:"baseRuntimeSpec"`
|
||||
// NetworkPluginConfDir is a directory containing the CNI network information for the runtime class.
|
||||
@@ -527,6 +530,10 @@ func ValidateRuntimeConfig(ctx context.Context, c *RuntimeConfig) ([]deprecation
|
||||
}
|
||||
|
||||
for k, r := range c.ContainerdConfig.Runtimes {
|
||||
if r.CgroupWritable && !opts.IsCgroup2UnifiedMode() {
|
||||
return warnings, fmt.Errorf("runtime %s: `cgroup_writable` is only supported on cgroup v2", k)
|
||||
}
|
||||
|
||||
if !r.PrivilegedWithoutHostDevices && r.PrivilegedWithoutHostDevicesAllDevicesAllowed {
|
||||
return warnings, errors.New("`privileged_without_host_devices_all_devices_allowed` requires `privileged_without_host_devices` to be enabled")
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
|
||||
"github.com/containerd/containerd/v2/internal/cri/opts"
|
||||
"github.com/containerd/containerd/v2/pkg/deprecation"
|
||||
)
|
||||
|
||||
@@ -189,6 +190,40 @@ func TestValidateConfig(t *testing.T) {
|
||||
},
|
||||
warnings: []deprecation.Warning{deprecation.CRIRegistryConfigs},
|
||||
},
|
||||
"cgroup_writable enabled": {
|
||||
runtimeConfig: &RuntimeConfig{
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
DefaultRuntimeName: RuntimeDefault,
|
||||
Runtimes: map[string]Runtime{
|
||||
RuntimeDefault: {
|
||||
CgroupWritable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
runtimeExpectedErr: func() string {
|
||||
if !opts.IsCgroup2UnifiedMode() {
|
||||
return "`cgroup_writable` is only supported on cgroup v2"
|
||||
}
|
||||
return ""
|
||||
}(),
|
||||
runtimeExpected: func() *RuntimeConfig {
|
||||
if !opts.IsCgroup2UnifiedMode() {
|
||||
return nil
|
||||
}
|
||||
return &RuntimeConfig{
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
DefaultRuntimeName: RuntimeDefault,
|
||||
Runtimes: map[string]Runtime{
|
||||
RuntimeDefault: {
|
||||
CgroupWritable: true,
|
||||
Sandboxer: string(ModePodSandbox),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}(),
|
||||
},
|
||||
"privileged_without_host_devices_all_devices_allowed without privileged_without_host_devices": {
|
||||
runtimeConfig: &RuntimeConfig{
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
|
||||
@@ -39,8 +39,7 @@ import (
|
||||
"github.com/containerd/log"
|
||||
)
|
||||
|
||||
// WithMounts sorts and adds runtime and CRI mounts to the spec
|
||||
func WithMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount, mountLabel string, handler *runtime.RuntimeHandler) oci.SpecOpts {
|
||||
func withMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount, mountLabel string, handler *runtime.RuntimeHandler, cgroupWritable bool) oci.SpecOpts {
|
||||
return func(ctx context.Context, client oci.Client, _ *containers.Container, s *runtimespec.Spec) (err error) {
|
||||
// mergeMounts merge CRI mounts with extra mounts. If a mount destination
|
||||
// is mounted by both a CRI mount and an extra mount, the CRI mount will
|
||||
@@ -67,12 +66,15 @@ func WithMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*ru
|
||||
// shadow other mounts.
|
||||
sort.Stable(orderedMounts(mounts))
|
||||
|
||||
// Mount cgroup into the container as readonly, which inherits docker's behavior.
|
||||
mode := "ro"
|
||||
if cgroupWritable {
|
||||
mode = "rw"
|
||||
}
|
||||
s.Mounts = append(s.Mounts, runtimespec.Mount{
|
||||
Source: "cgroup",
|
||||
Destination: "/sys/fs/cgroup",
|
||||
Type: "cgroup",
|
||||
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
|
||||
Options: []string{"nosuid", "noexec", "nodev", "relatime", mode},
|
||||
})
|
||||
|
||||
// Copy all mounts from default mounts, except for
|
||||
@@ -214,6 +216,17 @@ func WithMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*ru
|
||||
}
|
||||
}
|
||||
|
||||
// WithMounts sorts and adds runtime and CRI mounts to the spec
|
||||
func WithMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount, mountLabel string, handler *runtime.RuntimeHandler) oci.SpecOpts {
|
||||
return withMounts(osi, config, extra, mountLabel, handler, false)
|
||||
|
||||
}
|
||||
|
||||
// WithMountsCgroupWritable sorts and adds runtime and CRI mounts to the spec if cgroup_writable is enabled.
|
||||
func WithMountsCgroupWritable(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount, mountLabel string, handler *runtime.RuntimeHandler) oci.SpecOpts {
|
||||
return withMounts(osi, config, extra, mountLabel, handler, true)
|
||||
}
|
||||
|
||||
// Ensure mount point on which path is mounted, is shared.
|
||||
func ensureShared(path string, lookupMount func(string) (mount.Info, error)) error {
|
||||
mountInfo, err := lookupMount(path)
|
||||
|
||||
@@ -40,3 +40,9 @@ func WithCDI(_ map[string]string, _ []*runtime.CDIDevice) oci.SpecOpts {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode.
|
||||
// On non-Linux platforms, this always returns false.
|
||||
func IsCgroup2UnifiedMode() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -710,7 +710,14 @@ func (c *criService) buildLinuxSpec(
|
||||
}
|
||||
}()
|
||||
|
||||
specOpts = append(specOpts, customopts.WithMounts(c.os, config, extraMounts, mountLabel, runtimeHandler))
|
||||
var ociSpecOpts oci.SpecOpts
|
||||
if ociRuntime.CgroupWritable {
|
||||
ociSpecOpts = customopts.WithMountsCgroupWritable(c.os, config, extraMounts, mountLabel, runtimeHandler)
|
||||
} else {
|
||||
ociSpecOpts = customopts.WithMounts(c.os, config, extraMounts, mountLabel, runtimeHandler)
|
||||
}
|
||||
|
||||
specOpts = append(specOpts, ociSpecOpts)
|
||||
|
||||
if !c.config.DisableProcMount {
|
||||
// Change the default masked/readonly paths to empty slices
|
||||
|
||||
Reference in New Issue
Block a user