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:
Divya
2024-12-10 12:05:38 +00:00
parent 3358f57a77
commit dda7020429
5 changed files with 73 additions and 5 deletions

View File

@@ -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")
}

View File

@@ -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{

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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