Docker audit fixes

Container Hardening: fixing all 5 checks

The container hardening audit inspects each running container's privileged mode, user, capabilities, no-new-privileges and root filesystem: the settings that decide how far a compromise reaches.

All five read docker ps -q, so a host whose containers are all stopped reports five skips rather than five passes. Only Privileged Containers distinguishes "Docker daemon not reachable" from "No running containers"; the other four print one message for both.

5
checks in this audit
3
rated high severity
5
need root or sudo
~10s
automated run time

Transcribed from the docker-security-container-hardening audit script.

Every check

Find your finding

Listed in the order the audit runs them. Take the check name from the third field of the result line and open its page.

  • Privileged Containers

    Soon

    Check whether any running container uses --privileged, which grants every capability and device

    HighNeeds root3 results
  • No New Privileges

    Soon

    Check whether running containers set the no-new-privileges security option to block setuid escalation

    HighNeeds root3 results
  • Container User

    Soon

    Check whether running containers execute as a non-root user or rely on user namespace remapping

    HighNeeds root4 results
  • Container Capabilities

    Soon

    Check for dangerous added capabilities and whether containers drop the default capability set

    MediumNeeds root4 results
  • Read-Only Filesystem

    Soon

    Check whether running containers use a read-only root filesystem

    LowNeeds root3 results
Shared script behaviour

How the Container Hardening audit reads your server

Every check in this audit runs after the same preamble, and several of its results only make sense once you know what that preamble could and could not see.

All inspect running containers only (docker ps -q). Stopped containers are not evaluated.

Docker detection: DOCK=direct if docker ps works as the audit user (docker group), DOCK=root if it works through sudo; the dk helper wraps docker accordingly. IDS is the list of running container IDs and NRUN their count; dk inspect --format is used with Go templates. Podman is recognised but not audited.

Why checks skip

A SKIP is never a pass

A skipped check verified nothing at all. The most common cause is privilege: 5 of the 5 checks in this audit need root for a complete result. Before every run, each script works out which of four privilege modes it is in.

The four privilege modes an audit script can run in, and what each one means for checks that need root
ModeHow it is reachedWhat it means for your results
rootThe audit runs as UID 0.Every privileged branch runs directly. No check skips for lack of permission.
nopasssudo -n true succeeds, so the account has passwordless sudo.Privileged commands run through sudo -n.
passA sudo password was supplied in the CtrlOps audit settings and accepted.Privileged commands run through sudo -S.
noneNone of the above worked.CAN_ROOT=0. Every privileged branch returns SKIP with a "Need root" message rather than guessing.

If many checks skip with a “need root” message, run the audit as root, grant the audit account sudo (with or without NOPASSWD), or supply the sudo password in the CtrlOps audit settings. For Docker checks, membership of the docker group is an alternative to sudo: run usermod -aG docker <user> and log in again.

FAQ

Container Hardening questions

It is advisory. The WARN branch fires whenever HostConfig.CapDrop is empty, which is the default for every plain docker run, so it lands on practically every deployment that has not been hardened. Any non-empty CapDrop, even a single NET_RAW, satisfies it. The FAIL branch is the one to act on first, because it means ALL, SYS_ADMIN or NET_ADMIN was explicitly added.
The FAIL means containers run as UID 0 with no user namespace, so container root is host root the moment something escapes. The WARN means the same containers run as root but docker info reports userns among its security options, so container root maps to an unprivileged host UID and the blast radius is far smaller. It is still worth setting USER in the image, because remapping does nothing about what root can do inside the container itself.
No. --privileged, -u, --cap-drop, --security-opt no-new-privileges and --read-only are all create-time settings, so every fix in this audit means recreating the container from an updated definition rather than adjusting a live one. In Compose the equivalents are privileged, user, cap_drop, security_opt and read_only, applied with docker compose up -d, which recreates only the services whose definitions changed.
Audit your fleet

Run all 5 Container Hardening checks, in one click

CtrlOps runs this audit over your existing SSH connection - no agents, no scripts to manage. $7/user/month after a 1 month free trial - no credit card required.

Start instantly· No credit card· No sneaky autorenewals