# Per-bottle egress-proxy sidecar image (PRD 0017). # # Replaces cred-proxy (PRD 0010). Sits on the agent's HTTP_PROXY / # HTTPS_PROXY path (wiring lands in chunk 2) and owns three jobs: # 1. MITM HTTPS using the per-bottle CA (chunk 2 moves the CA # generation from pipelock). # 2. Enforce manifest-declared path_allowlist per route. # 3. Inject Authorization headers for routes that declare an auth # block. # # Chunk 1 of PRD 0017 ships this image and the addon. Wiring it # into the bottle launch (and the per-bottle CA + the pipelock # upstream proxy) is chunk 2. # mitmproxy base image. mitmdump + addon API are already there; we # only need to drop our addon in. TODO: pin by digest. FROM mitmproxy/mitmproxy:11.1.3 USER root # The addon ships as two files. `_core.py` is pure-logic, importable # both inside the container and from the host's tests; `_addon.py` is # the mitmproxy hook wrapper. Both land flat in /app/ so mitmdump's # loader finds them as top-level sibling modules. COPY claude_bottle/egress_proxy_addon_core.py /app/egress_proxy_addon_core.py COPY claude_bottle/egress_proxy_addon.py /app/egress_proxy_addon.py # Pre-create the runtime directories the backend's start step will # `docker cp` into. docker cp does not create intermediate dirs, so # the mkdir must be baked into the image. # /etc/egress-proxy routes.yaml lands here # ~/.mitmproxy mitmproxy CA (cert+key concat) + the # pipelock CA (cert only, for upstream # trust on the HTTPS_PROXY=pipelock leg) # Ownership lets the unprivileged mitmproxy user read the files. RUN mkdir -p /etc/egress-proxy /home/mitmproxy/.mitmproxy \ && chown -R mitmproxy:mitmproxy /etc/egress-proxy /home/mitmproxy/.mitmproxy /app USER mitmproxy # Listening port. Agents dial egress-proxy on this port via their # HTTP_PROXY env. Surfaced as EXPOSE for documentation; not required # for the internal network to route to it. EXPOSE 9099 # Entrypoint: # - Build a combined upstream-trust bundle when # EGRESS_PROXY_UPSTREAM_CA is set (the backend's start step # sets it to the in-container pipelock-CA path). # `--set ssl_verify_upstream_trusted_ca` REPLACES mitmproxy's # default trust store with the file we point it at; if we just # pointed it at pipelock's CA, mitmproxy would refuse any host # pipelock passes through (api.anthropic.com etc.) because # pipelock's CA doesn't sign the real upstream certs. So # concatenate the system bundle + pipelock CA into one PEM and # point mitmproxy at that — covers both pipelock-MITM'd and # pipelock-passthrough hosts. # - --mode regular@9099 → standard HTTP/HTTPS forward proxy. # - -s /app/egress_proxy_addon.py → loads our addon, reads # /etc/egress-proxy/routes.yaml. # Standalone runs (no EGRESS_PROXY_UPSTREAM_CA) skip the bundle # build and use mitmproxy's default trust store. ENTRYPOINT ["sh", "-c", "if [ -n \"$EGRESS_PROXY_UPSTREAM_CA\" ] && [ -f \"$EGRESS_PROXY_UPSTREAM_CA\" ]; then COMBINED=/home/mitmproxy/.mitmproxy/combined-trust.pem; cat /etc/ssl/certs/ca-certificates.crt \"$EGRESS_PROXY_UPSTREAM_CA\" > \"$COMBINED\"; exec mitmdump --mode regular@9099 --set ssl_verify_upstream_trusted_ca=\"$COMBINED\" -s /app/egress_proxy_addon.py; else exec mitmdump --mode regular@9099 -s /app/egress_proxy_addon.py; fi"]