Introduction
Docker is the de‑facto container runtime for modern micro‑services, but its convenience can hide security gaps. As a DevOps lead, you need a repeatable checklist that turns a vanilla Docker host into a hardened production platform. Below are seven practical steps you can apply today, each backed by concrete configuration snippets.
1. Pull Only Verified Base Images
Start with images that are official, signed, and minimal. Alpine, Distroless, or Ubuntu‑Slim reduce the attack surface by stripping unnecessary binaries.
- Use Docker Hub’s official badge as a first filter.
- Enable Docker Content Trust (DCT) to enforce image signatures.
# Enable Docker Content Trust globally
export DOCKER_CONTENT_TRUST=1
# Pull a signed Alpine image
docker pull alpine:3.18
2. Run Containers as Non‑Root Users
By default containers run as root inside the namespace, which can be escalated to the host if a breakout occurs. Define a non‑root user in the Dockerfile and enforce it at runtime.
FROM alpine:3.18
# Create a low‑privilege user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Override any accidental USER directive
docker run --user 1000:1000 myimage
3. Enable User Namespaces
User namespaces map the container’s root UID to an unprivileged UID on the host, preventing true root privileges from leaking.
{
"userns-remap": "default"
}
Save the snippet as /etc/docker/daemon.json
and restart the daemon:
systemctl restart docker
4. Apply Seccomp and AppArmor Profiles
Linux security modules can restrict syscalls and file accesses. Docker ships with a default seccomp profile, but you can tighten it further for your workload.
# Run with a custom seccomp profile
docker run --security-opt seccomp=/etc/docker/seccomp-profile.json myimage
For AppArmor, create a profile under /etc/apparmor.d/docker-myapp
and load it:
apparmor_parser -r /etc/apparmor.d/docker-myapp
5. Mount Filesystems as Read‑Only
If the container does not need to write to its own filesystem, mount it read‑only and provide explicit writable volumes for logs or temp data.
docker run \
--read-only \
-v /var/log/myapp:/var/log/myapp:rw \
myimage
6. Limit CPU, Memory, and PID Resources
Resource quotas prevent a single container from starving the host. Use Docker’s --cpus
, --memory
, and --pids-limit
flags.
docker run \
--cpus="1.5" \
--memory="512m" \
--pids-limit=100 \
myimage
7. Scan Images and Keep the Engine Updated
Integrate vulnerability scanners (e.g., Trivy, Clair) into your CI pipeline. Schedule nightly scans for any images that sit in your private registry.
trivy image myregistry.com/project/myimage:latest
Also, automate Docker Engine upgrades to receive the latest security patches:
apt-get update && apt-get install -y docker-ce docker-ce-cli
Bonus: Secure Secrets Management
Never bake passwords or API keys into images. Use Docker secrets (Swarm) or external vaults like HashiCorp Vault. Example with Docker Swarm:
# Create a secret
printf "super_secret" | docker secret create db_password -
# Use it in a service
docker service create \
--name db \
--secret db_password \
mydbimage
Monitoring and Auditing
Enable Docker’s built‑in audit logs and forward them to a central SIEM. A minimal daemon.json
addition:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5"
}
}
Collect these logs with Filebeat or Fluent Bit for real‑time alerts.
Conclusion
Hardening Docker isn’t a one‑off checklist; it’s a habit of layering defenses—trusted images, non‑root execution, namespace isolation, syscall filtering, read‑only filesystems, resource caps, continuous scanning, and vigilant logging. Apply these seven steps iteratively, and you’ll dramatically reduce the risk of container‑related breaches.
For deeper dives into container security best practices, check out the resources on lacidaweb.com.
Top comments (0)