fix(squid): run Squid container as non-root proxy user#1271
Conversation
Add USER directive to Squid Dockerfile so the container runs as the non-root 'proxy' user (uid=13) from the start, reducing the impact of potential container escapes or Squid vulnerabilities. Changes: - Dockerfile: Add USER proxy, remove gosu dependency - entrypoint.sh: Remove root-only chown operations and gosu exec - docker-manager.ts: Set squid log dir ownership to proxy uid - ssl-bump.ts: Chown SSL db to proxy user after initialization - squid-config.ts: Add pid_filename to proxy-owned directory Fixes #250 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 84.23% | 84.24% | ➡️ +0.01% |
| Statements | 84.21% | 84.18% | 📉 -0.03% |
| Functions | 84.37% | 84.44% | 📈 +0.07% |
| Branches | 77.09% | 77.00% | 📉 -0.09% |
📁 Per-file Coverage Changes (2 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/ssl-bump.ts |
90.5% → 87.0% (-3.52%) | 90.7% → 86.4% (-4.21%) |
src/docker-manager.ts |
86.8% → 87.4% (+0.61%) | 86.1% → 86.7% (+0.59%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
There was a problem hiding this comment.
Pull request overview
Hardens the Squid proxy container by running it as the non-root proxy user from container start, shifting any required filesystem permission setup to image build time and/or host-side pre-configuration.
Changes:
- Run the Squid container as
USER proxyand removegosu-based privilege dropping. - Move mounted-volume permission handling to the host side (e.g., create/chown Squid logs dir; chown SSL DB).
- Update generated Squid config to place the PID file under a proxy-owned run directory.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/ssl-bump.ts |
Adds host-side recursive chown of the SSL DB so a non-root Squid can access the mounted directory. |
src/squid-config.ts |
Sets pid_filename to /var/run/squid/squid.pid for non-root operation. |
src/docker-manager.ts |
Adjusts Squid logs directory creation to prefer chown to UID/GID 13 with a chmod fallback. |
containers/squid/entrypoint.sh |
Removes root-only permission fixes and starts Squid directly as the current user. |
containers/squid/Dockerfile |
Removes gosu package and sets USER proxy for the container runtime user. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/ssl-bump.ts
Outdated
| function chownRecursive(dirPath: string, uid: number, gid: number): void { | ||
| fs.chownSync(dirPath, uid, gid); | ||
| for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) { | ||
| const fullPath = path.join(dirPath, entry.name); | ||
| if (entry.isDirectory()) { | ||
| chownRecursive(fullPath, uid, gid); | ||
| } else { | ||
| fs.chownSync(fullPath, uid, gid); | ||
| } | ||
| } |
| // Chown to proxy user (uid=13, gid=13) so the non-root Squid container can access it | ||
| // Gracefully skip if not running as root (e.g., in unit tests) | ||
| try { | ||
| chownRecursive(sslDbPath, 13, 13); | ||
| } catch (err: unknown) { | ||
| if ((err as NodeJS.ErrnoException).code !== 'EPERM') throw err; | ||
| logger.debug('Skipping SSL db chown (not running as root)'); | ||
| } |
| try { | ||
| fs.chownSync(squidLogsDir, SQUID_PROXY_UID, SQUID_PROXY_GID); | ||
| } catch { | ||
| // Fallback to world-writable if chown fails (e.g., non-root context) | ||
| fs.chmodSync(squidLogsDir, 0o777); |
Smoke Test Results — PASS
Author:
|
Export chownRecursive function and add isolated tests with mocked fs to cover directory traversal logic. Add tests for EPERM graceful handling in initSslDb. Fixes coverage regression in ssl-bump.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Smoke Test ResultsPRs: feat(proxy): add GitHub Enterprise Cloud/Server support (#1264), feat(cli): add predownload command to pre-pull container images (#1245)
Overall: PASS
|
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (2 files)
Coverage comparison generated by |
|
Smoke Test Results — ✅ GitHub MCP: #1267 "fix: drop -f from curl to avoid GitHub API rate-limit flakiness", #1265 "fix: add missing formatItem and program imports in cli.test.ts" Overall: PASS
|
Smoke Test Results✅ GitHub MCP: #1264 feat(proxy): add GitHub Enterprise Cloud/Server support | #1267 fix: drop -f from curl to avoid GitHub API rate-limit flakiness Overall: PASS
|
|
fix(squid): run Squid container as non-root proxy user
|
Chroot Version Comparison Results
Overall: ❌ FAILED — Python and Node.js versions differ between host and chroot environments.
|
🏗️ Build Test Suite Results
Overall: 7/8 ecosystems passed — ❌ FAIL ❌ Failure DetailsJava (gson, caffeine) — Maven could not download dependencies from Maven Central. Error: Root cause: The runner environment has an egress firewall (AWF). Maven attempted to use the configured proxy (
|
Summary
USER proxydirective to Squid Dockerfile so the container runs as the non-rootproxyuser (uid=13) from the startgosudependency and root-onlychownoperations from entrypointpid_filenameto squid.conf pointing to proxy-owned/var/run/squid/directorySecurity Impact
Reduces blast radius of potential container escapes or Squid vulnerabilities. Previously the container started as root (using gosu to drop privileges for the main process). Now the entire container lifecycle runs as non-root.
Fixes #250
Test plan
npm run build)npm test)🤖 Generated with Claude Code