| name | docker-security-hardening |
| description | Use this skill whenever you need to secure Docker containers, audit Docker configurations, scan for vulnerabilities, or harden Docker deployments. Trigger this for any Docker security questions, container hardening, vulnerability scanning, reviewing Dockerfiles for security issues, or when you need to understand Docker security features like namespaces, capabilities, seccomp, or AppArmor. |
Docker Security Hardening
A comprehensive guide to securing Docker containers and deployments.
Quick Start
Immediate Security Checklist
When deploying any container, apply these security measures:
docker run \
--read-only \
--security-opt=no-new-privileges:true \
--cap-drop=all \
--cap-add=NET_BIND_SERVICE \
--user 1000:1000 \
--memory=512m \
--cpus=1 \
--pids-limit=100 \
--name secure-container \
your-image
Security Features Reference
| Feature | Purpose | Default | Recommended |
|---|
--read-only | Prevents filesystem writes | Disabled | Enable |
--security-opt=no-new-privileges:true | Blocks privilege escalation | Disabled | Enable |
--cap-drop=all | Drops all Linux capabilities | Partial drop | Drop all, add only needed |
--user | Run as non-root | Root | Non-root user |
--memory | Memory limit | Unlimited | Set appropriate limit |
--cpus | CPU limit | Unlimited | Set appropriate limit |
--pids-limit | Process limit | Unlimited | Set to prevent fork bombs |
Container Security Features
Namespaces
Docker uses Linux namespaces for isolation:
- PID namespace: Process isolation
- Mount namespace: Filesystem isolation
- Network namespace: Network stack isolation
- IPC namespace: Inter-process communication isolation
- UTS namespace: Hostname isolation
Security implication: Namespaces provide isolation but are not foolproof. Combined with other mechanisms, they form a defense-in-depth strategy.
Capabilities
Capabilities provide fine-grained privilege control. When a container starts, Docker drops sensitive capabilities by default.
Remaining capabilities by default:
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
Best practice: Drop all capabilities and add only what's needed:
--cap-drop=all --cap-add=NET_BIND_SERVICE --cap-add=CHOWN
Seccomp
Seccomp (Secure Computing Mode) restricts syscalls available to containers.
- Default: Docker uses a default seccomp profile
- Location:
profiles/seccomp/default.json
- Disable (not recommended):
--security-opt seccomp=unconfined
- Custom profile:
--security-opt seccomp=/path/to/profile.json
AppArmor
AppArmor confines containers to limited resources with per-program profiles.
- Default: Docker has a built-in AppArmor template
- Disable (not recommended):
--security-opt apparmor=unconfined
- Custom profile:
--security-opt apparmor=custom-profile-name
SELinux
SELinux provides mandatory access control through labeling:
- Container processes: Labeled as
container_t
- Container files: Labeled as
container_file_t
- Policy:
container_t can only interact with container_file_t
- Disable (not recommended):
--security-opt label:disable
Vulnerability Scanning
Built-in Docker Scan
docker scan <image-name>
Trivy (Recommended)
trivy image <image-name>:<tag>
trivy image --format json --output results.json <image-name>:<tag>
trivy image --severity HIGH,CRITICAL <image-name>:<tag>
Snyk
snyk container test <image> --json-file-output=results.json --severity-threshold=high
Clair Scanner
clair-scanner -w config.yaml --ip YOUR_LOCAL_IP <image-name>:<tag>
Secret Management
❌ Don't Do This
# Bad: Secrets in environment variables
ENV API_KEY=secret123
# Bad: Secrets in image layers
RUN echo "password123" > /app/config.txt
✅ Do This Instead
Option 1: Docker Secrets (Swarm)
version: "3.7"
services:
my_service:
image: myapp:latest
secrets:
- my_secret
secrets:
my_secret:
file: ./my_secret_file.txt
Option 2: BuildKit Build-time Secrets
export DOCKER_BUILDKIT=1
docker build --secret id=my_key,src=./secret.txt .
Dockerfile with BuildKit secrets:
FROM node:18
RUN --mount=type=secret,id=my_key \
cp /run/secrets/my_key /app/config/key.txt
Option 3: Runtime Volume Mount
docker run -v /path/to/secrets:/run/secrets:ro myapp
Dockerfile Security Best Practices
Secure Dockerfile Template
# Use specific version, not 'latest'
FROM node:18-alpine AS builder
# Don't run as root
USER node
# Install only production dependencies
COPY package*.json ./
RUN npm ci --only=production
# Copy application code
COPY --chown=node:node . .
# Use COPY instead of ADD (ADD can fetch URLs and extract archives)
# COPY is safer and more predictable
# Multi-stage builds reduce attack surface
FROM node:18-alpine
WORKDIR /app
COPY --from=builder --chown=node:node /app .
# Non-root user
USER node
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
EXPOSE 3000
CMD ["node", "server.js"]
Security Anti-Patterns to Avoid
| Anti-Pattern | Risk | Fix |
|---|
FROM image:latest | Unpredictable builds | Pin specific version |
USER root | Full container compromise | Use non-root user |
ADD http://... | Supply chain attack | Use COPY |
ENV SECRET=... | Secrets in image | Use secrets management |
--privileged | Host escape | Remove flag |
| No resource limits | DoS attacks | Set memory/CPU limits |
| No health check | Zombie containers | Add HEALTHCHECK |
Hardening Checklist
Pre-Deployment
Runtime
Infrastructure
Common Security Commands
Check Container Security
docker inspect <container> | grep -A 20 "EffectiveCaps"
docker exec <container> id
docker inspect <container> | grep -A 10 "Mounts"
docker inspect <container> | grep -A 10 "SecurityOpt"
Run Security Audit
git clone https://github.com/docker/docker-bench-security
cd docker-bench-security
./docker-bench-security.sh
Image Signing
export DOCKER_CONTENT_TRUST=1
docker pull <image>
tar -zcvf private_keys_backup.tar.gz ~/.docker/trust/private
Security Options Reference
--security-opt Options
--security-opt=no-new-privileges:true
--security-opt seccomp=unconfined
--security-opt seccomp=/path/to/profile.json
--security-opt apparmor=unconfined
--security-opt apparmor=custom-profile
--security-opt label:disable
--security-opt label=level:s0:c100,c200
Capability Management
--cap-drop=all
--cap-add=NET_BIND_SERVICE
--cap-add=CHOWN
--cap-add=SETUID
--cap-drop=NET_RAW
--cap-drop=SYS_ADMIN
Alternative Runtimes
gVisor
Provides stronger isolation between application and host kernel:
docker run --runtime=runsc <image>
Kata Containers
Uses lightweight VMs for stronger isolation:
docker run --runtime=kata <image>
Troubleshooting
Container Won't Start
docker inspect <container> | grep -i cap
docker logs <container>
dmesg | grep -i apparmor
dmesg | grep -i avc
Performance Issues
docker stats <container>
cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes
References