The big three: privileged, docker.sock, hostPath

The flags and mounts that hand over the host.

Advanced16 min · lesson 23 of 25

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.

terminal
# 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.

terminal
# 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.

These three are almost always a "no"
In a review, --privileged, a docker.sock mount, and a broad hostPath are the first things to flag, and the correct default answer is no. When someone insists one is required, the fix is nearly always a scoped substitute: a single --cap-add, a single --device, a socket proxy, or a narrow read-only mount. Grant the specific thing, never the whole host.