Skip to main content

Security Audit Checklist

Use this checklist before deploying to production or during periodic security reviews. Each item includes the command to verify it.

1. Container Runtime

CheckPass CriteriaVerify Command
Non-root userConfig.User is set and not rootdocker inspect CONTAINER --format '{{.Config.User}}'
Capabilities droppedCapDrop includes ALLdocker inspect CONTAINER --format '{{json .HostConfig.CapDrop}}'
No --privilegedPrivileged is falsedocker inspect CONTAINER --format '{{.HostConfig.Privileged}}'
Read-only FS (if feasible)ReadonlyRootfs is truedocker inspect CONTAINER --format '{{.HostConfig.ReadonlyRootfs}}'
No new privilegesSecurity option setdocker inspect CONTAINER --format '{{json .HostConfig.SecurityOpt}}'
No host namespace sharingNo --pid host, --network hostdocker inspect CONTAINER --format '{{.HostConfig.PidMode}} {{.HostConfig.NetworkMode}}'

2. Image Security

CheckPass CriteriaVerify Command
Base image is minimalAlpine or Distroless-basedReview Dockerfile FROM line
No critical CVEsScan passes severity policytrivy image IMAGE:TAG or docker scout quickview IMAGE
Image pinned by digestDigest used in prod ComposeCheck compose.yaml for @sha256: references
No secrets in layersNo passwords or keys in imagedocker history IMAGE --no-trunc

3. Network Exposure

CheckPass CriteriaVerify Command
Only required ports publishedNo unnecessary exposed portsdocker ps --format 'table {{.Names}}\t{{.Ports}}'
Internal services unpublishedBackend/DB not accessible externallydocker port CONTAINER
Bound to specific interfaceNot 0.0.0.0 unless intendedCheck Compose ports: directives
Services on user-defined networksNot using default bridgedocker inspect CONTAINER --format '{{json .NetworkSettings.Networks}}'

4. Secrets and Configuration

CheckPass CriteriaVerify Command
No secrets in Compose YAMLNo plain-text passwords in fileReview compose.yaml
No secrets in DockerfileNo ENV PASSWORD= or ARG with secretsReview Dockerfile
.env files gitignored.env not trackedgit status .env should show untracked
Secrets use file mounts_FILE pattern or Compose secrets:Review service configuration

5. Docker Daemon

CheckPass CriteriaVerify Command
Log rotation configuredmax-size and max-file setcat /etc/docker/daemon.json
Daemon socket not exposed/var/run/docker.sock not publishedReview Compose volumes
Docker up to dateRunning recent stable versiondocker version
Docker Socket Warning

Mounting /var/run/docker.sock into a container gives that container full root access to the host. Only do this for trusted management tools (Portainer, Watchtower) and never for application containers.

Quick Audit Script

Run this against any container for a quick security snapshot:

#!/bin/bash
CONTAINER=$1

echo "=== Security Audit: $CONTAINER ==="
echo "User: $(docker inspect $CONTAINER --format '{{.Config.User}}')"
echo "Privileged: $(docker inspect $CONTAINER --format '{{.HostConfig.Privileged}}')"
echo "ReadOnly FS: $(docker inspect $CONTAINER --format '{{.HostConfig.ReadonlyRootfs}}')"
echo "Cap Drop: $(docker inspect $CONTAINER --format '{{json .HostConfig.CapDrop}}')"
echo "Cap Add: $(docker inspect $CONTAINER --format '{{json .HostConfig.CapAdd}}')"
echo "PID Mode: $(docker inspect $CONTAINER --format '{{.HostConfig.PidMode}}')"
echo "Network Mode: $(docker inspect $CONTAINER --format '{{.HostConfig.NetworkMode}}')"
echo "Security Opt: $(docker inspect $CONTAINER --format '{{json .HostConfig.SecurityOpt}}')"
echo "Ports: $(docker port $CONTAINER 2>/dev/null || echo 'none')"

Usage: bash audit.sh my-container

Key Takeaways

  • A security audit should be repeatable and evidence-based -- use commands, not opinions.
  • The five areas that matter most: runtime privileges, image supply chain, network exposure, secrets handling, and daemon configuration.
  • Run a quick audit before every production deployment and a full audit monthly.
  • Automate checks where possible -- the audit script above is a starting point.

What's Next