QuantyKube

ADR-026 Without a device filter there is no MKNOD

All pages

ADR-026, from docs/DECISIONS.md in the QuantyKube repository.

Status In force

A review of the runtime against what a container can reach found that Docker's default capabilities include MKNOD, and Docker can afford it because every container also gets a device cgroup that refuses to open any node but the standard ones. This runtime has no such filter, so with MKNOD a container could make a node for the host's disk in its own /dev and open it: root in a container is root on the host here (ADR-023). MKNOD is therefore not in the default set, cannot be added back, and the kubelet's capability lists are honoured otherwise: drops always, adds only within the default set, since most capabilities beyond it lead out of a container without a user namespace. Device nodes in image layers were already skipped when unpacking (ADR-016).

The same review closed three smaller holes. A log path from the kubelet that is absolute or climbs out with .. is refused, since the runtime writes the log as root. A registry login goes to the token realm the registry names only over TLS or on the registry's own host. And image references whose repository has empty, . or .. parts are refused.

A seccomp profile of the node's own (Localhost) is refused; Unconfined, which the kubelet sends for every pod that names no profile, gets the default filter, as with the kubelet's --seccomp-default.

The device filter followed the same evening: every container cgroup gets an eBPF program of type cgroup device, as runc loads one, that lets a container make or open only the standard character devices and the pty slaves. A test interprets the program for allowed and refused devices, and in CI a container that holds MKNOD can neither make a node for a disk nor open one, while /dev/null works. MKNOD stays out of the default set all the same: two locks where one would do.

The cost: a program that makes device nodes fails here where it works under Docker, and a pod that asks for Unconfined to make a call Docker's profile refuses fails too.