feat(egress-proxy): mitmproxy sidecar core (PRD 0017 chunk 1) #28

Merged
didericis merged 1 commits from egress-proxy-sidecar-core into main 2026-05-25 14:04:33 -04:00
Owner

Summary

Chunk 1 of PRD 0017 (docs/prds/0017-egress-proxy-via-mitmproxy.md). Lands the new egress-proxy artifact alongside cred-proxy without yet displacing it. Chunk 2 cuts agent HTTP_PROXY over and removes cred-proxy; chunk 3 retargets PRD 0014 remediation.

What's in the box:

  • Dockerfile.egress-proxy — mitmproxy 11.1.3 base, addon copied flat to /app, routes dir pre-created at /etc/egress-proxy/. Digest pin deferred to chunk 2 (TODO marker in the Dockerfile).
  • egress_proxy_addon_core.py — pure parse + decide logic. Stdlib only. 21 host-importable unit tests cover host-only / full-route / partial-auth-reject / path-allowlist enforcement / case-insensitive host match / Bearer + token schemes / empty + missing token-env behaviour.
  • egress_proxy_addon.py — mitmproxy hook wrapper. Boot + SIGHUP reload of routes.yaml; on each request strips Authorization, calls decide(), returns 403 or injects header. Imports mitmproxy so it's container-only; uses absolute import (from egress_proxy_addon_core) because both files land flat in /app.
  • claude_bottle/egress_proxy.py — host helpers: route lift from manifest, token-env-map derivation, routes.yaml render (JSON content, .yaml extension per PRD), EgressProxy abstract class with prepare().
  • backend/docker/egress_proxy.pyDockerEgressProxy start/stop mirroring DockerCredProxy's shape. Not yet called from launch.py; chunk 2 wires it in.
  • manifest.pyEgressProxyRoute + EgressProxyConfig with the nested auth: { scheme, token_ref } block. Empty auth: {} is an error per PRD. egress_proxy joins the bottle key set; cred_proxy stays (chunk 2 hard-fails on it).

Validated locally:

  • python3 -m unittest discover -s tests/unit — 427 pass (62 of them are new).
  • docker build -f Dockerfile.egress-proxy . succeeds.
  • docker run claude-bottle-egress-proxy:test boots mitmdump; with a mounted routes.yaml the addon logs loaded 2 route(s): api.example.com, x.example.

Notes for review:

  • The routes file is JSON content under a .yaml filename per the PRD's /etc/egress-proxy/routes.yaml path. Every JSON document is valid YAML, and stdlib json works on both ends.
  • The addon's empty-token-env check 403s rather than forwards an unauthenticated request — burning the upstream rate-limit with a guaranteed 401 helps nobody.
  • The Dockerfile pins mitmproxy/mitmproxy:11.1.3. Chunk 2 (which moves the pipelock CA over and starts wiring TLS through this image) is the natural place to lock in the digest after we've shaken anything loose.
## Summary Chunk 1 of PRD 0017 ([docs/prds/0017-egress-proxy-via-mitmproxy.md](../src/branch/main/docs/prds/0017-egress-proxy-via-mitmproxy.md)). Lands the new egress-proxy artifact alongside cred-proxy without yet displacing it. Chunk 2 cuts agent HTTP_PROXY over and removes cred-proxy; chunk 3 retargets PRD 0014 remediation. What's in the box: - `Dockerfile.egress-proxy` — mitmproxy 11.1.3 base, addon copied flat to `/app`, routes dir pre-created at `/etc/egress-proxy/`. Digest pin deferred to chunk 2 (TODO marker in the Dockerfile). - `egress_proxy_addon_core.py` — pure parse + decide logic. Stdlib only. 21 host-importable unit tests cover host-only / full-route / partial-auth-reject / path-allowlist enforcement / case-insensitive host match / Bearer + token schemes / empty + missing token-env behaviour. - `egress_proxy_addon.py` — mitmproxy hook wrapper. Boot + SIGHUP reload of routes.yaml; on each request strips Authorization, calls `decide()`, returns 403 or injects header. Imports `mitmproxy` so it's container-only; uses absolute import (`from egress_proxy_addon_core`) because both files land flat in `/app`. - `claude_bottle/egress_proxy.py` — host helpers: route lift from manifest, token-env-map derivation, routes.yaml render (JSON content, .yaml extension per PRD), `EgressProxy` abstract class with `prepare()`. - `backend/docker/egress_proxy.py` — `DockerEgressProxy` start/stop mirroring `DockerCredProxy`'s shape. Not yet called from `launch.py`; chunk 2 wires it in. - `manifest.py` — `EgressProxyRoute` + `EgressProxyConfig` with the nested `auth: { scheme, token_ref }` block. Empty `auth: {}` is an error per PRD. `egress_proxy` joins the bottle key set; `cred_proxy` stays (chunk 2 hard-fails on it). Validated locally: - `python3 -m unittest discover -s tests/unit` — 427 pass (62 of them are new). - `docker build -f Dockerfile.egress-proxy .` succeeds. - `docker run claude-bottle-egress-proxy:test` boots mitmdump; with a mounted routes.yaml the addon logs `loaded 2 route(s): api.example.com, x.example`. Notes for review: - The routes file is JSON content under a `.yaml` filename per the PRD's `/etc/egress-proxy/routes.yaml` path. Every JSON document is valid YAML, and stdlib `json` works on both ends. - The addon's empty-token-env check 403s rather than forwards an unauthenticated request — burning the upstream rate-limit with a guaranteed 401 helps nobody. - The Dockerfile pins `mitmproxy/mitmproxy:11.1.3`. Chunk 2 (which moves the pipelock CA over and starts wiring TLS through this image) is the natural place to lock in the digest after we've shaken anything loose.
didericis added 1 commit 2026-05-25 13:58:51 -04:00
feat(egress-proxy): add mitmproxy-based sidecar core (PRD 0017 chunk 1)
test / unit (pull_request) Successful in 18s
test / integration (pull_request) Successful in 1m39s
3df54573d4
Lands the new egress-proxy artifact alongside cred-proxy. Chunk 2
wires the agent's HTTP_PROXY to it and removes cred-proxy.

  - `Dockerfile.egress-proxy` — mitmproxy 11.1.3 base, COPY addon
    files flat to /app, mkdir routes dir at /etc/egress-proxy/.
    Digest pin deferred to chunk 2.
  - `egress_proxy_addon_core.py` — pure-logic parse + decide
    (host-importable; 21 unit tests).
  - `egress_proxy_addon.py` — mitmproxy hook wrapper, container-only
    (boot + SIGHUP reload, strip-Authorization + decide + 403/inject).
  - `egress_proxy.py` — host helpers: manifest lift, routes.yaml
    render (JSON content), token-env-map, Plan + abstract class.
  - `backend/docker/egress_proxy.py` — `DockerEgressProxy` start/stop
    mirroring `DockerCredProxy`; not yet called from launch.py.
  - `manifest.py` — new `EgressProxyRoute` + `EgressProxyConfig` types
    with the nested `auth: { scheme, token_ref }` block per PRD;
    `bottle.egress_proxy` added to the bottle key set alongside
    `cred_proxy` (chunk 2 hard-fails on the latter).

All 427 unit tests pass. Image builds; `docker run` boots mitmdump
and the addon loads routes from a mounted routes.yaml.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis merged commit 9e41845a2b into main 2026-05-25 14:04:33 -04:00
didericis deleted branch egress-proxy-sidecar-core 2026-05-25 15:05:08 -04:00
Sign in to join this conversation.