Operator On The Wire
← Back to Knowledge Base
OOTW / Chapter II - Local / 02. Containers / Container Escape

Remediation

Container hardening is least privilege applied to runtime boundaries.

The goal is not to make compromise impossible. The goal is to prevent one compromised container from controlling the host, runtime, cluster, or neighboring workloads.


Docker Runtime Hardening

Avoid privileged mode. There is no clean hardening switch that makes --privileged safe for normal application workloads; remove it first.

Use no-new-privileges:

docker run --security-opt no-new-privileges:true IMAGE

Drop capabilities:

docker run --cap-drop=ALL IMAGE

Add only what is required:

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE IMAGE

Run as non-root:

docker run --user 1000:1000 IMAGE

Read-only root filesystem:

docker run --read-only IMAGE

Limit resources:

docker run --memory 256m --cpus 1 --pids-limit 100 IMAGE

Mount Hardening

Avoid:

/:/host
/var/run/docker.sock:/var/run/docker.sock
/etc:/host/etc
/root:/host/root
/var/lib/docker:/var/lib/docker

Prefer:

narrow application-specific paths
read-only mounts
nosuid
nodev
noexec

Example:

docker run -v /srv/app/config:/app/config:ro IMAGE

Docker Socket

Do not mount:

/var/run/docker.sock

If containerized build or automation needs Docker access:

  • Use a dedicated build host.
  • Use rootless builders where possible.
  • Use a restricted Docker socket proxy.
  • Restrict which actions the automation can perform.
  • Separate build workloads from production workloads.

Also treat the local Docker group as privileged. A user in the docker group can usually create containers that mount the host filesystem, which makes group membership close to root-equivalent on the Docker host.


API Exposure

Check daemon listeners:

ss -lntp | grep dockerd
ps aux | grep dockerd

Do not expose unauthenticated Docker API on TCP.

If TCP is required:

  • Use mutual TLS.
  • Firewall trusted clients only.
  • Monitor requests.
  • Rotate client certificates.

Review daemon configuration:

cat /etc/docker/daemon.json 2>/dev/null
ps aux | grep dockerd

Dangerous listener patterns include:

tcp://0.0.0.0:2375
tcp://*:2375

Security Profiles

Keep these enabled:

seccomp
AppArmor
SELinux
no-new-privileges

Do not disable profiles casually:

--security-opt seccomp=unconfined
--security-opt apparmor=unconfined
--privileged

These options are strong signals for review.

Where practical, enable user namespace remapping or rootless Docker so container UID 0 does not map directly to host UID 0.


Secrets

Avoid secrets in:

environment variables
image layers
command-line arguments
world-readable mounted files
logs

Prefer:

  • Runtime secret mounts
  • Short-lived credentials
  • Narrow service accounts
  • Secret rotation
  • Separate secrets per workload

Monitoring

Watch for:

  • New privileged containers
  • Docker socket mounts
  • Host root bind mounts
  • Host PID/network mode
  • Dangerous capabilities
  • Disabled seccomp/AppArmor
  • Containers running as root unexpectedly
  • Unusual Docker API requests
  • Unexpected container images
  • Containers mounting sensitive host paths

Useful commands:

docker events
docker ps --no-trunc
docker inspect CONTAINER

Checklist

  • No Docker socket in app containers
  • No unauthenticated Docker API
  • No privileged containers unless formally approved
  • Capabilities dropped by default
  • Containers run as non-root
  • Root filesystem read-only where possible
  • Host mounts narrow and read-only where possible
  • Seccomp and AppArmor/SELinux enabled
  • Secrets not stored in images or env vars
  • Kernel and runtime patched
  • Container creation events monitored