PRD 0017: Egress-proxy — universal MITM via mitmproxy (replaces cred-proxy) #27

Merged
didericis merged 3 commits from prd-0017-path-aware-egress into main 2026-05-25 13:45:34 -04:00
Showing only changes of commit a79b2b7be0 - Show all commits
+33 -18
View File
@@ -21,7 +21,8 @@ are split between cred-proxy and pipelock:
the cred-proxy URL.
3. **Credential injection.** Continues cred-proxy's existing role:
match by hostname (or hostname + path), strip inbound
Authorization, inject one based on `auth_scheme` + `token_ref`.
Authorization, inject one based on the route's optional `auth:
{ scheme, token_ref }` block.
Pipelock's role narrows to hostname allowlist + DLP body scanning
on the egress-proxy → upstream leg. Pipelock no longer holds the
@@ -90,8 +91,10 @@ delivery.
(was pipelock's CA).
- Manifest rename: `bottle.cred_proxy.routes[]`
`bottle.egress_proxy.routes[]`. The route shape gains optional
`path_allowlist: [<prefix>, ...]` and supports `auth_scheme:
"none"`.
`path_allowlist: [<prefix>, ...]` and a nested optional `auth:
{ scheme, token_ref }` block (presence/absence of `auth` is the
authenticated vs unauthenticated signal — replaces the old
`auth_scheme: "none"` pattern).
- Agent's `HTTP_PROXY` / `HTTPS_PROXY` env vars repointed at the
egress-proxy (was pipelock).
- Pipelock retains its sidecar slot and its own DLP + hostname
@@ -151,30 +154,41 @@ default proxy path.
```yaml
egress_proxy:
routes:
# Authenticated route (today's cred-proxy shape, slightly
# renamed). path_allowlist optional.
# Authenticated route — `auth` block carries the injection
# config. path_allowlist optional.
- host: "api.github.com"
auth_scheme: "Bearer"
token_ref: "GH_PAT"
auth:
scheme: "Bearer"
token_ref: "GH_PAT"
path_allowlist:
- "/repos/didericis/"
- "/users/didericis"
# Unauthenticated path-filtered route.
# Unauthenticated path-filtered route — `auth` omitted
# entirely (presence/absence of the key is the auth signal).
- host: "github.com"
auth_scheme: "none"
path_allowlist:
- "/didericis/"
# Bare-pass route: no auth injection, no path enforcement.
# Useful when you want a host to skip path filtering but
# still be DLP-scanned by pipelock.
# Bare-pass route: no auth, no path constraint. Useful when
# you want a host to skip path filtering but still be
# DLP-scanned by pipelock on the outbound leg.
- host: "api.anthropic.com"
auth_scheme: "none"
# no path_allowlist → all paths pass
```
Route matching is on `host` (was `path` prefix). The hostname
gates whether a route applies; `path_allowlist` (if present)
constrains the URL path under that host.
constrains the URL path under that host. The optional `auth`
block carries credential-injection config:
- Omit `auth` → no Authorization header injected (replaces the
earlier draft's `auth_scheme: "none"`).
- `auth.scheme` → one of `Bearer`, `token` (the values
cred-proxy supports today; sidesteps the gitea-token quirk).
- `auth.token_ref` → host env var holding the secret. Same
semantics as cred-proxy's `TokenRef` field today.
Validation: `auth` (if present) must contain both `scheme` and
`token_ref`. An empty `auth: {}` is an error rather than a
synonym for "no auth" — that's what omission is for.
### mitmproxy addon shape
@@ -187,9 +201,10 @@ The egress-proxy ships a small Python addon that:
matches → forward unchanged (pipelock will hostname-gate it). If
route matches and has `path_allowlist`, check `flow.request.path`
against the prefix list; 403 with a clear reason if no match.
- On approved requests: strip inbound Authorization, inject
`Authorization: <auth_scheme> <token-from-env>` if `auth_scheme
!= "none"`.
- On approved requests: strip inbound Authorization. If the route
carries an `auth` block, inject `Authorization: <auth.scheme>
<token-from-env-named-by-auth.token_ref>`. If the route omits
`auth`, leave Authorization unset.
- SIGHUP / file-mtime watch on `routes.yaml` for hot-reload (same
cadence as today's cred-proxy SIGHUP path).