Docker Security Best Practices


#Devsecops#Docker-Security

Docker Security Best Practices (Categorized by Lifecycle)

1. While Writing the Dockerfile

  • Use minimal and trusted base images
    Why: Lightweight images (like alpine, distroless) reduce the attack surface and minimize vulnerabilities, plus they make builds smaller and faster. Official images are vetted and maintained regularly.
  • Pin image versions (avoid latest).
    Why: Ensures consistency and prevents unexpected behavior from upstream image changes.
  • Run as non-root user
    Why: Containers running as root can be exploited to compromise the host. Running as a non-root user limits damage if compromised.
    RUN addgroup -S appgroup && adduser -S appuser -G appgroup
    USER appuser
    


  • Reduce attack surface
    Why: Fewer tools and packages mean fewer potential vulnerabilities and exploits.
    • Remove unnecessary packages and debugging tools.
    • Use multi-stage builds to exclude build-time dependencies.
  • Do not store secrets in Dockerfile
    Why: Secrets baked into images can be extracted easily, leading to credential leaks.
  • Set explicit permissions on files and directories.
    Why: Prevents unauthorized access or modification inside the container.
  • Use .dockerignore to exclude sensitive files.
    Why: Prevents secrets and unnecessary files from being sent to the Docker daemon.

2. While Building the Image

  • Scan images during build (Trivy, Grype, Snyk):
    Why: Detect vulnerabilities early before pushing to production.
    trivy image myimage:tag
    


  • Immutable builds (rebuild instead of patching running containers).
    Why: Ensures a clear audit trail and avoids configuration drift.
  • Sign images (Docker Content Trust or cosign):
    Why: Guarantees image integrity and authenticity.
    export DOCKER_CONTENT_TRUST=1
    docker push myimage:tag
    


  • Use BuildKit for secure builds:
    Why: BuildKit isolates build secrets and offers better performance and caching.
    DOCKER_BUILDKIT=1 docker build .
    


  • Keep build cache clean (--no-cache when needed).
    Why: Prevents reusing potentially vulnerable layers.

3. While Deploying & Running Containers

  • Run containers as non-root
    Why: Limits the impact of a compromise.
    docker run --user 1000:1000 myimage:tag
    


  • Apply security profiles (seccomp, AppArmor, SELinux):
    Why: Restricts system calls and reduces the attack surface.
    docker run --security-opt seccomp=default.json myimage:tag
    


  • Limit capabilities:
    Why: Drops unneeded kernel privileges to mitigate exploitation.
    docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myimage:tag
    


  • Use read-only filesystems:
    Why: Prevents tampering with the filesystem during runtime.
    docker run --read-only --tmpfs /tmp myimage:tag
    


  • Restrict resource usage:
    Why: Prevents denial-of-service attacks by resource exhaustion.
    docker run --memory="256m" --cpus="0.5" --pids-limit=100 myimage:tag
    


  • Isolate networks with custom bridge networks.
    Why: Prevents lateral movement between containers.
  • Avoid privileged mode.
    Why: Privileged mode gives containers host-level access, which is dangerous.
  • Rotate secrets at runtime (Docker Secrets or external managers).
    Why: Reduces risk from stale or leaked credentials.
  • Use healthchecks in Dockerfile or Compose.
    Why: Detects unhealthy containers early and triggers self-healing mechanisms.

4. While Maintaining & Monitoring

  • Keep Docker Engine & Host OS updated.
    Why: Patches security vulnerabilities in both layers.
  • Continuous vulnerability scanning (Harbor, AWS ECR, GitHub Security).
    Why: Catches new CVEs affecting existing images.
  • Enable logging & monitoring (Falco, Sysdig, Aqua Security).
    Why: Helps detect suspicious runtime behavior.
  • Remove unused images and containers:
    Why: Reduces attack surface and disk usage.
    docker image prune -a
    docker container prune
    


  • Backup and disaster recovery for image registries.
    Why: Ensures quick recovery after a breach or data loss.
  • Implement RBAC in CI/CD to restrict build/deploy permissions.
    Why: Prevents unauthorized modifications or deployments.

Extra Best Practices (Often Missed)

  • Use multi-stage builds to separate dev & prod dependencies.
    Why: Reduces final image size and removes sensitive build-time data.
  • Perform dependency scanning for code inside containers.
    Why: Identifies vulnerabilities in application libraries.
  • Verify image digests (docker pull myimage@sha256:...).
    Why: Ensures you are pulling the exact, trusted image.
  • Run containers in rootless mode where possible.
    Why: Adds another layer of isolation from the host.
  • Audit Docker daemon configuration (/etc/docker/daemon.json).
    Why: Prevents insecure defaults like unauthenticated API access.
  • Use private registries with authentication.
    Why: Prevents unauthorized access or image tampering.