Containers have revolutionized application deployment, but they introduce unique security challenges. This guide covers essential Docker security practices for production environments.
Image Security
Use Minimal Base Images
1
2
3
4
5
6
7
8
| # ❌ Avoid
FROM ubuntu:latest
# ✅ Better
FROM alpine:3.18
# ✅ Best - distroless
FROM gcr.io/distroless/static-debian11
|
Scan for Vulnerabilities
1
2
3
4
5
6
7
8
| # Trivy
trivy image myapp:latest
# Docker Scout
docker scout cves myapp:latest
# Snyk
snyk container test myapp:latest
|
Sign and Verify Images
1
2
3
4
5
6
7
| # Docker Content Trust
export DOCKER_CONTENT_TRUST=1
docker push myregistry.com/myapp:v1.0
# Cosign
cosign sign --key cosign.key myapp:latest
cosign verify --key cosign.pub myapp:latest
|
Dockerfile Security
Multi-Stage Builds
1
2
3
4
5
6
7
8
9
10
11
| # Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o app
# Runtime stage
FROM gcr.io/distroless/base
COPY --from=builder /app/app /
USER nonroot:nonroot
CMD ["/app"]
|
Run as Non-Root
1
2
3
4
5
6
| FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
COPY --chown=nodejs:nodejs . .
CMD ["node", "server.js"]
|
Minimize Layers and Secrets
1
2
3
4
5
6
7
| # ❌ Bad - secrets in layer
RUN echo "token=secret123" > /config
# ✅ Good - use BuildKit secrets
RUN --mount=type=secret,id=token \
TOKEN=$(cat /run/secrets/token) && \
configure-app --token=$TOKEN
|
Runtime Security
Resource Limits
1
2
3
4
5
6
7
8
9
10
11
| # docker-compose.yml
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 256M
|
Read-Only Filesystem
1
2
3
4
| docker run --read-only \
--tmpfs /tmp \
--tmpfs /var/run \
myapp:latest
|
Security Options
1
2
3
4
5
| docker run \
--security-opt=no-new-privileges:true \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
myapp:latest
|
Network Security
Isolate Networks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
services:
web:
networks:
- frontend
db:
networks:
- backend
|
Use TLS
1
2
3
4
5
6
| # Enable Docker TLS
dockerd \
--tlsverify \
--tlscacert=ca.pem \
--tlscert=server-cert.pem \
--tlskey=server-key.pem
|
Secrets Management
Docker Secrets
1
2
3
4
5
6
| echo "db_password" | docker secret create db_pass -
docker service create \
--name myapp \
--secret db_pass \
myapp:latest
|
External Secret Managers
1
2
3
4
5
6
7
8
| # Using Vault
services:
app:
environment:
VAULT_ADDR: http://vault:8200
command: |
vault kv get -field=password secret/db > /tmp/pass
app --db-pass=$(cat /tmp/pass)
|
Monitoring and Logging
Container Logs
1
2
3
4
5
| # Centralized logging
docker run \
--log-driver=syslog \
--log-opt syslog-address=tcp://logserver:514 \
myapp:latest
|
Security Monitoring
1
2
3
4
5
6
7
8
| # Falco rules for Docker
- rule: Unauthorized Process in Container
condition: >
spawned_process and
container and
not proc.name in (allowed_processes)
output: Unauthorized process in container
priority: WARNING
|
Best Practices Checklist
Compliance
CIS Docker Benchmark
1
2
3
4
| # Run Docker Bench Security
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
./docker-bench-security.sh
|
Policy Enforcement
1
2
3
4
5
6
7
| # OPA policy for containers
package container.security
deny[msg] {
input.User == "root"
msg = "Container must not run as root"
}
|
Conclusion
Container security requires attention at every stage from image building to runtime. Implement these practices to run Docker securely in production.