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.