Docker audit fix

How to Audit and Restrict Docker Group Members on Linux

Every member of the docker group can reach the daemon socket and start a container that mounts the host filesystem, which makes them root without sudo and without a sudo log entry. Keep the list empty, or justify every name on it.

Hiren KalariyaLast reviewed: Oct 6, 2026Check docker-group-members
Medium
severity
No
needs root or sudo
2
results it can return
4 of 8
checks in this audit

Every threshold on this page is transcribed from the docker-security-daemon-socket audit script that CtrlOps runs, and a build check fails if the two ever disagree. See all 8 Daemon & Socket checks, or how the audit runs.

Medium severityNo sudo needed

What this check reads

It queries the group database with getent group docker and takes field 4 of that line with cut -d: -f4, the comma-separated list of secondary members, turning the commas into spaces with tr ',' ' '. The test is then simply whether anything is left once the spaces are stripped out: an empty field is the PASS, and any name at all is the WARN, which prints every member it found. There is no allow-list and no notion of an expected account, so a CI runner or a deploy user still produces the warning.

When it applies

Always runs, needs no root, and does not need Docker to be installed or the daemon reachable - it simply reads the docker line from the group database via getent, so it also works with LDAP/SSSD-backed groups. It lists secondary members only: a user whose primary group is docker (their GID in /etc/passwd) is not in that field and is not reported. It also cannot see other routes to the socket, such as a sudo rule permitting docker, or filesystem ACLs on /var/run/docker.sock.

What each result means

Result thresholds this check applies
ResultWhenWhat it means
PASSNo membersThe docker group has no secondary members, so socket access stays with root. A user whose primary group is docker would not appear in this field.
WARNMembers listed (confirm each)Every name printed is root-equivalent on this host and can take the machine over without ever appearing in a sudo log. Confirm each one is intended.

Why it matters

Docker's post-install docs state it plainly: the docker group grants root-equivalent privileges. A member can run docker run -v /:/host alpine chroot /host without sudo and without any audit trail in sudo logs. CIS Docker 1.1.2 (v1.6: 1.2.2) "Ensure only trusted users are allowed to control Docker daemon"; OWASP Docker Cheat Sheet Rule #11 (rootless mode) as the alternative.

Why it fails, and when it is wrong

  • Users whose primary group is docker are not listed by field 4. Check getent passwd | awk -F: '$4==<docker gid>'.
  • Membership only matters while the socket is group-owned by docker (default srw-rw---- root docker). Rootless installs have no such requirement.
  • CI runners and deploy users are usually in this group by necessity; treat them as administrators (key-only SSH, restricted sudo is irrelevant since they already have root via Docker).

How to fix it

List the members

The audit lists secondary members only, so check primary-group members as well.

getent group docker
awk -F: '$4=="'"$(getent group docker | cut -d: -f3)"'" {print $1}' /etc/passwd

Remove the accounts that do not need it

Group membership is read at login, so end the user's existing sessions after removing them.

sudo gpasswd -d <user> docker
sudo loginctl terminate-user <user>

Give the rest a narrower route

Every account left in the group is root-equivalent, so treat it as an administrator. People who run containers for themselves are better served by rootless Docker, which gives them their own daemon with no host root. Automation can use a Docker context over SSH or a socket proxy that exposes only the endpoints it needs.

Verify the fix

getent group docker
# expected: empty member list, or only accounts you can justify

# Catch the two blind spots - primary-group members and ACLs on the socket:
awk -F: '$4=="'"$(getent group docker | cut -d: -f3)"'" {print $1}' /etc/passwd
getfacl /var/run/docker.sock 2>/dev/null
sudo grep -rn docker /etc/sudoers /etc/sudoers.d 2>/dev/null

# Confirm a removed user really lost access (they must log out and back in first):
sudo -u <user> docker ps      # expected: permission denied on the socket

Debugging

The check has no allow-list, so any membership is reported. Each name is genuinely root-equivalent: a member can run docker run -v /:/host --privileged and own the machine, entirely bypassing sudo logging. Confirm each is intended and record the exception.

Check the blind spots above: primary-group membership, a sudo docker rule, or an ACL on the socket. Any of those grants the same power without appearing in the group's member list.

Group membership is evaluated at login. They keep it until every session ends; verify with id <user> (the database) versus sudo -u <user> id and have them log out fully.

You largely cannot: socket access is root-equivalent by design. The real options are rootless Docker (see the Rootless Mode check), a socket proxy exposing only specific endpoints, or an audited sudo rule for narrowly-defined commands.

getent returns nothing and the check passes. That is correct on a host where only root uses Docker.

Sources

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
Audit your fleet

Find every one of these on every server, 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.

Windows

✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals