fix(macos): supply proxy env only at exec, so Codex keeps its identity token
test / stage-firecracker-inputs (push) Successful in 4s
test / integration-docker (push) Successful in 17s
test / unit (push) Successful in 34s
lint / lint (push) Successful in 46s
Update Quality Badges / update-badges (push) Failing after 35s
test / build-infra (push) Successful in 9m18s
test / integration-firecracker (push) Successful in 1m46s
test / coverage (push) Successful in 2m3s
test / publish-infra (push) Successful in 2m31s
test / stage-firecracker-inputs (push) Successful in 4s
test / integration-docker (push) Successful in 17s
test / unit (push) Successful in 34s
lint / lint (push) Successful in 46s
Update Quality Badges / update-badges (push) Failing after 35s
test / build-infra (push) Successful in 9m18s
test / integration-firecracker (push) Successful in 1m46s
test / coverage (push) Successful in 2m3s
test / publish-infra (push) Successful in 2m31s
`container exec --env` does not override the run-time environment on Apple Container — it appends. The launch path baked a token-less `*_PROXY` into `container run` and relied on the exec-time, token-bearing value superseding it, so the agent's `environ` ended up with two `HTTPS_PROXY` entries, token-less first. Which entry a runtime reads is then luck. Node reads the last, so Claude bottles picked up the token and worked. Rust's `std::env::var` reads the first, so Codex proxied with no identity token at all; `/resolve` requires a matching (source_ip, identity_token) pair and fail-closes, so every request from a Codex bottle was denied — its model calls, its `wss://` reconnects, and its MCP servers alike. The registry row was correct the whole time, which is what made this read as a registration bug. Drop the run-time proxy vars entirely. A token-less proxy URL has no legitimate consumer: the init process is `sleep` and everything that egresses arrives by exec. Its only benefit was a tidy 403 for unattributed callers, which is not worth silently dropping attribution for — and a process that egresses before the exec-time env still fails closed, since the agent network is host-only and the gateway is the only route off it. Also corrects the two comments that asserted the false "exec --env wins" invariant, so the next reader doesn't rebuild the same assumption. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #442.
This commit is contained in:
@@ -20,7 +20,6 @@ from bot_bottle.backend.macos_container.consolidated_launch import GatewayEndpoi
|
||||
from bot_bottle.backend.macos_container.launch import (
|
||||
_agent_run_argv,
|
||||
_identity_proxy_env,
|
||||
_proxy_url,
|
||||
)
|
||||
from bot_bottle.manifest import ManifestIndex
|
||||
|
||||
@@ -104,13 +103,26 @@ class TestAgentRunArgv(unittest.TestCase):
|
||||
read back after start."""
|
||||
self.assertNotIn("--ip", self.argv)
|
||||
|
||||
def test_run_time_proxy_carries_no_identity_token(self) -> None:
|
||||
"""The token is minted by registration, which happens after this run —
|
||||
so it cannot be here. `/resolve` denies the token-less pair (#366),
|
||||
which is the safe direction; the real value arrives at exec time."""
|
||||
joined = " ".join(self.argv)
|
||||
self.assertIn(f"HTTP_PROXY={_proxy_url('192.168.128.3')}", joined)
|
||||
self.assertNotIn("bottle:", joined)
|
||||
def test_run_time_env_sets_no_proxy_vars_at_all(self) -> None:
|
||||
"""Regression: the proxy vars must exist *only* in the exec-time env.
|
||||
|
||||
`container exec --env` appends rather than replaces, so a token-less
|
||||
`HTTPS_PROXY` here would survive next to the token-bearing one and
|
||||
leave two entries in the agent's `environ`. Resolution is then
|
||||
runtime-specific — Node reads the last, Rust's `std::env::var` reads
|
||||
the first — so Codex proxied without its identity token and every
|
||||
request fail-closed at `/resolve`.
|
||||
"""
|
||||
for entry in self.argv:
|
||||
self.assertFalse(
|
||||
entry.upper().startswith(("HTTP_PROXY=", "HTTPS_PROXY=")),
|
||||
f"run-time env must not set a proxy var, got {entry!r}",
|
||||
)
|
||||
|
||||
def test_run_time_env_carries_no_identity_token(self) -> None:
|
||||
"""The token is minted by registration, which happens after this run,
|
||||
so it cannot be here under any name."""
|
||||
self.assertNotIn("bottle:", " ".join(self.argv))
|
||||
|
||||
def test_gateway_bypasses_the_proxy(self) -> None:
|
||||
"""git-http + supervise live on the gateway and must be reached
|
||||
|
||||
Reference in New Issue
Block a user