fix(sidecar_init): scope EGRESS_TOKEN_* to egress daemon only (issue #84) #85

Merged
didericis merged 1 commits from sidecar-init-scope-egress-tokens-issue-84 into main 2026-05-27 22:30:58 -04:00
Collaborator

Closes #84.

Summary

Pipelock was 403-blocking the very Authorization header egress had just injected — blocked: request header contains secret on any in-bottle request to a host with an auth_scheme route. Self-DOS by the bundle's two DLP-aware proxies.

Root cause

Chain is agent → egress → pipelock → internet. Egress injects Authorization: Bearer <token> for configured routes, then forwards upstream. Pipelock has scan_env: true + scan_headers: true + header_mode: all, and the bundle supervisor spawned every daemon (egress, pipelock, git-gate, supervise) inheriting the bundle container's full env — including the EGRESS_TOKEN_<n> slots set via docker run -e. Pipelock had the token value egress injected sitting in its own env, matched it in headers, blocked.

The agent runs in a different machine and never sees EGRESS_TOKEN_*, so stripping these from non-egress daemons loses no DLP coverage.

Changes

  • New _env_for_daemon(name, base_env) in claude_bottle/sidecar_init.py: returns the unchanged base for egress, a copy with EGRESS_TOKEN_* filtered for everyone else. Prefix-based so future egress-only slots don't need code changes.
  • _spawn now passes the scoped env to subprocess.Popen.

Tests

  • TestEnvForDaemon (new, 4 cases): egress gets full env; pipelock / git-gate / supervise lose EGRESS_TOKEN_0+EGRESS_TOKEN_1 but keep PATH / EGRESS_UPSTREAM_PROXY / SUPERVISE_PORT; returned dict is independent of the source.

642 unit tests pass.

Stacking

Branches off main directly. Touches claude_bottle/sidecar_init.py only — no manifest, no provisioning, no protocol changes.

Closes #84. ## Summary Pipelock was 403-blocking the very `Authorization` header egress had just injected — `blocked: request header contains secret` on any in-bottle request to a host with an `auth_scheme` route. Self-DOS by the bundle's two DLP-aware proxies. ## Root cause Chain is `agent → egress → pipelock → internet`. Egress injects `Authorization: Bearer <token>` for configured routes, then forwards upstream. Pipelock has `scan_env: true` + `scan_headers: true` + `header_mode: all`, and the bundle supervisor spawned every daemon (egress, pipelock, git-gate, supervise) inheriting the bundle container's full env — including the `EGRESS_TOKEN_<n>` slots set via `docker run -e`. Pipelock had the token value egress injected sitting in its own env, matched it in headers, blocked. The agent runs in a different machine and never sees `EGRESS_TOKEN_*`, so stripping these from non-egress daemons loses no DLP coverage. ## Changes - New `_env_for_daemon(name, base_env)` in `claude_bottle/sidecar_init.py`: returns the unchanged base for `egress`, a copy with `EGRESS_TOKEN_*` filtered for everyone else. Prefix-based so future egress-only slots don't need code changes. - `_spawn` now passes the scoped env to `subprocess.Popen`. ## Tests - `TestEnvForDaemon` (new, 4 cases): egress gets full env; pipelock / git-gate / supervise lose `EGRESS_TOKEN_0`+`EGRESS_TOKEN_1` but keep `PATH` / `EGRESS_UPSTREAM_PROXY` / `SUPERVISE_PORT`; returned dict is independent of the source. 642 unit tests pass. ## Stacking Branches off main directly. Touches `claude_bottle/sidecar_init.py` only — no manifest, no provisioning, no protocol changes.
didericis-claude added 1 commit 2026-05-27 21:15:11 -04:00
fix(sidecar_init): strip EGRESS_TOKEN_* from non-egress daemons' env (issue #84)
test / unit (pull_request) Successful in 27s
test / integration (pull_request) Successful in 41s
test / unit (push) Successful in 26s
test / integration (push) Successful in 41s
574551e2eb
Pipelock was 403-blocking legitimate egress cred-injected
traffic with 'blocked: request header contains secret'. The
chain is `agent → egress → pipelock → internet`: egress injects
`Authorization: Bearer <token>` for routes with an `auth_scheme`,
then forwards upstream to pipelock. Pipelock has `scan_env:
true` + `scan_headers: true` + `header_mode: all`, and the
bundle supervisor spawned every daemon (egress, pipelock,
git-gate, supervise) inheriting the bundle container's full env
— including the `EGRESS_TOKEN_<n>` slots set via
`docker run -e`. So pipelock had the token value egress
injected sitting in its own env, matched it in the request
headers, and blocked.

The agent itself runs in a different machine and never sees
`EGRESS_TOKEN_*`, so stripping these from non-egress daemons'
env loses no DLP coverage — pipelock can't catch the exfil of
a value the agent doesn't have in the first place.

New helper `_env_for_daemon(name, base_env)` returns the
unchanged base for `egress` and a copy with `EGRESS_TOKEN_*`
filtered for everyone else. `_spawn` now passes the scoped env
to `subprocess.Popen`. Prefix-based filter (not exact-match) so
future egress-only env slots don't have to update this code.

Tests:
- `TestEnvForDaemon`: egress gets full env, pipelock /
  git-gate / supervise lose `EGRESS_TOKEN_0` + `EGRESS_TOKEN_1`
  but keep `PATH`, `EGRESS_UPSTREAM_PROXY`, `SUPERVISE_PORT`.
- Independent-dict invariant locked so callers can't
  accidentally mutate the supervisor's env.

642 unit tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis approved these changes 2026-05-27 22:30:49 -04:00
didericis merged commit 574551e2eb into main 2026-05-27 22:30:58 -04:00
didericis deleted branch sidecar-init-scope-egress-tokens-issue-84 2026-05-27 22:30:58 -04:00
Sign in to join this conversation.