diff --git a/src/tools/genpolicy/rules.rego b/src/tools/genpolicy/rules.rego index 6caba44357..1acfca19c8 100644 --- a/src/tools/genpolicy/rules.rego +++ b/src/tools/genpolicy/rules.rego @@ -86,7 +86,7 @@ CreateContainerRequest:= {"ops": ops, "allowed": true} { i_namespace := i_oci.Annotations[S_NAMESPACE_KEY] print ("CreateContainerRequest: p_namespace =", p_namespace, "i_namespace =", i_namespace) add_namespace_to_state := allow_namespace(p_namespace, i_namespace) - ops := concat_op_if_not_null(ops_builder1, add_namespace_to_state) + ops_builder2 := concat_op_if_not_null(ops_builder1, add_namespace_to_state) print("CreateContainerRequest: p Version =", p_oci.Version, "i Version =", i_oci.Version) p_oci.Version == i_oci.Version @@ -102,7 +102,10 @@ CreateContainerRequest:= {"ops": ops, "allowed": true} { p_devices := p_container.devices allow_devices(p_devices, i_devices) - allow_linux(p_oci, i_oci) + ret := allow_linux(ops_builder2, p_oci, i_oci) + ret.allowed + + ops := ret.ops print("CreateContainerRequest: true") } @@ -150,11 +153,12 @@ allow_namespace(p_namespace, i_namespace) = add_namespace { add_namespace := state_allows("namespace", i_namespace) } -# value hasn't been seen before, save it to state +# key hasn't been seen before, save key, value pair to state state_allows(key, value) = action { state := get_state() + print("state_allows 1: state[key] =", state[key], "value =", value) not state[key] - print("state_allows: saving to state key =", key, "value =", value) + print("state_allows 1: saving to state key =", key, "value =", value) path := get_state_path(key) action := { "op": "add", @@ -165,9 +169,11 @@ state_allows(key, value) = action { # value matches what's in state, allow it state_allows(key, value) = action { + print("state_allows 2: start") state := get_state() + print("state_allows 2: state[key] =", state[key], "value =", value) value == state[key] - print("state_allows: found key =", key, "value =", value, " in state") + print("state_allows 2: found key =", key, "value =", value, " in state") action := null } @@ -181,7 +187,7 @@ get_state_path(key) = path { path := concat("/", ["/pstate", key]) } -# Helper functions to conditionally concatenate if op is not null +# Helper functions to conditionally concatenate op is not null concat_op_if_not_null(ops, op) = result { op == null result := ops @@ -439,22 +445,74 @@ allow_devices(p_devices, i_devices) { print("allow_devices: true") } -allow_linux(p_oci, i_oci) { +allow_linux(state_ops, p_oci, i_oci) := {"ops": ops, "allowed": true} { p_namespaces := p_oci.Linux.Namespaces print("allow_linux: p namespaces =", p_namespaces) i_namespaces := i_oci.Linux.Namespaces print("allow_linux: i namespaces =", i_namespaces) - p_namespaces == i_namespaces + i_namespace_without_network := [obj | obj := i_namespaces[_]; obj.Type != "network"] + + print("allow_linux: i_namespace_without_network =", i_namespace_without_network) + + p_namespaces == i_namespace_without_network 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) + ret := allow_network_namespace_start(state_ops, p_oci, i_oci) + ret.allowed + + ops := ret.ops print("allow_linux: true") } +# Retrieve the "network" namespace from the input data and pass it on for the +# network namespace policy checks. +allow_network_namespace_start(state_ops, p_oci, i_oci) := {"ops": ops, "allowed": true} { + print("allow_network_namespace start: start") + + p_namespaces := p_oci.Linux.Namespaces + print("allow_network_namespace start: p namespaces =", p_namespaces) + + i_namespaces := i_oci.Linux.Namespaces + print("allow_network_namespace start: i namespaces =", i_namespaces) + + # Return path of the "network" namespace + network_ns := [obj | obj := i_namespaces[_]; obj.Type == "network"] + + print("allow_network_namespace start: network_ns =", network_ns) + + ret := allow_network_namespace(state_ops, network_ns) + ret.allowed + + ops := ret.ops +} + +# This rule is when there's no network namespace in the input data. +allow_network_namespace(state_ops, network_ns) := {"ops": ops, "allowed": true} { + count(network_ns) == 0 + + network_ns_path = "" + + add_network_namespace_to_state := state_allows("network_namespace", network_ns_path) + ops := concat_op_if_not_null(state_ops, add_network_namespace_to_state) + + print("allow_network_namespace 1: true") +} + +# This rule is when there's exactly one network namespace in the input data. +allow_network_namespace(state_ops, network_ns) := {"ops": ops, "allowed": true} { + count(network_ns) == 1 + + add_network_namespace_to_state := state_allows("network_namespace", network_ns[0].Path) + ops := concat_op_if_not_null(state_ops, add_network_namespace_to_state) + + print("allow_network_namespace 2: true") +} + allow_masked_paths(p_oci, i_oci) { p_paths := p_oci.Linux.MaskedPaths print("allow_masked_paths 1: p_paths =", p_paths) diff --git a/src/tools/genpolicy/tests/main.rs b/src/tools/genpolicy/tests/main.rs index 40247c287a..1c4119bdf4 100644 --- a/src/tools/genpolicy/tests/main.rs +++ b/src/tools/genpolicy/tests/main.rs @@ -134,4 +134,9 @@ mod tests { async fn test_create_sandbox() { runtests::("createsandbox").await; } + + #[tokio::test] + async fn test_create_container_network_namespace() { + runtests::("createcontainer/network_namespace").await; + } } diff --git a/src/tools/genpolicy/tests/testdata/createcontainer/network_namespace/pod.yaml b/src/tools/genpolicy/tests/testdata/createcontainer/network_namespace/pod.yaml new file mode 100644 index 0000000000..7ac6554ed9 --- /dev/null +++ b/src/tools/genpolicy/tests/testdata/createcontainer/network_namespace/pod.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Pod +metadata: + name: dummy +spec: + runtimeClassName: kata-cc-isolation + containers: + - name: dummy + image: registry.k8s.io/pause:3.6@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db diff --git a/src/tools/genpolicy/tests/testdata/createcontainer/network_namespace/testcases.json b/src/tools/genpolicy/tests/testdata/createcontainer/network_namespace/testcases.json new file mode 100644 index 0000000000..5de73c5fcc --- /dev/null +++ b/src/tools/genpolicy/tests/testdata/createcontainer/network_namespace/testcases.json @@ -0,0 +1,522 @@ +[ + { + "description": "one network namespace", + "allowed": true, + "request": { + "OCI": { + "Version": "1.1.0", + "Annotations": { + "io.kubernetes.cri.sandbox-name": "dummy", + "io.kubernetes.cri.sandbox-namespace": "default", + "io.kubernetes.cri.container-type": "sandbox", + "io.katacontainers.pkg.oci.container_type": "pod_sandbox", + "nerdctl/network-namespace": "/var/run/netns/cni-00000000-0000-0000-0000-000000000000", + "io.kubernetes.cri.sandbox-log-directory": "/var/log/pods/default_dummy_00000000-0000-0000-0000-000000000000", + "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/bundle-id", + "io.kubernetes.cri.sandbox-id": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "Linux": { + "GIDMappings": [], + "MountLabel": "", + "Resources": { + "Devices": [] + }, + "RootfsPropagation": "", + "Namespaces": [ + { + "Path": "", + "Type": "ipc" + }, + { + "Path": "", + "Type": "uts" + }, + { + "Path": "", + "Type": "mount" + }, + { + "Path": "/run/netns/podns", + "Type": "network" + } + ], + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + }, + "Process": { + "SelinuxLabel": "", + "User": { + "Username": "", + "UID": 65535 + }, + "Args": [ + "/pause" + ], + "Cwd": "/", + "NoNewPrivileges": true, + "Capabilities": { + "Ambient": [], + "Bounding": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Effective": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Permitted": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ] + } + }, + "Root": { + "Readonly": true, + "Path": "/run/kata-containers/shared/containers/bundle-id/rootfs" + } + } + } + }, + { + "description": "same network namespace", + "allowed": true, + "request": { + "OCI": { + "Version": "1.1.0", + "Annotations": { + "io.kubernetes.cri.sandbox-name": "dummy", + "io.kubernetes.cri.sandbox-namespace": "default", + "io.kubernetes.cri.container-type": "sandbox", + "io.katacontainers.pkg.oci.container_type": "pod_sandbox", + "nerdctl/network-namespace": "/var/run/netns/cni-00000000-0000-0000-0000-000000000000", + "io.kubernetes.cri.sandbox-log-directory": "/var/log/pods/default_dummy_00000000-0000-0000-0000-000000000000", + "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/bundle-id", + "io.kubernetes.cri.sandbox-id": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "Linux": { + "GIDMappings": [], + "MountLabel": "", + "Resources": { + "Devices": [] + }, + "RootfsPropagation": "", + "Namespaces": [ + { + "Path": "", + "Type": "ipc" + }, + { + "Path": "", + "Type": "uts" + }, + { + "Path": "", + "Type": "mount" + }, + { + "Path": "/run/netns/podns", + "Type": "network" + } + ], + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + }, + "Process": { + "SelinuxLabel": "", + "User": { + "Username": "", + "UID": 65535 + }, + "Args": [ + "/pause" + ], + "Cwd": "/", + "NoNewPrivileges": true, + "Capabilities": { + "Ambient": [], + "Bounding": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Effective": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Permitted": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ] + } + }, + "Root": { + "Readonly": true, + "Path": "/run/kata-containers/shared/containers/bundle-id/rootfs" + } + } + } + }, + { + "description": "no network namespace", + "allowed": false, + "request": { + "OCI": { + "Version": "1.1.0", + "Annotations": { + "io.kubernetes.cri.sandbox-name": "dummy", + "io.kubernetes.cri.sandbox-namespace": "default", + "io.kubernetes.cri.container-type": "sandbox", + "io.katacontainers.pkg.oci.container_type": "pod_sandbox", + "nerdctl/network-namespace": "/var/run/netns/cni-00000000-0000-0000-0000-000000000000", + "io.kubernetes.cri.sandbox-log-directory": "/var/log/pods/default_dummy_00000000-0000-0000-0000-000000000000", + "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/bundle-id", + "io.kubernetes.cri.sandbox-id": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "Linux": { + "GIDMappings": [], + "MountLabel": "", + "Resources": { + "Devices": [] + }, + "RootfsPropagation": "", + "Namespaces": [ + { + "Path": "", + "Type": "ipc" + }, + { + "Path": "", + "Type": "uts" + }, + { + "Path": "", + "Type": "mount" + } + ], + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + }, + "Process": { + "SelinuxLabel": "", + "User": { + "Username": "", + "UID": 65535 + }, + "Args": [ + "/pause" + ], + "Cwd": "/", + "NoNewPrivileges": true, + "Capabilities": { + "Ambient": [], + "Bounding": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Effective": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Permitted": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ] + } + }, + "Root": { + "Readonly": true, + "Path": "/run/kata-containers/shared/containers/bundle-id/rootfs" + } + } + } + }, + { + "description": "different network namespace", + "allowed": false, + "request": { + "OCI": { + "Version": "1.1.0", + "Annotations": { + "io.kubernetes.cri.sandbox-name": "dummy", + "io.kubernetes.cri.sandbox-namespace": "default", + "io.kubernetes.cri.container-type": "sandbox", + "io.katacontainers.pkg.oci.container_type": "pod_sandbox", + "nerdctl/network-namespace": "/var/run/netns/cni-00000000-0000-0000-0000-000000000000", + "io.kubernetes.cri.sandbox-log-directory": "/var/log/pods/default_dummy_00000000-0000-0000-0000-000000000000", + "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/bundle-id", + "io.kubernetes.cri.sandbox-id": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "Linux": { + "GIDMappings": [], + "MountLabel": "", + "Resources": { + "Devices": [] + }, + "RootfsPropagation": "", + "Namespaces": [ + { + "Path": "", + "Type": "ipc" + }, + { + "Path": "", + "Type": "uts" + }, + { + "Path": "", + "Type": "mount" + }, + { + "Path": "/run/netns/podns-diff", + "Type": "network" + } + ], + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + }, + "Process": { + "SelinuxLabel": "", + "User": { + "Username": "", + "UID": 65535 + }, + "Args": [ + "/pause" + ], + "Cwd": "/", + "NoNewPrivileges": true, + "Capabilities": { + "Ambient": [], + "Bounding": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Effective": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "Permitted": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ] + } + }, + "Root": { + "Readonly": true, + "Path": "/run/kata-containers/shared/containers/bundle-id/rootfs" + } + } + } + } +] \ No newline at end of file