Docker Security Best Practices
Complete guide to Docker security including image scanning, non-root users, capabilities, secrets, content trust, and runtime security.
# Docker Security Best Practices
Introduction
Docker security is critical for running containerized applications in production. Containers share the host kernel, so misconfigurations can lead to container escapes and host compromise. This tutorial covers image scanning, non-root users, Linux capabilities, secrets management, content trust, and runtime security.
Learning Objectives
By the end of this tutorial, you will be able to:
- Scan Docker images for vulnerabilities
- Run containers as non-root users
- Manage Linux capabilities
- Use Docker Secrets for sensitive data
- Enable Docker Content Trust
- Apply runtime security measures
- Secure Docker daemon and networks
Technical Background
Docker Security Model
Host Kernel (shared by all containers)
|
+-- Container 1 (isolated via namespaces, cgroups)
+-- Container 2 (isolated via namespaces, cgroups)
|
Docker Daemon (root by default)Security Layers
| Layer | Controls | |-------|----------| | Image | Vulnerability scanning, signed images | | Build | Secure Dockerfile, no secrets in layers | | Runtime | Non-root, capabilities, seccomp, AppArmor | | Daemon | TLS, authentication, rootless mode | | Network | Network segmentation, firewall rules |
Prerequisites
- Docker installed and running
- Understanding of Linux security concepts
Hardware and Software Requirements
| Component | Requirement | |-----------|-------------| | Docker | v24+ | | Trivy or Docker Scout | For image scanning |
Step-by-Step Implementation
Phase 1: Image Scanning
#### Install and Use Trivy
# Install Trivy
sudo apt-get install -y wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -cs) main | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install -y trivy
# Scan image
trivy image myapp:latest
# Scan with severity filter
trivy image --severity HIGH,CRITICAL myapp:latest
# Exit code on vulnerabilities
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest#### Use Docker Scout
# Enable Docker Scout
docker scout cves myapp:latest
docker scout quickview myapp:latestPhase 2: Run as Non-Root
#### Dockerfile with Non-Root User
FROM node:20-alpine
# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
# Switch to non-root user
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]#### Run Container as Non-Root
# Run as specific user
docker run -u 1000:1000 myapp:latest
# Run with security options
docker run --read-only --tmpfs /tmp --cap-drop=ALL myapp:latestPhase 3: Linux Capabilities
# Drop all capabilities, add only needed
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp:latest
# View container capabilities
docker run --rm myapp:latest capsh --print
# Add specific capability
docker run --cap-add=SYS_TIME myapp:latestPhase 4: Docker Secrets
# Create secret
echo "supersecretpassword" | docker secret create db_password -
# List secrets
docker secret ls
# Inspect secret
docker secret inspect db_password
# Use secret in service (Docker Swarm)
docker service create \
--name db \
--secret db_password \
-e MYSQL_PASSWORD_FILE=/run/secrets/db_password \
mysql:8.0#### Secrets in docker-compose
# docker-compose.yml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txtPhase 5: Docker Content Trust
# Enable content trust
export DOCKER_CONTENT_TRUST=1
# Push signed image
docker push myregistry.com/myapp:latest
# Pull verified image
docker pull myregistry.com/myapp:latest
# Disable content trust
export DOCKER_CONTENT_TRUST=0Phase 6: Runtime Security
# Run with seccomp profile
docker run --security-opt seccomp=profile.json myapp:latest
# Run with AppArmor profile
docker run --security-opt apparmor=docker-restricted myapp:latest
# Run with no new privileges
docker run --security-opt no-new-privileges:true myapp:latest
# Limit memory and CPU
docker run --memory="256m" --cpus="0.5" myapp:latest
# Prevent privilege escalation
docker run --security-opt no-new-privileges --cap-drop=ALL myapp:latestPhase 7: Docker Daemon Security
#### Configure TLS for Docker Daemon
# Generate CA key and certificate
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
# Generate server key and certificate
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=docker-host" -new -key server-key.pem -out server.csr
echo subjectAltName = DNS:docker-host,IP:192.168.1.10 > extfile.cnf
echo extendedKeyUsage = serverAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -extfile extfile.cnf
# Configure Docker daemon with TLS
sudo dockerd \
--tlsverify \
--tlscacert=ca.pem \
--tlscert=server-cert.pem \
--tlskey=server-key.pem \
-H=0.0.0.0:2376#### Rootless Docker
# Install rootless Docker
curl -fsSL https://get.docker.com/rootless | sh
# Set up environment
export PATH=/home/user/bin:$PATH
export DOCKER_HOST=unix:///home/user/docker.sock
# Start rootless Docker
dockerd-rootless.sh &Phase 8: Network Security
# Create isolated network
docker network create --driver bridge --opt com.docker.network.bridge.enable_ip_masquerade=true secure-network
# Run container on isolated network
docker run -d --network secure-network --name web myapp:latest
# Inspect network
docker network inspect secure-networkExpected Output
- Images scanned for vulnerabilities
- Containers running as non-root
- Linux capabilities managed
- Secrets configured
- Content trust enabled
- Runtime security applied
Verification Procedures
| Check | Command | Expected Result | |-------|---------|-----------------| | Image scan | `trivy image myapp:latest` | No HIGH/CRITICAL vulnerabilities | | User check | `docker exec myapp whoami` | Non-root user | | Capabilities | `docker exec myapp capsh --print` | Limited capabilities | | Secrets | `docker secret ls` | Secrets listed | | Content trust | `docker pull --disable-content-trust=false myapp:latest` | Signed image verified |
Troubleshooting Scenarios
| Issue | Diagnosis | Resolution | |-------|-----------|------------| | Permission denied | Non-root user can't write to volume | Fix volume ownership; use user flag | | Port binding fails | Missing NET_BIND_SERVICE capability | Add --cap-add=NET_BIND_SERVICE | | Secret not found | Secret not created or wrong name | Create secret; verify name | | Content trust fails | Unsigned image | Sign image or disable trust for specific registry |
Best Practices
- **Scan all images:** Integrate scanning into CI/CD pipeline
- **Run as non-root:** Never run containers as root in production
- **Use minimal base images:** Alpine, distroless, or scratch
- **Drop all capabilities:** Add only what's needed
- **Use secrets:** Never hardcode passwords or API keys
- **Enable content trust:** Verify image integrity
- **Limit resources:** Set memory and CPU limits
- **Use rootless Docker:** Run daemon as non-root
Security Considerations
- **Kernel sharing:** Containers share host kernel; kernel exploits affect all containers
- **Daemon access:** Docker daemon runs as root; secure daemon socket
- **Image supply chain:** Verify base images and dependencies
- **Registry security:** Use private registries with authentication
- **Network isolation:** Segment container networks
- **Audit logging:** Log Docker daemon and container events
Conclusion
Docker security requires defense in depth across image, build, runtime, and daemon layers. By scanning images, running as non-root, managing capabilities, using secrets, enabling content trust, and applying runtime security, you can significantly reduce the risk of container escapes and host compromise.
References
- Docker Security: https://docs.docker.com/engine/security/
- Docker Bench Security: https://github.com/docker/docker-bench-security
- Trivy: https://github.com/aquasecurity/trivy
- Docker Content Trust: https://docs.docker.com/engine/security/trust/
- Rootless Docker: https://docs.docker.com/engine/security/rootless/