# Per-bottle egress 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 three files. `_core.py` is pure-logic, # importable both inside the container and from the host's tests; # `_addon.py` is the mitmproxy hook wrapper; `yaml_subset.py` is # the stdlib-only YAML parser the addon uses to read routes.yaml. # All three land flat in /app/ so mitmdump's loader resolves them # as top-level sibling modules (absolute imports). COPY claude_bottle/egress_addon_core.py /app/egress_addon_core.py COPY claude_bottle/egress_addon.py /app/egress_addon.py COPY claude_bottle/yaml_subset.py /app/yaml_subset.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 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 /home/mitmproxy/.mitmproxy \ && chown -R mitmproxy:mitmproxy /etc/egress /home/mitmproxy/.mitmproxy /app USER mitmproxy # Listening port. Agents dial egress 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: # - Upstream proxy: when EGRESS_UPSTREAM_PROXY is set, # use mitmproxy's `--mode upstream:URL` to forward all # post-MITM traffic through pipelock. (mitmproxy does NOT # honor HTTPS_PROXY env vars on its outbound side — it's a # proxy server, not a client.) Standalone runs without # EGRESS_UPSTREAM_PROXY fall back to `regular@9099` # direct-to-upstream — useful for unit tests of the image. # - Upstream trust: when EGRESS_UPSTREAM_CA is set, build # a combined trust bundle (system roots + pipelock CA) and # point mitmproxy at it via # `--set ssl_verify_upstream_trusted_ca`. This option REPLACES # mitmproxy's default trust store with the file we point it # at — passing just pipelock's CA would break pipelock- # passthrough hosts (api.anthropic.com etc.) where mitmproxy # sees real upstream certs signed by public CAs. The combined # bundle covers both pipelock-MITM'd and pipelock-passthrough # hosts. # - -s /app/egress_addon.py → loads our addon, reads # /etc/egress/routes.yaml. ENTRYPOINT ["sh", "-c", "MODE=\"--mode regular@9099\"; if [ -n \"$EGRESS_UPSTREAM_PROXY\" ]; then MODE=\"--mode upstream:$EGRESS_UPSTREAM_PROXY --listen-port 9099\"; fi; TRUST_FLAG=\"\"; if [ -n \"$EGRESS_UPSTREAM_CA\" ] && [ -f \"$EGRESS_UPSTREAM_CA\" ]; then COMBINED=/home/mitmproxy/.mitmproxy/combined-trust.pem; cat /etc/ssl/certs/ca-certificates.crt \"$EGRESS_UPSTREAM_CA\" > \"$COMBINED\"; TRUST_FLAG=\"--set ssl_verify_upstream_trusted_ca=$COMBINED\"; fi; exec mitmdump $MODE $TRUST_FLAG -s /app/egress_addon.py"]