Privileged Containers and Container Escapes
Containers isolate with namespaces and cgroups, not with the hardware-enforced boundary a virtual machine gets. That isolation is real but thin, and a container escape is what happens when a process crosses it to reach the host or a sibling container. Escapes come in two flavours: exploits of genuine kernel or runtime bugs, and abuse of configurations that were never isolating in the first place. The second flavour is far more common, and it is entirely on you.
The most dangerous line in any pod spec is privileged: true. A privileged container runs with all Linux capabilities, an unrestricted device list, and most kernel-enforced protections stripped away. It can access host devices under /dev, manipulate the host's cgroups and kernel modules, and mount filesystems. Reaching the host from a privileged container is not an exploit — it is intended behaviour, misapplied. Some workloads legitimately need it (certain CNI and storage plugins); the vast majority that request it do not.
The self-inflicted escapes
- Mounting the host filesystem. A
hostPathvolume of/— or just/etcor the container runtime socket — hands the container the host's files to read and rewrite. - Mounting the Docker/CRI socket.
/var/run/docker.sockinside a container lets it instruct the daemon to start a new privileged container mounting the host. It is root-on-host by API call. - Host namespaces.
hostPID,hostNetwork, andhostIPCpunch through the very isolation namespaces provide, exposing host processes and interfaces. - Dangerous capabilities.
CAP_SYS_ADMINis a near-superset of root;CAP_SYS_PTRACE,CAP_SYS_MODULE, andCAP_DAC_READ_SEARCHeach enable well-documented breakout techniques.
privileged: true, a host mount, or a mounted runtime socket removes the wall and then acts surprised when someone walks through.The exploit-driven escapes
The rarer, headline class exploits real flaws in the runtime or kernel. The 2019 runc vulnerability (CVE-2019-5736) let a malicious image overwrite the host's runc binary during container startup, executing on the host on the next run. The 2022 release_agent cgroups escape (CVE-2022-0492) allowed breakout when certain protections were absent. The 2024 'Leaky Vessels' set (CVE-2024-21626 and related) again put runc-based escapes in the spotlight. Kernel bugs like Dirty Pipe (CVE-2022-0847) are shared by every container on the host because they all share one kernel. The defence against these is unglamorous: patch the runtime and the host kernel promptly, because a shared kernel means a shared exposure.
Building real containment
- Ban
privileged, host namespaces, and host mounts at admission. The Baseline/Restricted Pod Security Standards block these by default — enforce them. - Never mount the container runtime socket into a workload. If a pod needs to manage containers, that is an architecture to rethink, not a mount to grant.
- Drop all capabilities and add back the few needed. Explicitly refuse
CAP_SYS_ADMINunless a workload cannot function without it — and then isolate that workload. - Apply seccomp and an LSM. The
RuntimeDefaultseccomp profile blocks obscure syscalls exploits rely on; AppArmor or SELinux adds mandatory access control on top. - Use user namespaces. Remapping container root to an unprivileged host UID means even 'root' in the container is nobody on the host — now stabilising as a native Kubernetes feature.
- For genuinely hostile multi-tenancy, add a sandbox. gVisor or Kata Containers interpose a user-space kernel or a lightweight VM, so a kernel exploit inside the container never reaches the real host kernel.
privileged: true
volumes:
- name: host
hostPath: { path: / } # host root, writable, from inside the container
Defence in depth here is layered on purpose: admission control stops the dangerous spec, capability dropping and seccomp shrink the exploit surface, patching closes the known bugs, and sandboxing contains the unknown ones. But every layer assumes the workload is one you govern. A privileged container running on a node nobody is watching is the escape you will read about in someone else's incident report — you can't contain what you haven't discovered.
- Skim running pods for
privileged: true, host namespaces, or ahostPathmount of/or the runtime socket — each is a boundary that was never really closed. - Spot-check whether workloads drop capabilities to
ALL, or quietly carryCAP_SYS_ADMIN.
Finding the obvious ones by eye is a start; systematically hunting every dangerous mount and capability across the fleet is what our assessment runs for you.