What this check reads
It asks the daemon for its server version with docker version --format '{{.Server.Version}}', takes the runtime version from the first version number in runc --version, and counts lines from docker ps -q, docker ps -aq and docker images -q | sort -u for running containers, total containers and unique images. All of those values go into a single informational PASS line, so there is no threshold and no failing branch. The two SKIPs are the only negative outcomes: no docker binary on the host, or a binary whose daemon could not be reached.
When it applies
Always runs on a host where the docker binary exists; without it, SKIP (noting Podman if that is present instead). Producing the inventory line needs the daemon to be reachable - as root, through sudo, or via docker group membership - otherwise SKIP again. It reports whichever daemon the audit can reach, so on a host running both a rootful and a rootless daemon only one is described. Counts come from docker ps, docker ps -a and docker images, so they reflect that daemon's view at the moment of the audit.
What each result means
| Result | When | What it means |
|---|---|---|
| SKIP | Docker not installed (notes Podman if present) | Nothing was inventoried, because there is no docker binary here. If Podman is installed the message says so, but nothing in this audit inspects Podman workloads. |
| SKIP | Installed but daemon unreachable | Nothing was inventoried. This is the important negative: every other check that needs the daemon will SKIP too, so the Docker audit tells you nothing until access is restored. |
| PASS | Inventory line | The daemon answered and the line records what was found. PASS here means measured, not secure. |
Why it matters
You cannot secure a container platform you have not measured. This check is informational - it never fails - and exists to give the rest of the Daemon & Socket script a stated baseline: which engine version is running, which runc is underneath it, and how many containers and images are on the host. It also distinguishes "no Docker findings because Docker is clean" from "no Docker findings because nothing could be inspected", which is the difference between a passing audit and a blind one. NIST SP 800-128 and the CIS benchmarks both begin with inventory for exactly this reason. The engine and runc versions matter directly: container escapes have repeatedly been runc bugs, so the version string here is a vulnerability check in its own right.
Why it fails, and when it is wrong
- It never returns FAIL or WARN by design. PASS means "this is what was found", not "this is secure".
- SKIP "daemon not reachable" is the important negative: every other Docker check that needs the daemon will also SKIP, so the whole Docker audit is uninformative until it is fixed.
runc --versionmay be missing fromPATHeven when containerd bundles it (/usr/bin/runcversus/usr/local/sbin/runc), sorunc unknownis cosmetic and does not mean runc is absent.- Image and container counts come from the daemon the audit can reach. On a host with both rootful and rootless daemons, only one of them is seen.
- Podman is reported when Docker is absent, but nothing in this script audits Podman workloads.
How to fix it
Nothing to fix - this check reports, it does not judge. Two things are worth acting on from its output:
- If it SKIPs, restore daemon access before trusting any other Docker result: start
docker.service, grant the audit account sudo ordockergroup membership, or setDOCKER_HOSTfor a rootless daemon. - Check the versions against known escapes. A
runcolder than 1.2.8, or a 1.3.x older than 1.3.3, is exposed to three container escapes disclosed in November 2025 (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881). Older releases add CVE-2024-21626 ("Leaky Vessels", container escape via a leaked file descriptor, fixed in 1.1.12) and CVE-2019-5736 (overwriting the hostruncbinary, 1.0-rc6 and earlier). Upgrade thedocker-ce/containerd.io/runcpackages rather than only the engine. See the Docker Engine Updates check.
Verify the fix
docker version --format 'engine {{.Server.Version}}'
runc --version
docker ps -q | wc -l; docker ps -aq | wc -l; docker images -q | sort -u | wc -l
# Confirm the versions are not vulnerable to the known escapes:
dpkg -l docker-ce containerd.io runc 2>/dev/null || rpm -q docker-ce containerd.io runc
# runc must be 1.2.8+ or 1.3.3+ (CVE-2025-31133, CVE-2025-52565, CVE-2025-52881)
# If the daemon is unreachable, work out which case you are in:
systemctl is-active docker
id -nG | tr ' ' '\n' | grep -x docker
echo "$DOCKER_HOST"Debugging
Correct on a host with no Docker. If Podman is in use, note that nothing in this script audits Podman workloads; review them separately.
The single most important failure here, because every other Docker check that needs the daemon will SKIP too. Work through it in order: is the service running (systemctl is-active docker)? can you reach it at all (docker ps)? is the account in the docker group or able to sudo? For a rootless daemon the socket is at $XDG_RUNTIME_DIR/docker.sock and you must set DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock.
Cosmetic: runc is not on the audit's PATH. Find it with find / -name runc -type f 2>/dev/null or docker info | grep -i runc, then check its version manually - it matters, see below.
Check them against the escapes rather than assuming. A runc older than 1.2.8, or a 1.3.x older than 1.3.3, is exploitable through the November 2025 escapes; one before 1.1.12 also through CVE-2024-21626, and 1.0-rc6 or earlier through CVE-2019-5736. Each gives a full container escape regardless of how well the containers are configured.
You are looking at a different daemon (rootful versus rootless), or at a host where another orchestrator manages containers. Compare docker context ls and echo $DOCKER_HOST.
Sources
- Docker Engine release notes
- runc security advisories
- runc: container escape via masked path abuse (CVE-2025-31133)
- Docker: Post-installation steps (docker group, socket)
- Podman
How the script reads this
Next
Re-run the Daemon & Socket audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 8 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.
All 8 Daemon & Socket fixes