policy: check the linux network namespace

Peer pods have a linux namespace of type network. We want to make sure that all
container in the same pod use the same namespace. Therefore, we add the first
namespace path to the state and check all other requests against that.

This commit also adds the corresponding integration test in the policy crate
showcasing the benefit of having rust integration tests for the policy.

Signed-off-by: Leonard Cohnen <lc@edgeless.systems>
This commit is contained in:
Leonard Cohnen 2024-12-03 23:33:01 +01:00
parent 7aca7a6671
commit ec0af6fbda
4 changed files with 602 additions and 8 deletions

View File

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

View File

@ -134,4 +134,9 @@ mod tests {
async fn test_create_sandbox() {
runtests::<CreateSandboxRequest>("createsandbox").await;
}
#[tokio::test]
async fn test_create_container_network_namespace() {
runtests::<CreateContainerRequest>("createcontainer/network_namespace").await;
}
}

View File

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

View File

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