How container escapes happen — and how to prevent them
Privileged flags, host mounts, and leaked sockets are the usual doors. Close them before an attacker finds them.
Containers are not virtual machines. They are ordinary processes on a shared kernel, separated by namespaces and cgroups — and every misconfiguration is a hole in that separation. Nearly every real-world container escape in production traces back to the same few doors: a privileged pod, a hostPath mount of / or /var/run/docker.sock, excessive Linux capabilities like CAP_SYS_ADMIN, or a workload running as root with a writable host mount. Close those doors and you have eliminated most of what attackers actually use — not theoretical CVEs, but the config someone copied from a Stack Overflow answer.
Defense is layered: admission policy blocks dangerous pod specs before they schedule, Pod Security Standards enforce baseline constraints at the namespace level, and runtime tools like Falco catch syscall patterns that policy missed. The Runtime security track walks through escape mechanics and the detection rules that prove your hardening worked.
privileged, hostPath, and missing seccomp are the usual doors. Close them with PSS.
One privileged DaemonSet with a host mount undoes every other control. Audit cluster-wide, not namespace by namespace.
What an escape looks like
With --privileged, the container sees nearly all host devices and capabilities. Mount the host root filesystem and chroot into it — you are now root on the node, with access to every other pod's data and the kubelet credentials on disk. This is not a sophisticated exploit; it is three flags and one command.
# --privileged + host mount = trivial host takeoverdocker run --rm --privileged -v /:/host alpine \chroot /host sh -c "id && cat /etc/shadow | head -1"# you are now root on the node filesystem
The usual doors — and how to shut them
A hardened pod spec
Apply the same constraints in Docker and Kubernetes. Read-only root filesystem where possible, all capabilities dropped, no privilege escalation, seccomp profile set, and a non-zero UID baked into the image — not just declared in the manifest while the image still runs as root.
securityContext:runAsNonRoot: truerunAsUser: 10001seccompProfile:type: RuntimeDefaultcontainers:- name: appsecurityContext:allowPrivilegeEscalation: falsereadOnlyRootFilesystem: truecapabilities:drop: ["ALL"]
Detect escapes you did not prevent
Policy drift happens. Falco rules on mount to sensitive paths, unexpected setns, or execution from /proc/self/exe after a breakout attempt catch what admission missed. Route alerts to the same channel as GuardDuty findings — an escape attempt on a node is a paging event, not a dashboard tile. Re-run your privileged-pod inventory after every platform upgrade; DaemonSets and monitoring agents are how escaped configs creep back in.
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | .metadata.namespace + "/" + .metadata.name'kube-system/node-agent privileged=truekubectl get pods -A -o json | jq -r '.items[] | select(.spec.volumes[]?.hostPath) | .metadata.namespace + "/" + .metadata.name'every hostPath mount deserves a written justificationWhere this goes next
Blocking escapes at admission is necessary; detecting them at runtime is how you know the blocks held. Next: seccomp and AppArmor profiles tuned to your workloads, gVisor or Kata for high-risk multi-tenant isolation, and regular red-team exercises against your cluster baseline. The Runtime security path covers escape techniques, Falco tuning, and incident response on compromised nodes.
Go deeper in a courseRuntime securityContainer escapes, seccomp, Falco detection, and runtime incident response.View course