Docker Bench Security: Running a CIS Audit for Container Hosts
AI generated
FROM
RUN
Docker · Container Security · CIS Benchmark · DevSecOps
Docker Bench Security: Running a CIS Audit for Container Hosts
from raw report to prioritized hardening

Docker Bench Security automatically checks host, daemon and running containers against the CIS Docker Benchmark and produces a structured report within minutes. Reading that output correctly, prioritizing it and wiring it into the CI pipeline turns a one time audit into a continuous security process instead of an ignored PDF file.

18 min read CIS Benchmark · daemon hardening · CI integration Docker CE/EE · Linux hosts

1. What Docker Bench Security checks and why it matters

Docker Bench Security is a shell script maintained by Docker Inc. and the community that checks the host, the running daemon and all active containers against the CIS Docker Benchmark. Instead of manually looking up individual settings, Docker Bench Security produces a structured report within minutes, marking each of roughly a hundred individual checks as PASS, WARN or INFO. The tool does not replace deep image analysis, but it is the fastest way to get a realistic overview of a Docker host's security posture.

The real value of Docker Bench Security lies in the fact that it answers configuration questions that are often overlooked in practice: is audit logging enabled on the daemon? Are container permissions unnecessarily broad? Is the Docker socket reachable without protection? These questions do not concern a single image but the entire operating environment, and that is exactly where Docker Bench Security steps in.

In practice, Docker Bench Security is often run once before an audit or certification and then forgotten. That is a mistake, because configuration drift is the rule rather than the exception in Docker environments. A new team member resets a daemon feature flag, a deployment script forgets a security setting, and the security posture degrades quietly. The following sections show how Docker Bench Security is installed, interpreted, prioritized and permanently embedded into operations.

2. Installation and a first run against the host

Docker Bench Security itself runs as a container with far reaching access to the host, because it needs to check exactly those host properties. The script requires access to the Docker socket, to `/etc`, to `/usr/bin/docker` and to the daemon's systemd unit files. Without these mounts, Docker Bench Security can only actually perform a fraction of the checks and reports the remaining ones as INFO instead of a real result.

The first run should happen on a representative host, not on an isolated test machine with a different configuration. Only then does Docker Bench Security deliver results that actually reflect the production state. The script writes its report to the console by default, but it can also be exported as JSON, which matters greatly for later automation.


#!/usr/bin/env bash
# Run Docker Bench Security against the local host
set -euo pipefail

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 /usr/bin/docker-containerd:/usr/bin/docker-containerd:ro \
  -v /usr/bin/docker-runc:/usr/bin/docker-runc:ro \
  -v /usr/lib/systemd:/usr/lib/systemd:ro \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  --label docker_bench_security \
  docker/docker-bench-security > bench-report.log

# Extract only WARN lines for a quick first triage
grep '\[WARN\]' bench-report.log | sort | uniq -c | sort -rn

A common beginner mistake is starting Docker Bench Security without `--net host` and `--pid host`. Without these flags the script only sees its own isolated namespace instead of the host processes, and a large share of the runtime checks come back as INFO instead of a reliable result. Anyone who wants to seriously evaluate the report should always use the full mount and namespace configuration from the official documentation.

3. Understanding the structure of the CIS Docker Benchmark

The CIS Docker Benchmark that Docker Bench Security maps onto is divided into seven main sections: host configuration, Docker daemon configuration, daemon configuration files, container images and build files, container runtime, Docker security operations and Docker Swarm configuration. Each section contains numbered individual checks, which appear in the Docker Bench Security report under the same numbering, making it easy to look them up in the official CIS documentation.

Not every check is equally relevant for every environment. A check about Docker Swarm configuration provides no practical value in pure Compose or Kubernetes setups and should be classified accordingly. Docker Bench Security does not automatically distinguish between relevant and irrelevant checks for a given architecture, that assessment remains the task of the team reading the report. Whoever ignores this context wastes time fixing findings that do not even apply in their own environment.

4. Checking and hardening host configuration

The first section of the CIS benchmark that Docker Bench Security queries concerns host configuration independent of Docker itself: a separate partition for container data, a current kernel version, enabled auditing for Docker binaries and directories. These checks look generic at first glance, but they are the foundation on which every container isolation is built. An outdated kernel without the relevant security patches makes even the best container configuration attackable.

Particularly important is the check for auditing `/usr/bin/docker`, `/var/lib/docker`, `/etc/docker` and the corresponding systemd unit files through `auditd`. Without these audit rules, an unauthorized change to the Docker configuration goes unnoticed until damage has already occurred. Docker Bench Security consistently marks missing audit rules as WARN, because they are forensically decisive as soon as an incident needs to be investigated.


#!/usr/bin/env bash
# Add auditd rules that Docker Bench Security checks for (host hardening)
set -euo pipefail

cat >> /etc/audit/rules.d/docker.rules <<'EOF'
-w /usr/bin/docker -p wa -k docker
-w /var/lib/docker -p wa -k docker
-w /etc/docker -p wa -k docker
-w /lib/systemd/system/docker.service -p wa -k docker
-w /lib/systemd/system/docker.socket -p wa -k docker
-w /etc/default/docker -p wa -k docker
-w /etc/docker/daemon.json -p wa -k docker
-w /usr/bin/containerd -p wa -k docker
-w /usr/sbin/runc -p wa -k docker
EOF

augenrules --load
systemctl restart auditd

5. Securing the Docker daemon configuration

The second major block of checks in Docker Bench Security concerns the running daemon configuration: is TLS encrypted access to the Docker socket active, if it is reachable over the network at all? Is live restore enabled so containers survive a daemon restart? Is user namespace remapping configured to decouple root inside a container from real root on the host? Each of these questions corresponds to a concrete entry in `/etc/docker/daemon.json`, which Docker Bench Security checks specifically.

Particularly critical is the check for an unprotected TCP socket. A Docker daemon listening on `tcp://0.0.0.0:2375` without TLS grants any network participant full root rights on the host, because any container can be started with `--privileged`. Docker Bench Security consistently classifies this finding as a critical WARN, and rightly so: in practice this is one of the most common ways compromised Docker hosts are found in the wild.


{
  "live-restore": true,
  "userns-remap": "default",
  "no-new-privileges": true,
  "icc": false,
  "log-driver": "journald",
  "log-opts": { "max-size": "10m", "max-file": "3" },
  "userland-proxy": false,
  "tls": true,
  "tlsverify": true,
  "tlscacert": "/etc/docker/certs/ca.pem",
  "tlscert": "/etc/docker/certs/server-cert.pem",
  "tlskey": "/etc/docker/certs/server-key.pem"
}

6. Interpreting container and image checks correctly

The container runtime section is usually the longest WARN block on a first Docker Bench Security run, because it checks every running container against more than twenty criteria: an active AppArmor or SELinux profile, no privilege escalation allowed, no mounted sensitive host paths, no access to the Docker socket inside the container itself, bounded memory and CPU resources. A single container can easily generate ten or more WARN entries here.

What matters is not to work through these findings uniformly, but to sort them by attack surface. A container without a defined memory limit is an operational risk, but rarely a direct security incident. A container with a mounted Docker socket (`-v /var/run/docker.sock:/var/run/docker.sock`), on the other hand, is a direct path to full host compromise, because that container can start any other container with any permissions. Docker Bench Security reports both cases as WARN, but they are not comparable in terms of prioritization.


# Typical Docker Bench Security output for the container runtime section
[WARN] 5.1  - Ensure AppArmor Profile is Enabled
[WARN] 5.4  - Ensure privileged containers are not used
[WARN] 5.6  - Ensure ssh is not run within containers
[WARN] 5.9  - Ensure the host's network namespace is not shared
[WARN] 5.10 - Ensure memory usage for container is limited
[WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers

# Prioritize by real attack surface, not just WARN count
for cid in $(docker ps -q); do
  docker inspect "$cid" | jq -r '.[0].Mounts[].Source' | grep -q docker.sock \
    && echo "[CRITICAL] Container $cid has the Docker socket mounted"
done

7. Recognizing false positives and defining a baseline

Not every WARN from Docker Bench Security is a real risk in every environment. Some checks assume Docker Swarm operation and are irrelevant for pure Compose deployments, others concern legacy options such as the unprotected userland proxy, which is disabled anyway in modern setups. Whoever weighs every check equally quickly loses overview and starts ignoring reports instead of using them.

The pragmatic approach is to define a documented baseline: which checks are deliberately marked as accepted risk, with reasoning and an owner, and which must be at PASS on every Docker Bench Security run. This baseline belongs in the repository, not in one person's head, so new team members can trace why certain WARN findings are consciously accepted.

8. Automating Docker Bench Security in CI/CD

A one time audit loses its value as soon as infrastructure changes. The sustainable approach is to run Docker Bench Security regularly against production like hosts and compare results against the defined baseline. If a new run deviates from the baseline, because a previously PASS marked check suddenly reports WARN, that is a clear signal of configuration drift that should trigger an alert.

The JSON output of Docker Bench Security can be filtered with `jq` for new or changed findings instead of manually reading the entire report on every run. In a scheduled pipeline, for example a weekly cron job against the production hosts, a one time compliance proof turns into a continuous control mechanism.


#!/usr/bin/env bash
# Compare Docker Bench Security JSON output against an accepted baseline
set -euo pipefail

docker run --rm --net host --pid host --userns host --cap-add audit_control \
  -v /etc:/etc:ro -v /var/lib:/var/lib:ro -v /var/run/docker.sock:/var/run/docker.sock:ro \
  docker/docker-bench-security -l /tmp/bench.json -j > /dev/null

# Fail the pipeline if a new WARN appears that is not in the accepted baseline
new_warns=$(jq -r '.tests[].results[] | select(.result=="WARN") | .id' /tmp/bench.json \
  | grep -vFf accepted-baseline.txt || true)

if [[ -n "$new_warns" ]]; then
  echo "[ALERT] New Docker Bench Security findings outside baseline:" >&2
  echo "$new_warns" >&2
  exit 1
fi

echo "[OK] No new findings outside the accepted Docker Bench Security baseline"

9. Docker Bench Security compared to other tools

Docker Bench Security covers host and daemon configuration excellently, but does not replace image vulnerability analysis or runtime anomaly detection. The following table places the tool against other common approaches so it becomes clear which gaps additional tools need to close.

Tool Checks Runs when Complements Docker Bench Security
Docker Bench Security Host, daemon, config files, runtime Manual or scheduled Foundation for every other tool
Trivy / Grype Known CVEs in image layers Build time / CI Yes, different layer (image instead of host)
Falco Anomalous runtime behavior Continuous, live Yes, covers attacks after startup
Manual code review Dockerfile anti patterns Pull request Yes, catches root causes earlier

Docker Bench Security is therefore not a replacement, but the foundation: without a properly hardened host and daemon, even the best scanned image runs on unsafe ground. The combination of Docker Bench Security for host and daemon, Trivy or Grype for images, and Falco for runtime covers the three relevant layers of container security completely.

Mironsoft

Container security audits and Docker hardening

How secure is your Docker infrastructure really?

We run Docker Bench Security audits, prioritize findings by real attack surface, and harden host, daemon and container configuration systematically instead of just checking off compliance reports.

CIS audit

Docker Bench Security runs with full findings prioritization

Daemon hardening

Configure TLS, user namespace remapping and audit logging production ready

CI integration

Build continuous audits with baseline comparison into your pipeline

10. Summary

Docker Bench Security is the fastest entry point into a structured CIS audit for Docker hosts. The tool checks host configuration, daemon settings, configuration files and container runtime against roughly a hundred documented checks and delivers a prioritizable report instead of vague guesses. What matters is not to treat the report as a one time exercise, but to define a documented baseline and detect deviations automatically.

Docker Bench Security replaces neither image scanning nor runtime threat detection, but it forms the foundation both build on. A hardened host with a correctly configured daemon reduces the attack surface before a single image is even checked. Combined with regular CI runs, the script turns into a continuous control mechanism instead of a one time compliance proof.

Docker Bench Security — The essentials at a glance

Installation

Runs as a container with `--net host --pid host` and access to Docker socket, `/etc` and systemd units, otherwise runtime checks stay incomplete.

Scope

Seven CIS sections: host, daemon, configuration files, images, runtime, security operations, Swarm.

Prioritization

Not every WARN is equally critical: a mounted Docker socket outranks a missing memory limit by far.

Automation

JSON export plus baseline comparison with `jq` in a scheduled pipeline detects configuration drift automatically.

11. FAQ: Docker Bench Security

1What exactly does Docker Bench Security check?
Host, daemon, configuration files, images, runtime, security operations and optionally Swarm against the CIS Docker Benchmark.
2Does it need full root rights?
Yes, access to the Docker socket, --pid host and configuration directories are needed for complete checks.
3Is every WARN a real risk?
No, some checks concern Swarm or legacy options. A documented baseline with reasoning is the pragmatic path.
4Does it replace Trivy or Grype?
No, it checks host and daemon, not CVEs in image layers. Both layers complement each other.
5How often should it run?
Regularly, for example weekly, with automated baseline comparison rather than a one time audit.
6What does a TCP socket WARN mean?
The daemon is reachable over the network without TLS, effectively meaning root rights on the host. One of the most critical findings.
7Does it work with Kubernetes?
Only to a limited extent. For clusters there are dedicated tools like kube-bench using the same CIS methodology.
8How do I export the results?
With -l for the log file and -j for JSON, then filter specifically with jq, for example for new findings outside the baseline.
9Which checks should never be ignored?
Unprotected TCP socket, missing audit logging, mounted Docker socket inside a container, and missing user namespace remapping.
10Only relevant for enterprise teams?
No, small teams benefit too since the script is free and quickly surfaces critical misconfigurations.