Skip to content

Security Best Practices

Securing Docker containers is essential for protecting your applications, data, and infrastructure. This guide covers comprehensive security best practices across the container lifecycle — from building images to running containers in production.

Security Layers

Docker security operates at multiple layers:

┌─────────────────────────────────────────────┐
│              Application Security            │
│   (Code, Dependencies, Secrets Management)   │
├─────────────────────────────────────────────┤
│              Container Security               │
│   (User, Capabilities, Read-only FS)         │
├─────────────────────────────────────────────┤
│              Image Security                   │
│   (Base Image, Scanning, Signing)            │
├─────────────────────────────────────────────┤
│              Docker Daemon Security           │
│   (TLS, Authorization, Rootless Mode)        │
├─────────────────────────────────────────────┤
│              Host Security                    │
│   (Kernel, Namespaces, Cgroups, SELinux)     │
└─────────────────────────────────────────────┘

Image Security

Use Trusted Base Images

dockerfile
# ✅ Good: Use official images from verified publishers
FROM node:20-alpine

# ✅ Better: Pin to a specific digest for immutability
FROM node:20-alpine@sha256:abc123def456...

# ❌ Bad: Use unverified third-party images
FROM randomuser/node:latest

Scan Images for Vulnerabilities

bash
# Scan with Docker Scout
docker scout cves my-image:latest

# Scan with Docker Scout and get recommendations
docker scout recommendations my-image:latest

# Scan with Trivy (open-source)
trivy image my-image:latest

# Scan with Snyk
snyk container test my-image:latest

# Scan during CI/CD pipeline
docker scout cves --exit-code --only-severity critical,high my-image:latest

Minimize Image Attack Surface

dockerfile
# Use minimal base images
FROM alpine:3.19

# Remove unnecessary packages
RUN apk add --no-cache \
    nodejs \
    npm && \
    rm -rf /var/cache/apk/*

# Use distroless for maximum security
FROM gcr.io/distroless/nodejs20-debian12

# Use scratch for statically compiled binaries
FROM scratch
COPY --from=builder /app/binary /binary
CMD ["/binary"]

Image Signing and Verification

bash
# Enable Docker Content Trust
export DOCKER_CONTENT_TRUST=1

# Push a signed image
docker push myuser/myapp:1.0

# Pull only signed images
docker pull myuser/myapp:1.0

# Verify image with Cosign (Sigstore)
cosign sign --key cosign.key myregistry/myapp:1.0
cosign verify --key cosign.pub myregistry/myapp:1.0

Container Runtime Security

Run as Non-Root User

dockerfile
# Create and use a non-root user
FROM node:20-alpine

# Create a system group and user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Set ownership of application files
WORKDIR /app
COPY --chown=appuser:appgroup . .
RUN npm ci --only=production

# Switch to non-root user
USER appuser

CMD ["node", "server.js"]
bash
# Verify the container is running as non-root
docker run --rm my-app:latest whoami
# Output: appuser

# Force a container to run as a specific user
docker run -u 1000:1000 my-app:latest

Use Read-Only Filesystems

bash
# Run with read-only root filesystem
docker run -d \
  --read-only \
  --tmpfs /tmp \
  --tmpfs /var/run \
  my-app:latest

# In Docker Compose
# services:
#   app:
#     read_only: true
#     tmpfs:
#       - /tmp
#       - /var/run

Drop Linux Capabilities

bash
# Drop all capabilities and add only needed ones
docker run -d \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  my-app:latest

Common Linux Capabilities

CapabilityDescriptionUsually Needed?
NET_BIND_SERVICEBind to ports below 1024Sometimes
CHOWNMake arbitrary changes to file UIDs/GIDsRarely
DAC_OVERRIDEBypass file permission checksRarely
SETUID / SETGIDSet user/group IDRarely
SYS_ADMINBroad admin privileges❌ Never
SYS_PTRACETrace processesDebugging only
NET_RAWUse raw socketsRarely

Security Options

bash
# Disable privilege escalation
docker run -d --security-opt no-new-privileges my-app:latest

# Apply AppArmor profile
docker run -d --security-opt apparmor=docker-default my-app:latest

# Apply Seccomp profile
docker run -d --security-opt seccomp=/path/to/profile.json my-app:latest

# Apply SELinux labels
docker run -d --security-opt label=type:container_runtime_t my-app:latest

Prevent Privilege Escalation

bash
# Never use --privileged unless absolutely necessary
# ❌ Bad: Gives full host access
docker run --privileged my-app:latest

# ✅ Good: Use specific capabilities instead
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE my-app:latest

Secrets Management

Docker Secrets (Swarm Mode)

bash
# Create a secret
echo "my-secret-password" | docker secret create db_password -

# Create from a file
docker secret create ssl_cert ./server.crt

# Use secrets in a service
docker service create \
  --name my-db \
  --secret db_password \
  -e POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
  postgres:16

Environment Variable Best Practices

bash
# ❌ Bad: Secrets in Dockerfile
ENV DATABASE_PASSWORD=mysecretpassword

# ❌ Bad: Secrets in docker-compose.yml
environment:
  - DATABASE_PASSWORD=mysecretpassword

# ✅ Good: Use .env file (not committed to Git)
env_file:
  - .env

# ✅ Good: Use Docker secrets
secrets:
  db_password:
    file: ./secrets/db_password.txt

# ✅ Good: Use external secret management
# HashiCorp Vault, AWS Secrets Manager, etc.

Build-Time Secrets with BuildKit

dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine

# Use BuildKit secret mount (secret not stored in image layers)
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    npm ci --only=production
bash
# Build with the secret
docker build --secret id=npmrc,src=.npmrc -t my-app:latest .

Network Security

Isolate Networks

yaml
# docker-compose.yml with network isolation
services:
  frontend:
    networks:
      - frontend-net
  
  api:
    networks:
      - frontend-net
      - backend-net
  
  database:
    networks:
      - backend-net  # Not accessible from frontend

networks:
  frontend-net:
  backend-net:
    internal: true  # No external access

Limit Network Exposure

bash
# Bind to localhost only
docker run -d -p 127.0.0.1:8080:80 my-app:latest

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

Resource Limits

Prevent containers from consuming excessive resources:

bash
# Set memory limits
docker run -d \
  --memory=512m \
  --memory-swap=1g \
  my-app:latest

# Set CPU limits
docker run -d \
  --cpus=1.5 \
  --cpu-shares=512 \
  my-app:latest

# Set process limits
docker run -d --pids-limit=100 my-app:latest

# Prevent fork bombs
docker run -d --ulimit nproc=50:100 my-app:latest

# Limit open files
docker run -d --ulimit nofile=1024:2048 my-app:latest

Resource Limits in Compose

yaml
services:
  app:
    image: my-app:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 128M

Docker Daemon Security

Enable TLS for Remote Access

bash
# Generate TLS certificates
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem

# Configure daemon with TLS
# /etc/docker/daemon.json
{
  "tls": true,
  "tlscacert": "/etc/docker/certs/ca.pem",
  "tlscert": "/etc/docker/certs/server-cert.pem",
  "tlskey": "/etc/docker/certs/server-key.pem",
  "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"]
}

Rootless Mode

bash
# Install rootless Docker
dockerd-rootless-setuptool.sh install

# Set environment variables
export PATH=/home/user/bin:$PATH
export DOCKER_HOST=unix:///run/user/1000/docker.sock

# Verify rootless mode
docker info | grep -i rootless

Security Checklist

CategoryPracticePriority
ImageUse minimal base images🔴 Critical
ImageScan images for vulnerabilities🔴 Critical
ImagePin image versions/digests🟡 High
ImageUse multi-stage builds🟡 High
RuntimeRun as non-root user🔴 Critical
RuntimeUse read-only filesystem🟡 High
RuntimeDrop all capabilities, add specific🟡 High
RuntimeSet no-new-privileges🟡 High
RuntimeNever use --privileged🔴 Critical
SecretsNever store secrets in images🔴 Critical
SecretsUse secret management tools🟡 High
NetworkIsolate networks by tier🟡 High
NetworkBind to localhost when possible🟡 High
ResourcesSet memory and CPU limits🟡 High
DaemonEnable TLS for remote access🟡 High
DaemonConsider rootless mode🟢 Medium
LoggingEnable audit logging🟢 Medium

Next Steps

基于 MIT 许可发布