| |

Docker 96 🐳 Docker Engine Benchmarking: CIS Docker Benchmark Verification and Compliance Auditing

⏱️ Estimated read: 11 min | |

The CIS Docker Benchmark is a vendor-neutral hardening standard published by the Center for Internet Security, currently at version 1.6.0, with over 100 recommendations organized into seven sections covering the host, daemon, configuration files, images, runtime, operations, and Swarm . Auditors for SOC 2 Type II, PCI DSS 4.0, and FedRAMP accept CIS Docker scores as evidence of secure configuration, which means a failed Scored check—like running containers as root or using privileged mode—can delay a deal cycle or trigger a re-audit . The verification tool is Docker Bench for Security, an open-source script that automates the checks and produces pass/fail/warn results .

Key point: The published docker/docker-bench-security image is out of date. The maintainers recommend cloning the repository and building the image locally before running the audit .


Why the CIS Benchmark is the reference standard

The specificity problem. Broader frameworks like NIST 800-190 describe container security goals but leave the implementation open. CIS provides numeric control IDs—4.1 for “ensure a user for the container has been created,” 5.4 for “do not use privileged containers”—which makes it testable and citable in a control narrative . A SOC 2 report that says “we follow CIS Docker Benchmark v1.6.0, Level 1” is making a claim that an auditor can verify.

The default-behavior problem. The controls that fail most often are not exotic misconfigurations; they are defaults. Control 4.1 fails because Docker images ship running as UID 0 unless a USER directive is added. Control 5.4 fails because CI runners and legacy deployment scripts default to --privileged for Docker-in-Docker builds. Control 2.1 fails because inter-container communication is permissive by default . The benchmark catches what ad hoc reviews miss because it requires an explicit override for each default.

The layered-audit problem. A clean image can still run on a daemon that fails host-level checks. The benchmark covers four distinct layers: host OS (kernel version, separate partition for /var/lib/docker, auditd rules), daemon configuration (TLS, userns-remap, seccomp), image hygiene (no root user, no secrets in layers, content trust), and runtime behavior (no privileged containers, no docker.sock mounts, resource limits) . A “pass CIS” claim without specifying which sections were assessed is meaningless.


a. Running Docker Bench for Security

The script must run with host network, PID, and user namespaces, and it needs read-only access to the Docker socket and the host’s configuration directories. It also requires the audit_control capability to run the auditd checks .

# Clone and build the current image
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
docker build --no-cache -t docker-bench-security .

# Run the full audit
docker run --rm \
  --net host --pid host --userns host \
  --cap-add audit_control \
  -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
  -v /etc:/etc:ro \
  -v /lib/systemd/system:/lib/systemd/system:ro \
  -v /usr/bin/containerd:/usr/bin/containerd:ro \
  -v /usr/bin/runc:/usr/bin/runc:ro \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  --label docker_bench_security \
  docker-bench-security

The volume mounts vary by distribution. On Ubuntu, docker.service lives in /lib/systemd/system; on other systems, it may be in /usr/lib/systemd . The script accepts -c to run specific check sections, -e to exclude specific checks, and -l to write the output to a file for JSON parsing .

# Run only container runtime checks
docker run --rm --net host --pid host --userns host \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  docker-bench-security -c container_runtime

# JSON output for CI integration
docker run ... docker-bench-security -l /dev/stdout 2>/dev/null | tee results.json

b. Interpreting the Results

The output uses four result types: [PASS], [WARN], [INFO], and [FAIL] . The [WARN] results are the ones that need attention—they indicate a check that failed or a configuration that does not meet the benchmark’s recommendation.

FindingRiskFix
Container running as rootMediumAdd USER to Dockerfile or --user to run
Privileged container foundCriticalRemove --privileged; add specific capabilities
Docker socket mountedCriticalRemove mount or use socket proxy
No memory limit setLowAdd --memory or compose mem_limit
No health check definedLowAdd HEALTHCHECK to Dockerfile
Host network mode usedHighUse port mapping and user-defined networks
SYS_ADMIN capability addedHighRemove from cap_add; drop all and add only needed

The critical findings—privileged containers, docker.sock mounts—are also container-escape vectors. A container with the Docker socket mounted can control the daemon, which is equivalent to root on the host . The audit flags these because they are both compliance failures and security failures.


c. Remediation and Continuous Compliance

The daemon configuration is where most of the security gains are realized. The daemon.json file should set icc: false to restrict inter-container communication, userns-remap to map container root to an unprivileged host user, no-new-privileges to prevent privilege escalation, and log rotation to prevent disk exhaustion .

{
  "icc": false,
  "userns-remap": "default",
  "live-restore": true,
  "userland-proxy": false,
  "no-new-privileges": true,
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

Container runtime hardening is handled at the docker run level or in the compose file. The pattern is to drop all capabilities and add back only what the process needs, mount the root filesystem as read-only with tmpfs for writable paths, and apply a seccomp profile .

services:
  app:
    image: myapp:1.4.2
    read_only: true
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=64m
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true

Continuous compliance requires running the audit on a schedule and alerting on [WARN] results. The script can be run from a cron job or a CI pipeline, and the JSON output can be compared against a baseline to detect drift .

# Daily audit
0 2 * * * /usr/local/bin/docker-bench-security.sh > /var/log/docker-audit.log
# Alert on failures
if grep -q "WARN" /var/log/docker-audit.log; then send-alert; fi

Complete Example Session

# ============================================
# PART 1: BUILD THE CURRENT AUDIT IMAGE
# ============================================
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
docker build --no-cache -t docker-bench-security .


# ============================================
# PART 2: RUN THE FULL AUDIT
# ============================================
docker run --rm --net host --pid host --userns host \
  --cap-add audit_control \
  -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
  -v /etc:/etc:ro \
  -v /lib/systemd/system:/lib/systemd/system:ro \
  -v /usr/bin/containerd:/usr/bin/containerd:ro \
  -v /usr/bin/runc:/usr/bin/runc:ro \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  --label docker_bench_security \
  docker-bench-security


# ============================================
# PART 3: IDENTIFY WARN RESULTS
# ============================================
# Look for [WARN] lines
# Common: 4.1 (root user), 5.4 (privileged), 5.9 (host network)


# ============================================
# PART 4: REMEDIATE DAEMON CONFIG
# ============================================
# Edit /etc/docker/daemon.json
{
  "icc": false,
  "userns-remap": "default",
  "no-new-privileges": true,
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}
sudo systemctl restart docker


# ============================================
# PART 5: REMEDIATE CONTAINER RUNTIME
# ============================================
docker run -d --name app \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges:true \
  myapp:1.4.2


# ============================================
# PART 6: REMEDIATE DOCKERFILE
# ============================================
# Add non-root user before the final CMD
FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --chown=app:app . .
USER app
CMD ["node", "server.js"]


# ============================================
# PART 7: RE-RUN THE AUDIT
# ============================================
# Compare results to the baseline
# The [WARN] count should decrease


# ============================================
# PART 8: SCHEDULE CONTINUOUS AUDIT
# ============================================
# Cron: daily at 2 AM
0 2 * * * /usr/local/bin/docker-bench-security.sh > /var/log/docker-audit.log
# Alert on WARN
if grep -q "WARN" /var/log/docker-audit.log; then send-alert; fi


# ============================================
# PART 9: VERIFY SPECIFIC SECTIONS
# ============================================
docker run ... docker-bench-security -c container_runtime,container_images


# ============================================
# PART 10: EXPORT FOR CI
# ============================================
docker run ... docker-bench-security -l /dev/stdout 2>/dev/null | tee results.json

The ten parts trace the audit from build through remediation to continuous monitoring. Part 3 shows the failure identification, and Parts 4 through 6 show the three layers of remediation: daemon, runtime, and image.


Quick Reference

CIS Docker Benchmark Sections

SectionScopeKey Checks
1Host configurationSeparate partition, auditd, kernel
2Docker daemonTLS, userns-remap, icc, seccomp
3Daemon filesFile permissions, ownership
4Container imagesNon-root USER, no secrets, content trust
5Container runtimeNo privileged, no host network, limits
6Security operationsAudit logging, API security
7SwarmSwarm mode security

Common Failures

CheckFailureFix
4.1Container runs as rootUSER in Dockerfile
5.4Privileged containerRemove --privileged
5.9Host network modeUse port mapping
2.1ICC enabled"icc": false in daemon.json
5.12Writable rootfs--read-only
4.6No HEALTHCHECKAdd to Dockerfile

Audit Commands

CommandPurpose
docker-bench-securityFull audit
-c sectionRun specific sections
-e checkExclude specific checks
-l fileWrite output to file
-bDisable colors

Best Practices

✅ Do This:

# Build the current image before auditing
git clone ... && docker build --no-cache -t docker-bench-security .  # ✅
# Run with host namespaces and audit_control capability
--net host --pid host --userns host --cap-add audit_control           # ✅
// Set icc false and userns-remap in daemon.json
{ "icc": false, "userns-remap": "default" }                           // ✅
// Add USER directive to every Dockerfile
USER app                                                               // ✅

❌ Don’t Do This:

# Use the outdated published image
docker run docker/docker-bench-security  # out of date                  // ❌
# Assume a clean image passes the audit
# Daemon and host checks are separate                                   // ❌
# Run the audit once and assume it stays compliant
# Drift occurs; schedule continuous audits                             // ❌

Common Pitfalls

PitfallWhy It HappensFix
Audit image is stalePublished image not maintainedBuild from source
audit_control missingChecks for auditd failAdd the capability
Volume mount wrongdocker.service path differs by distroAdjust mount path
Remediation breaks appDropped required capabilityAdd back only needed caps
--privileged still neededCI runner uses Docker-in-DockerUse rootless or socket proxy

Real-World Examples

1. Build Audit Image

git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security && docker build --no-cache -t docker-bench-security .

2. Run Full Audit

docker run --rm --net host --pid host --userns host --cap-add audit_control -v /var/run/docker.sock:/var/run/docker.sock:ro docker-bench-security

3. Restrict ICC

{ "icc": false }

4. Enable Userns Remap

{ "userns-remap": "default" }

5. Non-Root User

USER app

6. Read-Only Rootfs

docker run --read-only --tmpfs /tmp myapp

7. Drop Capabilities

docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp

8. Schedule Audit

0 2 * * * /usr/local/bin/docker-bench-security.sh > /var/log/docker-audit.log

9. Run Specific Section

docker-bench-security -c container_runtime

10. JSON Export

docker-bench-security -l /dev/stdout 2>/dev/null | tee results.json

Visual

The Seven Sections

┌──────────────────────────────────────────────────────────────┐
│  CIS DOCKER BENCHMARK v1.6.0                                 │
│                                                              │
│  1. Host Configuration                                       │
│     └─ Partitions, auditd, kernel                            │
│                                                              │
│  2. Docker Daemon Configuration                              │
│     └─ TLS, userns-remap, icc, seccomp                       │
│                                                              │
│  3. Daemon Configuration Files                               │
│     └─ Permissions, ownership                                │
│                                                              │
│  4. Container Images & Build Files                           │
│     └─ Non-root USER, no secrets, content trust              │
│                                                              │
│  5. Container Runtime                                        │
│     └─ Privileged, host network, limits, read-only           │
│                                                              │
│  6. Docker Security Operations                               │
│     └─ Audit logging, API security                           │
│                                                              │
│  7. Docker Swarm Configuration                               │
│     └─ Swarm mode security                                   │
│                                                              │
│  A clean image can still fail host and daemon checks.        │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The Audit Pipeline

┌──────────────────────────────────────────────────────────────┐
│  docker-bench-security                                       │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────┐                                        │
│  │ Read daemon.json│                                        │
│  │ docker info     │                                        │
│  │ docker inspect  │                                        │
│  │ File stats      │                                        │
│  └────────┬────────┘                                        │
│           ▼                                                  │
│  ┌─────────────────┐                                        │
│  │ Run checks      │                                        │
│  │ per section     │                                        │
│  └────────┬────────┘                                        │
│           ▼                                                  │
│  ┌─────────────────┐                                        │
│  │ Output          │                                        │
│  │ [PASS] [WARN]   │                                        │
│  │ [INFO] [FAIL]   │                                        │
│  └────────┬────────┘                                        │
│           ▼                                                  │
│  ┌─────────────────┐                                        │
│  │ Remediate       │                                        │
│  │ Re-run          │                                        │
│  └─────────────────┘                                        │
│                                                              │
│  Read-only tool. It never modifies the host.                │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The Three Remediation Layers

┌──────────────────────────────────────────────────────────────┐
│  LAYER 1: DAEMON                                             │
│  /etc/docker/daemon.json                                     │
│  ├─ icc: false                                              │
│  ├─ userns-remap: default                                   │
│  └─ no-new-privileges: true                                 │
│                                                              │
│  LAYER 2: CONTAINER RUNTIME                                  │
│  docker run / compose                                        │
│  ├─ --read-only                                             │
│  ├─ --cap-drop ALL                                          │
│  └─ --security-opt no-new-privileges                        │
│                                                              │
│  LAYER 3: IMAGE                                              │
│  Dockerfile                                                  │
│  ├─ USER app                                                │
│  ├─ Minimal base                                            │
│  └─ No secrets in layers                                    │
│                                                              │
│  Each layer is independent. All three must pass.             │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Continuous Compliance

┌──────────────────────────────────────────────────────────────┐
│  SCHEDULE (cron)                                             │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────┐                                        │
│  │ Run audit       │                                        │
│  │ daily at 2 AM   │                                        │
│  └────────┬────────┘                                        │
│           ▼                                                  │
│  ┌─────────────────┐                                        │
│  │ Check for WARN  │                                        │
│  └────────┬────────┘                                        │
│           │                                                  │
│     ┌─────┴─────┐                                           │
│     ▼           ▼                                           │
│  ┌──────┐   ┌──────┐                                       │
│  │ None │   │ WARN │                                       │
│  │      │   │ found│                                       │
│  │ Sleep│   │ Alert│                                       │
│  └──────┘   └──────┘                                       │
│                                                              │
│  Drift happens. The audit must run continuously.            │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
BenchmarkCIS Docker Benchmark v1.6.0
Controls100+ across 7 sections
Audit toolDocker Bench for Security
Tool requirementHost network, PID, userns, audit_control
Published imageOut of date; build from source
Result types[PASS], [WARN], [INFO], [FAIL]
Most common failuresRoot user, privileged, host network
Daemon fixicc: false, userns-remap, no-new-privileges
Runtime fix--read-only, --cap-drop ALL
Image fixUSER directive, minimal base

Key takeaways:

  • The CIS Docker Benchmark is the reference standard for Docker hardening. It is specific enough to be testable and is cited directly by SOC 2, PCI DSS, and FedRAMP auditors. The 100+ controls span the host, daemon, images, and runtime, and a failure at any layer is a failure of the deployment .
  • Docker Bench for Security is the audit tool, but the published image is stale. The repository must be cloned and the image built locally before running the audit. The script requires host namespaces and the audit_control capability to check the host-level controls .
  • The most common failures are defaults, not exotic misconfigurations. Containers running as root, privileged mode used for CI, host network mode used for service discovery, and inter-container communication left permissive. Each has a specific fix, and each fix is a configuration override .
  • Remediation is layered. The daemon configuration, the container runtime flags, and the Dockerfile each address different controls. A clean image does not compensate for a permissive daemon, and a hardened daemon does not compensate for a privileged container.
  • Compliance is continuous, not one-time. Configurations drift, new containers are deployed, and new CVEs are published. The audit must run on a schedule, and the results must be compared against a baseline to detect drift .

Remember: The CIS Docker Benchmark is a checklist, not a scanner. It tells you what a reasonably hardened Docker deployment looks like, and Docker Bench tells you which of those recommendations your deployment fails. The tool is read-only; the fixes are manual or automated through configuration management. The benchmark’s value is that it makes the definition of “hardened” specific enough to audit and specific enough to remediate. The controls that fail most often—root users, privileged containers, host networking—are the ones that are easiest to leave at their defaults and the ones that carry the highest risk. The audit exists to surface them before an attacker does.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!