mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-08-12 13:22:28 +00:00
genpolicy: check requested devices
CreateContainerRequest objects can specify devices to be created inside the guest VM. This change ensures that requested devices have a corresponding entry in the PodSpec. Devices that are added to the pod dynamically, for example via the Device Plugin architecture, can be allowlisted globally by adding their definition to the settings file. Fixes: #9651 Signed-off-by: Markus Rudy <mr@edgeless.systems>
This commit is contained in:
parent
ea578f0a80
commit
13310587ed
@ -54,6 +54,7 @@ default AllowRequestsFailingPolicy := false
|
||||
CreateContainerRequest {
|
||||
i_oci := input.OCI
|
||||
i_storages := input.storages
|
||||
i_devices := input.devices
|
||||
|
||||
some p_container in policy_data.containers
|
||||
print("======== CreateContainerRequest: trying next policy container")
|
||||
@ -77,6 +78,9 @@ CreateContainerRequest {
|
||||
p_storages := p_container.storages
|
||||
allow_by_anno(p_oci, i_oci, p_storages, i_storages)
|
||||
|
||||
p_devices := p_container.devices
|
||||
allow_devices(p_devices, i_devices)
|
||||
|
||||
allow_linux(p_oci, i_oci)
|
||||
|
||||
print("CreateContainerRequest: true")
|
||||
@ -328,6 +332,16 @@ allow_log_directory(p_oci, i_oci) {
|
||||
print("allow_log_directory: true")
|
||||
}
|
||||
|
||||
allow_devices(p_devices, i_devices) {
|
||||
print("allow_devices: start")
|
||||
every i_device in i_devices {
|
||||
print("allow_devices: i_device =", i_device)
|
||||
some p_device in p_devices
|
||||
p_device.container_path == i_device.container_path
|
||||
}
|
||||
print("allow_devices: true")
|
||||
}
|
||||
|
||||
allow_linux(p_oci, i_oci) {
|
||||
p_namespaces := p_oci.Linux.Namespaces
|
||||
print("allow_linux: p namespaces =", p_namespaces)
|
||||
@ -339,6 +353,7 @@ allow_linux(p_oci, i_oci) {
|
||||
|
||||
allow_masked_paths(p_oci, i_oci)
|
||||
allow_readonly_paths(p_oci, i_oci)
|
||||
allow_linux_devices(p_oci.Linux.Devices, i_oci.Linux.Devices)
|
||||
|
||||
print("allow_linux: true")
|
||||
}
|
||||
@ -427,6 +442,16 @@ allow_readonly_path(p_elem, i_array, masked_paths) {
|
||||
print("allow_readonly_path 2: true")
|
||||
}
|
||||
|
||||
allow_linux_devices(p_devices, i_devices) {
|
||||
print("allow_linux_devices: start")
|
||||
every i_device in i_devices {
|
||||
print("allow_linux_devices: i_device =", i_device)
|
||||
some p_device in p_devices
|
||||
i_device.Path == p_device.Path
|
||||
}
|
||||
print("allow_linux_devices: true")
|
||||
}
|
||||
|
||||
# Check the consistency of the input "io.katacontainers.pkg.oci.bundle_path"
|
||||
# and io.kubernetes.cri.sandbox-id" values with other fields.
|
||||
allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages) {
|
||||
|
@ -152,12 +152,14 @@ pub fn get_linux(privileged_container: bool) -> policy::KataLinux {
|
||||
"/proc/sys".to_string(),
|
||||
"/proc/sysrq-trigger".to_string(),
|
||||
],
|
||||
Devices: vec![],
|
||||
}
|
||||
} else {
|
||||
policy::KataLinux {
|
||||
Namespaces: vec![],
|
||||
MaskedPaths: vec![],
|
||||
ReadonlyPaths: vec![],
|
||||
Devices: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +186,10 @@ pub struct KataLinux {
|
||||
|
||||
/// ReadonlyPaths sets the provided paths as RO inside the container.
|
||||
pub ReadonlyPaths: Vec<String>,
|
||||
|
||||
/// Devices contains devices to be created inside the container.
|
||||
#[serde(default)]
|
||||
pub Devices: Vec<KataLinuxDevice>,
|
||||
}
|
||||
|
||||
/// OCI container LinuxNamespace struct. This struct is similar to the LinuxNamespace
|
||||
@ -201,6 +205,18 @@ pub struct KataLinuxNamespace {
|
||||
pub Path: String,
|
||||
}
|
||||
|
||||
/// OCI container LinuxDevice struct. This struct is similar to the LinuxDevice
|
||||
/// struct generated from oci.proto, but includes just the fields that are currently
|
||||
/// relevant for automatic generation of policy.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct KataLinuxDevice {
|
||||
/// Type is the type of device.
|
||||
pub Type: String,
|
||||
|
||||
/// Path is the path where the device should be created.
|
||||
pub Path: String,
|
||||
}
|
||||
|
||||
/// OCI container LinuxCapabilities struct. This struct is very similar to the
|
||||
/// LinuxCapabilities struct generated from oci.proto. The main difference is
|
||||
/// that it preserves the upper case field names from oci.proto, for consistency
|
||||
@ -252,6 +268,9 @@ pub struct ContainerPolicy {
|
||||
/// Data compared with req.storages for CreateContainerRequest calls.
|
||||
storages: Vec<agent::Storage>,
|
||||
|
||||
/// Data compared with req.devices for CreateContainerRequest calls.
|
||||
devices: Vec<agent::Device>,
|
||||
|
||||
/// Data compared with req.sandbox_pidns for CreateContainerRequest calls.
|
||||
sandbox_pidns: bool,
|
||||
|
||||
@ -546,6 +565,23 @@ impl AgentPolicy {
|
||||
};
|
||||
let exec_commands = yaml_container.get_exec_commands();
|
||||
|
||||
let mut devices: Vec<agent::Device> = vec![];
|
||||
if let Some(volumeDevices) = &yaml_container.volumeDevices {
|
||||
for volumeDevice in volumeDevices {
|
||||
let mut device = agent::Device::new();
|
||||
device.set_container_path(volumeDevice.devicePath.clone());
|
||||
devices.push(device);
|
||||
|
||||
linux.Devices.push(KataLinuxDevice {
|
||||
Type: "".to_string(),
|
||||
Path: volumeDevice.devicePath.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
for default_device in &c_settings.Linux.Devices {
|
||||
linux.Devices.push(default_device.clone())
|
||||
}
|
||||
|
||||
ContainerPolicy {
|
||||
OCI: KataSpec {
|
||||
Version: version_default(),
|
||||
@ -557,6 +593,7 @@ impl AgentPolicy {
|
||||
Linux: linux,
|
||||
},
|
||||
storages,
|
||||
devices,
|
||||
sandbox_pidns,
|
||||
exec_commands,
|
||||
}
|
||||
|
66
tests/integration/kubernetes/k8s-policy-pvc.bats
Normal file
66
tests/integration/kubernetes/k8s-policy-pvc.bats
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bats
|
||||
#
|
||||
# Copyright (c) 2024 Edgeless Systems GmbH
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
load "${BATS_TEST_DIRNAME}/../../common.bash"
|
||||
load "${BATS_TEST_DIRNAME}/tests_common.sh"
|
||||
|
||||
setup() {
|
||||
auto_generate_policy_enabled || skip "Auto-generated policy tests are disabled."
|
||||
|
||||
pod_name="policy-pod-pvc"
|
||||
pvc_name="policy-dev"
|
||||
|
||||
get_pod_config_dir
|
||||
|
||||
correct_pod_yaml="${pod_config_dir}/k8s-policy-pod-pvc.yaml"
|
||||
incorrect_pod_yaml="${pod_config_dir}/k8s-policy-pod-pvc-incorrect.yaml"
|
||||
pvc_yaml="${pod_config_dir}/k8s-policy-pvc.yaml"
|
||||
|
||||
# Save some time by executing genpolicy a single time.
|
||||
if [ "${BATS_TEST_NUMBER}" == "1" ]; then
|
||||
# Add policy to the correct pod yaml file
|
||||
auto_generate_policy "${pod_config_dir}" "${correct_pod_yaml}"
|
||||
fi
|
||||
|
||||
# Start each test case with a copy of the correct yaml files.
|
||||
cp "${correct_pod_yaml}" "${incorrect_pod_yaml}"
|
||||
}
|
||||
|
||||
@test "Successful pod with auto-generated policy" {
|
||||
kubectl create -f "${correct_pod_yaml}"
|
||||
kubectl create -f "${pvc_yaml}"
|
||||
kubectl wait --for=condition=Ready "--timeout=${timeout}" pod "${pod_name}"
|
||||
}
|
||||
|
||||
# Common function for several test cases from this bats script.
|
||||
test_pod_policy_error() {
|
||||
kubectl create -f "${incorrect_pod_yaml}"
|
||||
kubectl create -f "${pvc_yaml}"
|
||||
wait_for_blocked_request "CreateContainerRequest" "${pod_name}"
|
||||
}
|
||||
|
||||
@test "Policy failure: unexpected device mount" {
|
||||
# Changing the location of a mounted device after policy generation should fail the policy check.
|
||||
yq write -i \
|
||||
"${incorrect_pod_yaml}" \
|
||||
"spec.containers[0].volumeDevices.[0].devicePath" \
|
||||
"/dev/unexpected"
|
||||
|
||||
test_pod_policy_error
|
||||
}
|
||||
|
||||
teardown() {
|
||||
auto_generate_policy_enabled || skip "Auto-generated policy tests are disabled."
|
||||
|
||||
# Debugging information. Don't print the "Message:" line because it contains a truncated policy log.
|
||||
kubectl describe pod "${pod_name}" | grep -v "Message:"
|
||||
|
||||
# Clean-up
|
||||
kubectl delete -f "${correct_pod_yaml}"
|
||||
kubectl delete -f "${pvc_yaml}"
|
||||
rm -f "${incorrect_pod_yaml}"
|
||||
}
|
@ -55,6 +55,7 @@ else
|
||||
"k8s-pod-quota.bats" \
|
||||
"k8s-policy-job.bats" \
|
||||
"k8s-policy-pod.bats" \
|
||||
"k8s-policy-pvc.bats" \
|
||||
"k8s-policy-rc.bats" \
|
||||
"k8s-port-forward.bats" \
|
||||
"k8s-projected-volume.bats" \
|
||||
|
@ -0,0 +1,22 @@
|
||||
#
|
||||
# Copyright (c) 2024 Edgeless Systems GmbH
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: policy-pod-pvc
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 0
|
||||
runtimeClassName: kata
|
||||
containers:
|
||||
- name: busybox
|
||||
image: "quay.io/prometheus/busybox:latest"
|
||||
volumeDevices:
|
||||
- name: dev
|
||||
devicePath: /dev/csi0
|
||||
volumes:
|
||||
- name: dev
|
||||
persistentVolumeClaim:
|
||||
claimName: policy-dev
|
@ -0,0 +1,16 @@
|
||||
#
|
||||
# Copyright (c) 2024 Edgeless Systems GmbH
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: policy-dev
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
volumeMode: Block
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Mi
|
Loading…
Reference in New Issue
Block a user