CoursesAdvanced container securityHardening the container

cap-drop ALL & no-new-privileges

Least privilege, actually enforced.

Advanced12 min · lesson 12 of 25

Even a non-root container starts with a default set of ~14 capabilities, most of which a typical app never uses — and any of them is a rung an attacker might climb. Least privilege here is mechanical: drop ALL capabilities, then add back only the specific ones a proven need requires (often none, sometimes just NET_BIND_SERVICE). Pair it with no-new-privileges so the process can never gain capabilities it did not start with.

terminal
$ docker run -d \
--cap-drop ALL \ # start from zero capabilities
--cap-add NET_BIND_SERVICE \ # add back ONLY if it binds a port < 1024
--security-opt no-new-privileges \ # block setuid/privilege escalation
--user 10001:10001 \
myapp:1.0

no-new-privileges closes the setuid door

no-new-privileges sets a kernel flag that prevents a process (and its children) from ever gaining more privileges than it has — so a setuid-root binary inside the container cannot elevate, and known privilege-escalation tricks that rely on setuid are dead on arrival. It costs nothing and shuts a whole class of escalation, which is why it belongs on essentially every container alongside the capability drop.

terminal
# with no-new-privileges, a setuid-root helper cannot elevate
$ docker run --rm --security-opt no-new-privileges alpine \
sh -c 'ls -l /bin/su; su -c id 2>&1 | tail -1'
su: must be run from a terminal # (and it cannot gain the setuid privilege)

Find the real minimum

Do not guess which capabilities an app needs — drop ALL, run it, and add back only what actually fails. Most services need none; the common exceptions are NET_BIND_SERVICE (bind a low port) and, rarely, CHOWN/SETUID/SETGID during an entrypoint that drops privileges. If an app claims it needs SYS_ADMIN or NET_ADMIN, treat that as a design smell to investigate, not a flag to add.

cap-drop and non-root are different axes — do both
A common half-measure is dropping capabilities but still running as UID 0, or running non-root but keeping the default caps. Each alone leaves a path: root-with-no-caps can still do damage via file ownership, non-root-with-SYS_ADMIN can still escape. The baseline is all three together — non-root, cap-drop ALL, no-new-privileges.