fix(squid): chown bind-mounted log dirs to proxy user on startup#3532
Conversation
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR addresses Squid startup failures on split runner/Docker-daemon filesystems (e.g., ARC + DinD) by ensuring bind-mounted log (and optional SSL DB) directories are owned by Squid’s non-root proxy user before Squid starts.
Changes:
- Start the
squid-proxycompose service as root, run achownpreflight on bind-mounted directories, then drop privileges toproxyviarunuserbefore invoking the image entrypoint. - Apply the same preflight path both when squid config is injected (via
AWF_SQUID_CONFIG_B64) and when it is not. - Add regression tests asserting
user: 0:0, preflight ordering, and uniform application of the privilege-drop wrapper.
Show a summary per file
| File | Description |
|---|---|
| src/services/squid-service.ts | Adds root-run chown preflight + runuser privilege drop wrapper around Squid startup to fix DinD bind-mount ownership issues. |
| src/services/squid-service.test.ts | Adds regression tests to validate the new root preflight + privilege-drop behavior in both config and no-config branches. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 1
| // Pre-flight chown as root: ensure the bind-mounted log directory (and SSL | ||
| // database, when SSL Bump is enabled) are writable by the proxy user (UID 13). | ||
| // On split runner/Docker daemon filesystems (e.g. ARC + DinD), Docker | ||
| // auto-creates missing bind-mount source paths on the daemon side as | ||
| // root-owned, even though the runner pre-chowned its own view of the source | ||
| // to UID 13. The bind-mounted (root-owned) directory then overrides the | ||
| // Dockerfile-baked /var/log/squid (proxy-owned), and squid exits 1 the first | ||
| // time it tries to open access.log. This preflight runs as root, fixes the | ||
| // ownership in place, then drops to the proxy user before anything else | ||
| // (config decode, sed, squid itself) runs. On shared-filesystem runners this | ||
| // is a no-op because the runner already chowned the source dirs to 13:13 in | ||
| // config-writer.ts. runuser preserves env vars by default so AWF_SQUID_CONFIG_B64 | ||
| // remains visible to the dropped-privilege shell. | ||
| const SQUID_PROXY_UID = 13; | ||
| const SQUID_PROXY_GID = 13; | ||
| const chownPreflight = | ||
| `chown -R ${SQUID_PROXY_UID}:${SQUID_PROXY_GID} /var/log/squid && ` + | ||
| `if [ -d /var/spool/squid_ssl_db ]; then chown -R ${SQUID_PROXY_UID}:${SQUID_PROXY_GID} /var/spool/squid_ssl_db; fi`; |
This comment has been minimized.
This comment has been minimized.
b091020 to
9bedf2c
Compare
|
@copilot address the review feedback |
On split runner/Docker daemon filesystems (notably ARC + DinD), Docker auto-creates missing bind-mount source paths on the daemon side as root-owned. That bind-mount then overrides the Dockerfile-baked /var/log/squid (proxy-owned), and squid (UID 13) exits 1 the first time it tries to open access.log. The wrapper's runner-side mkdir + chown to UID 13 in config-writer.ts only applies to the runner's view of the source path — the daemon's view starts empty and is auto- created with root ownership. #3218 fixed the path-translation half of this class of bug. This change covers the second half: even when the path is correct, the daemon-side ownership has to be repaired before squid starts. The compose service builder now runs the squid container's entrypoint as root (user: '0:0'), performs a non-recursive chown of /var/log/squid (and /var/spool/squid_ssl_db when present) back to the proxy user, and drops to the proxy user via 'su -s /bin/bash proxy -c ...' before the image's own entrypoint runs. The image's entrypoint then handles the existing IPv6 listener strip and execs squid itself, all as proxy. Squid never runs as root; the root window is bounded to the two chown syscalls and the su exec. The chown is non-recursive: only the bind-mount source dir's own ownership needs repair, not its (potentially user-supplied) contents. This keeps the preflight bounded and fast even when proxyLogsDir points at a large host directory. su was chosen over runuser/gosu because the squid base image is plain ubuntu (util-linux ships in the base), so su is universally available without requiring an image rebuild. Doing the fix in the wrapper instead of in the squid image keeps it compatible with the older pinned squid shas that the integration and smoke tests use to validate the runtime stack against historical container artifacts. Tests: - Existing 'inject squid config via base64 env var' test still passes: the new entrypoint still emits the base64-decode, just wrapped after the chown and inside the su privilege drop. - New 'chown preflight + drop privileges' test asserts user '0:0', non-recursive chown of /var/log/squid and the conditional SSL DB chown, the ordering (chown before su), and that 'chown -R' is not used anywhere in the entrypoint. - New 'chown preflight applies without config' test covers the no-config branch. - All 2005 unit tests across the repo pass.
9bedf2c to
fad4b95
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Done in the latest commit. |
This comment has been minimized.
This comment has been minimized.
|
✅ Smoke Test Results
Overall: PASS
|
🔬 Smoke Test: Copilot BYOK (Offline) Mode
Running in BYOK offline mode ( PR by @salmanmkc · assignees: Overall: PARTIAL — BYOK inference path confirmed working; pre-step smoke data vars were not substituted at runtime.
|
🔥 Smoke Test Results
Overall: PASS PR: fix(squid): chown bind-mounted log dirs to proxy user on startup — author @salmanmkc, assignees
|
|
Smoke Codex: FAIL Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "registry.npmjs.org"See Network Configuration for more information.
|
|
Smoke Test Report Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "localhost"See Network Configuration for more information.
|
Smoke Test Results
Overall: FAIL —
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Chroot Smoke Test Results
Result: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot.
|
Bug Fix
What was the bug?
On split runner/Docker daemon filesystems (notably ARC + DinD),
awf-squidexits with code 1 immediately on startup:This blocks any agent run on those runners, even with
gh-aw v0.74.7(which pins this repo atv0.25.49— already containing the path-prefix translation fix from #3218).#3218 fixed the first half of this class of bug: making the runner and the daemon agree on which path the bind mount comes from. But the path-translation alone leaves a second symmetry gap: even when both sides resolve the same source path, the daemon-side directory has to be owned by the right user before the consuming container starts.
Why the existing fix is incomplete
config-writer.ts:56-75creates the squid logs dir and chowns it to the proxy UID (13), then sets achmod 0o777fallback if chown fails. But this all runs against the runner's view of the filesystem. On a DinD setup, the runner sets/$workDir/squid-logsto13:13, thendocker compose upstarts the daemon-side bind mount. The daemon's filesystem view is independent — the source path doesn't exist on the daemon side, so the daemon auto-creates it as root-owned (per the documented Docker bind-mount behavior:If you bind-mount a file or directory that does not yet exist on the Docker host, -v creates the endpoint for you. It is always created as a directory.).The container then sees a
root:rootdirectory mounted over/var/log/squid, even though the Dockerfile pre-chowned that path to proxy. Squid (UID 13) tries to open/var/log/squid/access.log→EACCES→ exit 1. No stderr surfaces in the wrapper output because squid dies before completing its log subsystem init.This is the same bug class as #3218 — asymmetric handling between the runner and the daemon — but one layer further down the stack. The two halves are independent: #3218 makes the path correct, this PR makes the ownership correct.
How did you fix it?
The compose squid service now runs its entrypoint as root (
user: '0:0'), performs a chown preflight, then drops to the proxy user before the image's own entrypoint script runs. The preflight is:The image's existing
entrypoint.sh(which handles the IPv6 listener strip and execs squid) is unchanged and still runs as the proxy user. Squid itself never runs as root; the root window is bounded to the twochownsyscalls and thesuexec.A few subtleties:
proxyLogsDirpoints at a large host directory.su -s /bin/bash proxyrather thanrunuserorgosu.suis in util-linux and ships in the plain ubuntu base of theubuntu/squidimage; using it means this PR requires no rebuild of the squid container. Crucially, that keeps the change compatible with the older pinnedsquid=sha256:…shas that the integration and smoke tests use to validate the runtime stack against historical container artifacts.if [ -d ... ]so it is a no-op when SSL Bump is disabled but engages automatically when it is enabled.Why this lives in the wrapper, not the squid image
A previous draft moved the chown + decode + privilege drop into
containers/squid/entrypoint.sh(withgosuinstalled in the Dockerfile). That is the cleaner long-term architecture, but in this repo the integration and smoke tests pin a specific older squid SHA — a wrapper-only PR like this one can ship without bumping that pin. Once a new squid image is pinned, a follow-up PR can move this logic into the image's entrypoint and remove the wrapper-side override.What is the impact?
gh-aw v0.74.7who have--docker-host-path-prefixengaged. Theawf-squidcontainer starts cleanly; noexit code 1; nodependency squid-proxy failed to startcascade.su -s /bin/bash proxyis a thin process hop.Testing
npx tsc --noEmit— clean.npx eslint src/services/squid-service.ts src/services/squid-service.test.ts— 0 errors. (6 pre-existinganywarnings in surrounding code, matching repo style — not introduced by this change.)npx jest src/services/squid-service.test.ts— 5 passed, including 2 new regression tests:should run squid container as root with a chown preflight that drops privileges— assertsuser: '0:0', non-recursivechown proxy:proxyof both/var/log/squidand the conditional SSL DB, the privilege-drop viasu -s /bin/bash proxy -c, the ordering (chown before su), and thatchown -Ris not used anywhere in the entrypoint.should apply the chown preflight even when no squid config content is provided— covers the no-config branch.npx jest(full unit suite) — 2005 of 2005 passed.Caveats
api-proxyandcli-proxyif they ever bind-mount directories that need to be owned by their own (non-root) users — but their containers use Alpineaddgroup -Swhich assigns dynamic UIDs, so a future similar report would need the same treatment with the right UID per service. Out of scope here.--read-only, the chown will fail with EROFS and squid will still exit 1 — but with a different log line that would make the new failure mode obvious.Related