pipelock blocks legitimate egress cred injection (scan_env sees EGRESS_TOKEN_*) #84

Closed
opened 2026-05-27 21:13:15 -04:00 by didericis-claude · 0 comments
Collaborator

Symptom

Inside a smolmachines bottle, any agent request to a host that egress has an auth route for (e.g. gitea.dideric.is) is blocked at the pipelock layer with:

blocked: request header contains secret

Reproducible: an in-bottle curl -s https://gitea.dideric.is/<owner>/<repo> returns the pipelock 403 body even though curl itself sends no Authorization header.

Root cause

The sidecar bundle's proxy chain is agent → egress → pipelock → internet. Egress injects the configured auth_scheme token into the Authorization header on outbound requests for routes that declare one. Pipelock is configured with scan_env: true + scan_headers: true + header_mode: all (see claude_bottle/pipelock.py:pipelock_build_config), and runs in the same container as egress under the bundle supervisor (claude_bottle/sidecar_init.py:_spawn).

scan_env: true makes pipelock treat every value in its own process env as a secret to scan request headers for. The supervisor spawns every daemon — egress, pipelock, git-gate, supervise — with the bundle container's full env, which includes the EGRESS_TOKEN_<n> slots set by docker run -e EGRESS_TOKEN_n=<val> (see claude_bottle/backend/smolmachines/sidecar_bundle.py:start_bundle and claude_bottle/backend/docker/compose.py). So:

  1. Egress reads EGRESS_TOKEN_1 from env, injects Authorization: Bearer <val> into the gitea request.
  2. Forwards the modified request upstream to pipelock (EGRESS_UPSTREAM_PROXY=http://127.0.0.1:8888).
  3. Pipelock scans the request headers, finds the token from its own env, matches → blocks.

It's a self-DOS by the bundle's two DLP-aware proxies.

Affected

Both backends — the chain order matches across docker and smolmachines (claude_bottle/backend/smolmachines/launch.py:156 notes the parity).

Fix direction

Strip EGRESS_TOKEN_* (and any other egress-only credential env vars) from the env passed to non-egress daemons in claude_bottle/sidecar_init.py:_spawn. The agent never sees these values (they live in the bundle container, not the bottle), so removing them from pipelock's env loses no real DLP coverage — pipelock can't catch an exfil of a value the agent doesn't have in the first place. Egress still gets the full env, since it needs the token slots to do the injection.

## Symptom Inside a smolmachines bottle, any agent request to a host that egress has an `auth` route for (e.g. `gitea.dideric.is`) is blocked at the pipelock layer with: ``` blocked: request header contains secret ``` Reproducible: an in-bottle `curl -s https://gitea.dideric.is/<owner>/<repo>` returns the pipelock 403 body even though curl itself sends no Authorization header. ## Root cause The sidecar bundle's proxy chain is `agent → egress → pipelock → internet`. Egress injects the configured `auth_scheme` token into the `Authorization` header on outbound requests for routes that declare one. Pipelock is configured with `scan_env: true` + `scan_headers: true` + `header_mode: all` (see `claude_bottle/pipelock.py:pipelock_build_config`), and runs in the **same** container as egress under the bundle supervisor (`claude_bottle/sidecar_init.py:_spawn`). `scan_env: true` makes pipelock treat every value in its own process env as a secret to scan request headers for. The supervisor spawns every daemon — egress, pipelock, git-gate, supervise — with the bundle container's full env, which includes the `EGRESS_TOKEN_<n>` slots set by `docker run -e EGRESS_TOKEN_n=<val>` (see `claude_bottle/backend/smolmachines/sidecar_bundle.py:start_bundle` and `claude_bottle/backend/docker/compose.py`). So: 1. Egress reads `EGRESS_TOKEN_1` from env, injects `Authorization: Bearer <val>` into the gitea request. 2. Forwards the modified request upstream to pipelock (`EGRESS_UPSTREAM_PROXY=http://127.0.0.1:8888`). 3. Pipelock scans the request headers, finds the token from its own env, matches → blocks. It's a self-DOS by the bundle's two DLP-aware proxies. ## Affected Both backends — the chain order matches across docker and smolmachines (`claude_bottle/backend/smolmachines/launch.py:156` notes the parity). ## Fix direction Strip `EGRESS_TOKEN_*` (and any other egress-only credential env vars) from the env passed to non-egress daemons in `claude_bottle/sidecar_init.py:_spawn`. The agent never sees these values (they live in the bundle container, not the bottle), so removing them from pipelock's env loses no real DLP coverage — pipelock can't catch an exfil of a value the agent doesn't have in the first place. Egress still gets the full env, since it needs the token slots to do the injection.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: didericis/bot-bottle#84