Skip to content

Ephemeral containers run Claude as root when Docker socket is absent or GIDs match #67

Description

@alanbem

Problem

In docker/usr/local/bin/docker-entrypoint.sh, NEED_USER_SWITCH starts as false and only flips to true inside the Docker-socket GID-mismatch branch:

NEED_USER_SWITCH=false
if [ -S "/var/run/docker.sock" ]; then
    # ... only sets NEED_USER_SWITCH=true when SOCKET_GID != DOCKER_GROUP_GID
fi

if [ "$NEED_USER_SWITCH" = "true" ]; then
    exec gosu claude "$@"
else
    exec "$@"          # <-- runs as root
fi

The Dockerfile ends with USER root, so for ephemeral containers (DCLAUDE_RM=true, which use the image ENTRYPOINT directly) Claude runs as root whenever:

  1. No Docker socket was mounted (the launcher warns "Container will not have Docker access" and continues), or
  2. The socket GID happens to equal the container's docker group GID (plausible on Linux where host docker GID is commonly 999)

Persistent containers are unaffected only because the launcher always uses docker exec -u claude.

Impact

  • Claude runs with root privileges inside the container — defeats the non-root design stated in SECURITY.md
  • On native Linux hosts, files written to the bind-mounted project are root-owned

Fix

The final exec should always drop to the claude user when currently root, regardless of whether group surgery was needed:

if [ "$(id -u)" = "0" ]; then
    exec gosu claude "$@"
else
    exec "$@"
fi

Related: #66 (UID mapping on Linux hosts) — same entrypoint area.


Found during a full-project code review.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity-relevant issue

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions