The big three: privileged, docker.sock, hostPath
The flags and mounts that hand over the host.
Three misconfigurations account for most real container escapes, and all three are things you grant, not bugs you inherit: the privileged flag, a mounted Docker socket, and a host-path mount. Knowing precisely why each hands over the host is what lets you refuse them confidently and spot them in a manifest review.
1. --privileged
--privileged is not one permission; it disables almost all of them at once — it restores every capability, drops the seccomp and AppArmor profiles, and exposes all host devices under /dev. From a privileged container an attacker can mount host block devices, access host memory, and load kernel modules; it is effectively a root shell on the host with extra steps. Almost nothing legitimately needs it — the rare real cases (some CI, some device workloads) need one capability or one device mount instead.
# a privileged container can see and mount the host’s root block device — escape.$ docker run --rm --privileged alpine sh -c 'ls /dev/sda* ; echo "^ host disks visible"'# the fix is to never grant it. instead of --privileged, grant the ONE thing:$ docker run --rm --cap-add NET_ADMIN --device /dev/net/tun myvpn:1.0 # scoped, not total
2. A mounted docker.sock
Bind-mounting /var/run/docker.sock into a container (common in CI and “Docker-in-Docker” shortcuts) hands that container the full Docker API — which means it can start a second, privileged container that mounts the host root and chroots in. The container did not escape the kernel; it just asked the daemon to give it the host. The defense is to never mount the socket into untrusted containers, use a socket-proxy that allow-lists only safe API calls if a container genuinely needs limited access, and prefer rootless or daemonless builders in CI.
# inside a container with the socket mounted, this is a host takeover in one line:# docker run -v /:/host -it alpine chroot /host sh# defenses:# - do NOT mount /var/run/docker.sock into app/CI containers# - if unavoidable, front it with tecnativa/docker-socket-proxy (allowlist API paths)# - use rootless DinD or Kaniko/BuildKit so no socket is needed at all
3. Host-path mounts
Mounting sensitive host paths into a container leaks the host to it: -v /:/host is total, but subtler ones are just as bad — the host /etc (credentials, cron), /root/.ssh (keys), /proc and /sys (kernel interfaces), or a directory that lets the container write a cron job or a systemd unit the host will execute. The defense is to mount only the narrow paths a workload needs, read-only wherever possible, and never the host root, /etc, /proc, or /sys into an untrusted container.