fix(selfhost): make Docker Compose secret files readable by the container#5124
Conversation
…iner Every secret file was created chmod 600, unreadable by the container's node user (uid 1000) since standalone Compose secrets are a plain bind mount that preserves the HOST file's raw ownership -- it cannot remap uid/gid/mode the way Swarm secrets can (confirmed against a real Compose v5.3.1 daemon: it warns "not supported" and silently no-ops uid/gid/mode fields for file-backed secrets). Every secret read failed with a Sentry-visible selfhost_secret_file_unreadable error on the first real deploy. 644 is the minimum mode that works without requiring the host to have a matching uid/group. scripts/selfhost-init-secrets.sh now also self-heals the mode on an already-existing but still-empty placeholder (never touches one that's been populated with a real value), so a box that already deployed the broken 600 version fixes itself on the next run. Verified against the actual affected server (edge-nl-01): 600 is UNREADABLE to a uid-1000 reader, 644 is READABLE.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5124 +/- ##
=======================================
Coverage 94.32% 94.32%
=======================================
Files 471 471
Lines 39821 39821
Branches 14533 14533
=======================================
Hits 37560 37560
Misses 1583 1583
Partials 678 678
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-11 17:25:19 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 6 non-blocking
Concerns raised — review before merging
Review context
Contributor next steps
Signal definitions
[BETA] Chat with GittensoryAsk Gittensory a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://gittensory.aethereal.dev/docs/gittensory-commands 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Summary
selfhost_secret_file_unreadableerrors (Sentry-visible —level:"error"+ aneventfield) immediately at boot.scripts/selfhost-init-secrets.shcreated each placeholder filechmod 600. Standalone (non-Swarm) Docker Composesecrets:is a plain bind mount under the hood — it cannot remap in-container ownership, so the container reads the file as its own uid (the Dockerfile'sUSER node, uid 1000), which is essentially never the deploying host user's uid. An owner-only 600 file is therefore unreadable to the app itself.uid/gid/modeon the service-levelsecrets:entries — but confirmed against a real Compose v5.3.1 daemon on the actual affected server that Compose silently ignores these fields for file-backed secrets ("secretsuid,gidandmodeare not supported, they will be ignored"). This is a known, longstanding Compose limitation (bind mounts can't implement per-mount uid remapping), not something fixable from the compose file.644is the minimum permission that works without requiring the host to have a matching uid/group with the container.scripts/selfhost-init-secrets.shnow creates new placeholders at644, and self-heals the mode on any already-existing-but-still-empty placeholder (checked via[ ! -s "$path" ]) — so a box that already deployed the broken600version (like edge-nl-01) fixes itself on the very next run, with zero manual intervention. A file that has been populated with a real secret is never touched (content or permissions).Scope
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
npm run test:ci— full local gate green.npm audit --audit-level=moderate— 0 vulnerabilities.uid: 1000reader against a600file mounted viadocker compose secrets:→UNREADABLE.644→READABLE.600placeholder is corrected to644on the next script run; a populated600secret (real content) is left completely untouched, both content and mode.docker composeinvocation (not just docs) that explicituid/gid/modeon file-backed secrets is silently ignored on Compose v5.3.1 — ruled out before choosing the644approach.Safety
secrets/README.mdupdated to document the644tradeoff honestly (readable by any local host user, not just the deploying account — narrower than.env's typical600, and an operator on a shared host should keep using inline.envinstead if that's unacceptable).