fix: run supervisord as root so services start under rootful Docker#582
Merged
Merged
Conversation
pancacake
added a commit
that referenced
this pull request
Jun 23, 2026
…root-supervisord model #582 made supervisord run as root with per-program user=deeptutor, so the gosu privilege-drop binary is no longer invoked. Remove the gosu and libcap2-bin apt packages and the setcap step (a setuid-capable binary that was left unused == needless attack surface), and refresh the Dockerfile comments. Realign the authoritative docs that still described the old gosu model: - CONTAINERIZATION.md: supervisord runs as root and drops each program to deeptutor via user=; fix the verification snippet. - compose.yaml: correct the (now wrong + dangerous) note that cap_drop ALL is safe — root supervisord NEEDS CAP_SETUID/CAP_SETGID to setuid its children, so cap_drop ALL would stop the backend/frontend from starting. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vaskoyudha
added a commit
to vaskoyudha/deeptutor-for-programmer-fork
that referenced
this pull request
Jul 25, 2026
…visord fix: run supervisord as root so services start under rootful Docker PR HKUDS#577 dropped supervisord itself to UID 1000 via gosu; under a rootful daemon (Docker Desktop / Docker Engine) the root-owned /dev/fd/1,2 and pidfile become unopenable, so backend+frontend never spawn (EACCES). Run supervisord as root and drop each child to deeptutor via per-program user= instead — app processes stay non-root in both rootful Docker and rootless podman. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
vaskoyudha
added a commit
to vaskoyudha/deeptutor-for-programmer-fork
that referenced
this pull request
Jul 25, 2026
…root-supervisord model HKUDS#582 made supervisord run as root with per-program user=deeptutor, so the gosu privilege-drop binary is no longer invoked. Remove the gosu and libcap2-bin apt packages and the setcap step (a setuid-capable binary that was left unused == needless attack surface), and refresh the Dockerfile comments. Realign the authoritative docs that still described the old gosu model: - CONTAINERIZATION.md: supervisord runs as root and drops each program to deeptutor via user=; fix the verification snippet. - compose.yaml: correct the (now wrong + dangerous) note that cap_drop ALL is safe — root supervisord NEEDS CAP_SETUID/CAP_SETGID to setuid its children, so cap_drop ALL would stop the backend/frontend from starting. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The published image does not boot under a rootful Docker daemon (Docker
Desktop on Windows/macOS, or plain Docker on Linux): supervisord fails to spawn
its children and neither the backend nor the frontend starts. The container
stays "up" but
:8001and:3782never respond.Root cause
The entrypoint dropped supervisord itself to the unprivileged
deeptutoruser (UID 1000) via
exec gosu deeptutor /usr/bin/supervisord. Supervisordlogs its programs to
/dev/fd/1,2(the container's stdout/stderr pipes).Under a rootful daemon those pipes are owned by root, so after the
gosudrop UID 1000 cannot open them:
The
gosuapproach only worked under rootless podman (userns_mode: keep-id), where UID 1000 is mapped to the host user that owns those FDs.Relation to #580 / #581
Issue #580 and PR #581 address a different, cosmetic symptom of the same
gosuhardening (PR #577): under rootless podman the pidfileCRITappearsin the log, but children still spawn and the stack is fully functional. Their
fix (
/var/runtmpfsmode=1777) is correct for that case.This PR addresses the fatal case: under a rootful daemon the children
never spawn at all because
/dev/fd/1,2— not the pidfile — areinaccessible to UID 1000. Changing the tmpfs mode does not help here; the
file descriptors are inherited from the container runtime, not a tmpfs path.
CRITpidfile in logs (cosmetic)EACCESon fd/1,2 — app never starts (fatal)compose.yamltmpfs modeDockerfilesupervisord configcompose.yamlDockerfile, testFix
Run supervisord as root and drop only the app processes to
deeptutorvia supervisord's own
user=directive — applied to both the production anddevelopment supervisord configs:
exec /usr/bin/supervisord …(nogosuprefix)[supervisord]section:user=root[program:backend]and[program:frontend]:user=deeptutorRoot opens the log FDs and pidfile, then supervisord
setuids each child —so the backend and frontend processes stay non-root in both runtimes,
preserving the security goal of PR #577.
The dev stage also
chowns/app/web/.nextsonext devcan write itsbuild cache as the unprivileged user.
Related Issues
gosu-based priv-drop from PR feat(deploy): harden image for rootless podman with read-only rootfs #577; differentsymptom and runtime)
rootful Docker)
Module(s) Affected
agentsapiconfigcoreknowledgeloggingservicestoolsutilsweb(Frontend)docs(Documentation)scriptstestsDocker (Dockerfile / container runtime)Checklist
pre-commit run --all-filesand fixed any issues.Additional Notes
Reproduction (released image, no overrides needed):
Verification (rebuilt
--target production, plaindocker run, no overrides):GET /api/v1/auth/status→200,GET /→200. App processes confirmedrunning as
deeptutor(UID 1000) via theuser=directive.Tests: added
test_supervisord_runs_as_root_with_unprivileged_childrenintests/scripts/test_docker_compose.py. Parses the Dockerfile and asserts: (1)no
gosu deeptutorbeforesupervisord, (2) every[program:*]declaresuser=deeptutor. Runs without a Docker build.pytest -q tests/scripts/test_docker_compose.py→ 6 passed.