← All cheat sheets

DOCKER SECURITY HARDENING

Plain-text reference · 6 KB. Read it, search it (Ctrl-F) or print it.

CONTAINER USER SECURITY#

# Never run as root
USER nonroot
# Or in Dockerfile
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# Run with read-only filesystem
docker run --read-only --tmpfs /tmp myimage

# Drop all capabilities, add only needed
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myimage

# No new privileges
docker run --security-opt=no-new-privileges myimage

DOCKERFILE BEST PRACTICES#

# Use minimal base images
FROM alpine:3.19
FROM gcr.io/distroless/static-debian12

# Multi-stage builds (don't ship build tools)
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server

FROM gcr.io/distroless/static
COPY --from=builder /app/server /server
CMD ["/server"]

# Don't store secrets in layers
# BAD:  COPY secrets.env /app/
# GOOD: Use Docker secrets or env at runtime

# Pin versions
FROM alpine:3.19.1@sha256:abc123...

# Use .dockerignore
echo ".git\n.env\nnode_modules\n*.md" > .dockerignore

# Scan for vulnerabilities
docker scout cves myimage
trivy image myimage
grype myimage

IMAGE SECURITY#

# Sign images
docker trust sign myregistry/myimage:latest

# Verify image signatures
docker trust inspect myregistry/myimage:latest

# Use content trust
export DOCKER_CONTENT_TRUST=1

# Scan with Trivy
trivy image --severity HIGH,CRITICAL myimage

# Scan with Grype
grype myimage --only-fixed

# Scan with Docker Scout
docker scout cves myimage

NETWORK SECURITY#

# Create isolated networks
docker network create --driver bridge isolated_net

# Don't use host network mode
# BAD:  docker run --net=host myimage
# GOOD: docker run --network=isolated_net myimage

# Disable inter-container communication
docker network create --driver bridge -o com.docker.network.bridge.enable_icc=false secure_net

# Don't expose unnecessary ports
# BAD:  docker run -p 0.0.0.0:3306:3306 mysql
# GOOD: docker run -p 127.0.0.1:3306:3306 mysql

RESOURCE LIMITS#

# Memory limits
docker run --memory="512m" --memory-swap="1g" myimage

# CPU limits
docker run --cpus="1.5" myimage
docker run --cpu-shares=512 myimage

# PID limits (prevent fork bombs)
docker run --pids-limit=100 myimage

# Restart policy
docker run --restart=on-failure:5 myimage

# Ulimits
docker run --ulimit nofile=1024:2048 myimage

SECURITY PROFILES#

# AppArmor
docker run --security-opt apparmor=docker-default myimage

# Seccomp (block dangerous syscalls)
docker run --security-opt seccomp=/path/to/seccomp-profile.json myimage

# Default seccomp profile blocks ~44 syscalls
# Create custom profile for tighter restrictions

# SELinux
docker run --security-opt label=type:container_t myimage

DOCKER DAEMON SECURITY#

# Enable TLS for remote Docker API
dockerd --tlsverify \
  --tlscacert=/certs/ca.pem \
  --tlscert=/certs/server-cert.pem \
  --tlskey=/certs/server-key.pem \
  -H=0.0.0.0:2376

# Don't expose Docker socket to containers
# BAD: docker run -v /var/run/docker.sock:/var/run/docker.sock

# Enable user namespaces
# /etc/docker/daemon.json
{
  "userns-remap": "default",
  "no-new-privileges": true,
  "live-restore": true,
  "log-driver": "json-file",
  "log-opts": {"max-size": "10m", "max-file": "3"}
}

SECRETS MANAGEMENT#

# Docker Swarm secrets
echo "db_password" | docker secret create db_pass -
docker service create --secret db_pass myimage

# BuildKit secrets (don't leak in layers)
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=mytoken \
    cat /run/secrets/mytoken

# Build with secret
docker build --secret id=mytoken,src=./token.txt .

# Use environment variables at runtime (not build time)
docker run -e DB_PASS_FILE=/run/secrets/db_pass myimage

DOCKER COMPOSE SECURITY#

# docker-compose.yml
services:
  app:
    image: myimage:latest
    read_only: true
    tmpfs:
      - /tmp
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
    networks:
      - backend
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3

CIS DOCKER BENCHMARK KEY CHECKS#

# 1. Host configuration
auditctl -w /usr/bin/docker -p wa    # Audit Docker binary
auditctl -w /etc/docker -p wa        # Audit Docker config
auditctl -w /var/lib/docker -p wa    # Audit Docker data

# 2. Check running containers
docker ps --format '{{.Names}}: User={{.Label "user"}} Privileged={{.Label "privileged"}}'

# 3. Find privileged containers
docker inspect --format='{{.HostConfig.Privileged}}' $(docker ps -q)

# 4. Find containers with host PID namespace
docker inspect --format='{{.HostConfig.PidMode}}' $(docker ps -q)

# 5. Check container capabilities
docker inspect --format='{{.HostConfig.CapAdd}}' $(docker ps -q)

CONTAINER ESCAPE PREVENTION#

# Never run privileged containers in production
# BAD: docker run --privileged myimage

# Don't mount sensitive host paths
# BAD: docker run -v /:/host myimage
# BAD: docker run -v /etc:/etc myimage

# Prevent ptrace
docker run --security-opt=no-new-privileges --cap-drop=SYS_PTRACE myimage

# Use rootless Docker
dockerd-rootless-setuptool.sh install

AUDITING COMMANDS#

# List all containers and their security settings
docker inspect $(docker ps -aq) | jq '.[].HostConfig | {Privileged, CapAdd, CapDrop, SecurityOpt, PidMode, NetworkMode}'

# Check for containers running as root
docker ps -q | xargs -I {} docker exec {} whoami 2>/dev/null

# Find images with known vulnerabilities
trivy image --severity CRITICAL $(docker images -q)

# Check Docker daemon configuration
docker system info --format '{{json .SecurityOptions}}'

Defensive reference on CyberRamen. Offensive / red-team sheets live on OffensiveRamen.com.