Q: Your security team conducted a penetration test and proved that default Docker containers retain 14 Linux capabilities, including `CAP_NET_RAW` (allowing ARP spoofing and packet sniffing) and `CAP_MKNOD` (allowing creation of device nodes). The CISO mandates that all production containers must drop ALL default capabilities by default, running as unprivileged users. However, your frontend Nginx proxy needs to bind to privileged port 80 and port 443 without running as root. You must configure granular capability hardening.
Implement principle of least privilege using Linux capabilities in Docker. Drop default capabilities (`--cap-drop=ALL`) and selectively restore only required capabilities like `CAP_NET_BIND_SERVICE`.
Want to master this scenario in a live sandbox? KodeKloud's Docker Certified Associate (DCA) Hands-On Lab Course covers this exact problem with hands-on terminal drills.
🛠️ Production Runbook & Step-by-Step Resolution
Audit Docker's Default Linux Capabilities
By default, Docker grants containers a subset of Linux capabilities: `CAP_CHOWN`, `CAP_DAC_OVERRIDE`, `CAP_FOWNER`, `CAP_FSETID`, `CAP_KILL`, `CAP_SETGID`, `CAP_SETUID`, `CAP_SETPCAP`, `CAP_NET_BIND_SERVICE`, `CAP_NET_RAW`, `CAP_SYS_CHROOT`, `CAP_MKNOD`, `CAP_AUDIT_WRITE`, `CAP_SETFCAP`. Many of these can be abused during container breakout attempts.
# Inspect granted capabilities of a default container
docker run --rm alpine capsh --print | grep Current
# Output: Current: = cap_chown,cap_dac_override,cap_net_raw,...
Enforce the --cap-drop=ALL Security Baseline
Always strip all capabilities first using `--cap-drop=ALL`. This establishes a zero-capability baseline, preventing unauthorized raw socket creation or ownership changes.
# Strip all capabilities
docker run --rm --cap-drop=ALL alpine capsh --print | grep Current
# Output: Current: = (empty, zero capabilities!)
Selectively Add CAP_NET_BIND_SERVICE for Unprivileged Port Binding
Linux kernels require the `CAP_NET_BIND_SERVICE` capability to bind to privileged ports below 1024 (e.g., 80, 443). Grant strictly this single capability to an unprivileged container user.
# Run unprivileged container with only CAP_NET_BIND_SERVICE
docker run -d \
--name secure-nginx \
--user 1001:1001 \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
-p 80:80 \
nginxinc/nginx-unprivileged:alpine
Declarative Capability Hardening in Docker Compose and Kubernetes
Apply the capability dropping and addition declaratively in `compose.yaml` and Kubernetes Pod `securityContext`.
# Docker Compose
services:
web:
image: nginxinc/nginx-unprivileged:alpine
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# Kubernetes Pod SecurityContext
# securityContext:
# capabilities:
# drop: ["ALL"]
# add: ["NET_BIND_SERVICE"]
- W
- e
- n
- e
- u
- t
- r
- a
- l
- i
- z
- e
- d
- c
- o
- n
- t
- a
- i
- n
- e
- r
- p
- r
- i
- v
- i
- l
- e
- g
- e
- e
- s
- c
- a
- l
- a
- t
- i
- o
- n
- r
- i
- s
- k
- s
- b
- y
- e
- n
- f
- o
- r
- c
- i
- n
- g
- `
- -
- -
- c
- a
- p
- -
- d
- r
- o
- p
- =
- A
- L
- L
- `
- a
- c
- r
- o
- s
- s
- o
- u
- r
- e
- n
- t
- i
- r
- e
- f
- l
- e
- e
- t
- .
- F
- o
- r
- w
- e
- b
- g
- a
- t
- e
- w
- a
- y
- s
- r
- e
- q
- u
- i
- r
- i
- n
- g
- b
- i
- n
- d
- i
- n
- g
- t
- o
- p
- o
- r
- t
- s
- 8
- 0
- a
- n
- d
- 4
- 4
- 3
- ,
- w
- e
- s
- e
- l
- e
- c
- t
- i
- v
- e
- l
- y
- a
- d
- d
- `
- C
- A
- P
- _
- N
- E
- T
- _
- B
- I
- N
- D
- _
- S
- E
- R
- V
- I
- C
- E
- `
- t
- o
- u
- n
- p
- r
- i
- v
- i
- l
- e
- g
- e
- d
- u
- s
- e
- r
- a
- c
- c
- o
- u
- n
- t
- s
- ,
- e
- n
- s
- u
- r
- i
- n
- g
- o
- u
- r
- w
- o
- r
- k
- l
- o
- a
- d
- s
- h
- a
- v
- e
- z
- e
- r
- o
- c
- a
- p
- a
- b
- i
- l
- i
- t
- y
- t
- o
- f
- o
- r
- g
- e
- r
- a
- w
- p
- a
- c
- k
- e
- t
- s
- ,
- a
- l
- t
- e
- r
- f
- i
- l
- e
- o
- w
- n
- e
- r
- s
- h
- i
- p
- s
- ,
- o
- r
- t
- o
- u
- c
- h
- k
- e
- r
- n
- e
- l
- d
- e
- v
- i
- c
- e
- s
- .