feat(mitmproxy): wire the sidecar into the bottle launch lifecycle
Second step of PRD 0005. The mitmproxy sidecar from the previous commit now actually runs alongside pipelock when a bottle launches. - BottleBackend gains a non-abstract provision_ca with a default no-op so non-Docker backends aren't forced to implement TLS interception. provision() orchestrates ca → prompt → skills → ssh → git; CA goes first so trust is set up before anything else runs inside the agent. - DockerBottlePlan gains `mitmproxy_plan: MitmproxyProxyPlan`. The prepare step builds it alongside the existing pipelock plan; no new manifest schema or host-side scratch files. - DockerBottleBackend grows self._mitm, threads it through prepare and launch. Mirror of the existing self._proxy pattern. - launch.py brings the mitmproxy sidecar up between pipelock and the agent container, passing pipelock's service-name URL via env. ExitStack callback handles teardown in reverse order. - The agent's HTTPS_PROXY / HTTP_PROXY now point at mitmproxy (not pipelock directly). Three new -e flags inject the CA trust trio (NODE_EXTRA_CA_CERTS / SSL_CERT_FILE / REQUESTS_CA_BUNDLE) at docker run time; Docker propagates those into docker exec so the claude process sees them without per-exec threading. - New provisioner backend/docker/provision/ca.py extracts the CA cert from the running mitmproxy sidecar, copies it into the agent at /usr/local/share/ca-certificates/claude-bottle-mitm.crt, runs update-ca-certificates, and emits a stderr line with the SHA-256 fingerprint (stdlib ssl + hashlib; no subprocess). Cleanup needs no change — `docker ps --filter name=^claude-bottle-` already catches the new claude-bottle-mitm-<slug> containers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -204,24 +204,35 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]):
|
||||
"""Build/run the bottle and yield a handle; tear down on exit."""
|
||||
|
||||
def provision(self, plan: PlanT, target: str) -> str | None:
|
||||
"""Copy host-side files (prompt, skills, SSH keys, .git) into
|
||||
the running bottle. Called from `launch` after the container/
|
||||
machine is up. `target` identifies the running instance in
|
||||
backend-specific terms (Docker: resolved container name; fly:
|
||||
machine id). Returns the in-container prompt path if a prompt
|
||||
was provisioned, else None — the Bottle handle uses it to
|
||||
decide whether to add --append-system-prompt-file to claude's
|
||||
argv.
|
||||
"""Copy host-side files (CA cert, prompt, skills, SSH keys,
|
||||
.git) into the running bottle. Called from `launch` after the
|
||||
container/machine is up. `target` identifies the running
|
||||
instance in backend-specific terms (Docker: resolved container
|
||||
name; fly: machine id). Returns the in-container prompt path
|
||||
if a prompt was provisioned, else None — the Bottle handle
|
||||
uses it to decide whether to add --append-system-prompt-file
|
||||
to claude's argv.
|
||||
|
||||
Default orchestration: prompt → skills → ssh → git. Subclasses
|
||||
typically don't override this; they implement the four
|
||||
sub-methods below."""
|
||||
Default orchestration: ca → prompt → skills → ssh → git.
|
||||
CA goes first because it changes how the agent process trusts
|
||||
the network; the rest don't depend on it but the order keeps
|
||||
trust setup adjacent to the launch step. Subclasses typically
|
||||
don't override this; they implement the sub-methods below."""
|
||||
self.provision_ca(plan, target)
|
||||
prompt_path = self.provision_prompt(plan, target)
|
||||
self.provision_skills(plan, target)
|
||||
self.provision_ssh(plan, target)
|
||||
self.provision_git(plan, target)
|
||||
return prompt_path
|
||||
|
||||
def provision_ca(self, plan: PlanT, target: str) -> None:
|
||||
"""Install the egress-proxy's CA into the running bottle's
|
||||
trust store. Default impl is a no-op so backends that don't
|
||||
yet support TLS interception (every backend except Docker
|
||||
today) aren't forced to implement it. The Docker backend
|
||||
overrides to extract mitmproxy's CA and run
|
||||
`update-ca-certificates` inside the agent container."""
|
||||
|
||||
@abstractmethod
|
||||
def provision_prompt(self, plan: PlanT, target: str) -> str | None:
|
||||
"""Copy the prompt file into the running bottle. Returns the
|
||||
|
||||
Reference in New Issue
Block a user