diff --git a/docs/cri/config.md b/docs/cri/config.md index 20609b02a..30d0217e2 100644 --- a/docs/cri/config.md +++ b/docs/cri/config.md @@ -168,6 +168,14 @@ version = 2 # i.e pass host devices through to privileged containers. privileged_without_host_devices = false + # privileged_without_host_devices_all_devices_allowed allows the allowlisting of all devices when + # privileged_without_host_devices is enabled. + # In plain privileged mode all host device nodes are added to the container's spec and all devices + # are put in the container's device allowlist. This flags is for the modification of the privileged_without_host_devices + # option so that even when no host devices are implicitly added to the container, all devices allowlisting is still enabled. + # Requires privileged_without_host_devices to be enabled. Defaults to false. + privileged_without_host_devices_all_devices_allowed = false + # base_runtime_spec is a file path to a JSON file with the OCI spec that will be used as the base spec that all # container's are created from. # Use containerd's `ctr oci spec > /etc/containerd/cri-base.json` to output initial spec file. diff --git a/pkg/cri/config/config.go b/pkg/cri/config/config.go index aeee9bfd4..b91229aa0 100644 --- a/pkg/cri/config/config.go +++ b/pkg/cri/config/config.go @@ -54,6 +54,10 @@ type Runtime struct { // PrivilegedWithoutHostDevices overloads the default behaviour for adding host devices to the // runtime spec when the container is privileged. Defaults to false. PrivilegedWithoutHostDevices bool `toml:"privileged_without_host_devices" json:"privileged_without_host_devices"` + // PrivilegedWithoutHostDevicesAllDevicesAllowed overloads the default behaviour device allowlisting when + // 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"` // 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"` } @@ -368,6 +372,9 @@ func ValidatePluginConfig(ctx context.Context, c *PluginConfig) error { } log.G(ctx).Warning("`runtime_root` is deprecated, please use runtime `options` instead") } + if !r.PrivilegedWithoutHostDevices && r.PrivilegedWithoutHostDevicesAllDevicesAllowed { + return errors.Errorf("`privileged_without_host_devices_all_devices_allowed` requires `privileged_without_host_devices` to be enabled") + } } useConfigPath := c.Registry.ConfigPath != "" diff --git a/pkg/cri/config/config_test.go b/pkg/cri/config/config_test.go index 15759c636..db48cb8fe 100644 --- a/pkg/cri/config/config_test.go +++ b/pkg/cri/config/config_test.go @@ -360,6 +360,21 @@ func TestValidateConfig(t *testing.T) { }, expectedErr: "`configs.tls` cannot be set when `config_path` is provided", }, + "privileged_without_host_devices_all_devices_allowed without privileged_without_host_devices": { + config: &PluginConfig{ + ContainerdConfig: ContainerdConfig{ + DefaultRuntimeName: RuntimeDefault, + Runtimes: map[string]Runtime{ + RuntimeDefault: { + PrivilegedWithoutHostDevices: false, + PrivilegedWithoutHostDevicesAllDevicesAllowed: true, + Type: "default", + }, + }, + }, + }, + expectedErr: "`privileged_without_host_devices_all_devices_allowed` requires `privileged_without_host_devices` to be enabled", + }, } { t.Run(desc, func(t *testing.T) { err := ValidatePluginConfig(context.Background(), test.config) diff --git a/pkg/cri/server/container_create_linux.go b/pkg/cri/server/container_create_linux.go index f94e1509d..91177d565 100644 --- a/pkg/cri/server/container_create_linux.go +++ b/pkg/cri/server/container_create_linux.go @@ -223,6 +223,9 @@ func (c *criService) containerSpec( specOpts = append(specOpts, oci.WithPrivileged) if !ociRuntime.PrivilegedWithoutHostDevices { specOpts = append(specOpts, oci.WithHostDevices, oci.WithAllDevicesAllowed) + } else if ociRuntime.PrivilegedWithoutHostDevicesAllDevicesAllowed { + // allow rwm on all devices for the container + specOpts = append(specOpts, oci.WithAllDevicesAllowed) } } diff --git a/pkg/cri/server/container_create_linux_test.go b/pkg/cri/server/container_create_linux_test.go index 321532de2..3178f0b10 100644 --- a/pkg/cri/server/container_create_linux_test.go +++ b/pkg/cri/server/container_create_linux_test.go @@ -1380,29 +1380,46 @@ func TestPrivilegedDevices(t *testing.T) { containerConfig, sandboxConfig, imageConfig, _ := getCreateContainerTestData() for desc, test := range map[string]struct { - privileged bool - privilegedWithoutHostDevices bool - expectHostDevices bool + privileged bool + privilegedWithoutHostDevices bool + privilegedWithoutHostDevicesAllDevicesAllowed bool + expectHostDevices bool + expectAllDevicesAllowed bool }{ "expect no host devices when privileged is false": { privileged: false, privilegedWithoutHostDevices: false, - expectHostDevices: false, + privilegedWithoutHostDevicesAllDevicesAllowed: false, + expectHostDevices: false, + expectAllDevicesAllowed: false, }, "expect no host devices when privileged is false and privilegedWithoutHostDevices is true": { privileged: false, privilegedWithoutHostDevices: true, - expectHostDevices: false, + privilegedWithoutHostDevicesAllDevicesAllowed: false, + expectHostDevices: false, + expectAllDevicesAllowed: false, }, - "expect host devices when privileged is true": { + "expect host devices and all device allowlist when privileged is true": { privileged: true, privilegedWithoutHostDevices: false, - expectHostDevices: true, + privilegedWithoutHostDevicesAllDevicesAllowed: false, + expectHostDevices: true, + expectAllDevicesAllowed: true, }, "expect no host devices when privileged is true and privilegedWithoutHostDevices is true": { privileged: true, privilegedWithoutHostDevices: true, - expectHostDevices: false, + privilegedWithoutHostDevicesAllDevicesAllowed: false, + expectHostDevices: false, + expectAllDevicesAllowed: false, + }, + "expect host devices and all devices allowlist when privileged is true and privilegedWithoutHostDevices is true and privilegedWithoutHostDevicesAllDevicesAllowed is true": { + privileged: true, + privilegedWithoutHostDevices: true, + privilegedWithoutHostDevicesAllDevicesAllowed: true, + expectHostDevices: false, + expectAllDevicesAllowed: true, }, } { t.Logf("TestCase %q", desc) @@ -1411,7 +1428,8 @@ func TestPrivilegedDevices(t *testing.T) { sandboxConfig.Linux.SecurityContext.Privileged = test.privileged ociRuntime := config.Runtime{ - PrivilegedWithoutHostDevices: test.privilegedWithoutHostDevices, + PrivilegedWithoutHostDevices: test.privilegedWithoutHostDevices, + PrivilegedWithoutHostDevicesAllDevicesAllowed: test.privilegedWithoutHostDevicesAllDevicesAllowed, } spec, err := c.containerSpec(t.Name(), testSandboxID, testPid, "", testContainerName, testImageName, containerConfig, sandboxConfig, imageConfig, nil, ociRuntime) assert.NoError(t, err) @@ -1431,6 +1449,11 @@ func TestPrivilegedDevices(t *testing.T) { } else { assert.Empty(t, spec.Linux.Devices) } + + assert.Len(t, spec.Linux.Resources.Devices, 1) + assert.Equal(t, spec.Linux.Resources.Devices[0].Allow, test.expectAllDevicesAllowed) + assert.Equal(t, spec.Linux.Resources.Devices[0].Access, "rwm") + } }