fix(proxy): rewrite proxy host for custom network gateways#217
Conversation
Containers on custom Apple/Docker networks (created for services or BuildKit) couldn't reach the credential proxy because proxy URLs used the default network gateway IP, which is unreachable from a different subnet. Add NetworkGateway() to the NetworkManager interface so the run manager can inspect the custom network's actual gateway after creation. When it differs from the default, rewrite all proxy-related env vars to use the correct gateway. Also extract inspectAppleNetworkGateway() shared helper to deduplicate the gateway inspection logic between probeDefaultGateway and NetworkGateway, and constrain replaceHostInEnv to only replace in env var values (not keys).
📦 Previous Review (superseded by newer review)Claude finished @dpup's task in 4m 32s —— View job Code Review
The fix is well-targeted and the implementation is clean. The Two issues worth addressing: 1. Docker for _, cfg := range inspect.IPAM.Config {
if cfg.Gateway != "" {
return cfg.Gateway // could be an IPv6 address
}
}On dual-stack Docker networks, the first IPAM config might be IPv6. Returning for _, cfg := range inspect.IPAM.Config {
if cfg.Gateway != "" {
if ip := net.ParseIP(cfg.Gateway); ip != nil && ip.To4() != nil {
return cfg.Gateway
}
}
}2. Stale doc comment in // NetworkManager returns the network manager if supported, nil otherwise.
// Docker provides this, Apple containers return nil.
NetworkManager() NetworkManagerApple containers now expose a real Minor observation — Docker + BuildKit path The rewrite also fires for Docker BuildKit runs (where Overall: the core fix is correct and the Apple container scenario is well handled. The IPv6 guard in the Docker implementation is the main hardening I'd recommend. |
Filter Docker NetworkGateway to IPv4-only gateways (skip IPv6 from dual-stack networks that would produce malformed proxy URLs), and update stale doc comment that claimed Apple containers return nil for NetworkManager.
📦 Previous Review (superseded by newer review)Claude finished @dpup's task in 4m 2s —— View job Code Review
The fix addresses the real problem correctly. The core logic — create network, inspect its gateway, rewrite proxy env vars — is sound. Two issues worth noting: 1. Rewrite fires unnecessarily on Docker+macOS — On macOS with Docker, This works because the proxy binds to A simple guard limits the rewrite to IP-based host addresses (the only case where the gateway can differ across subnets): if networkID != "" && hostAddr != "" && net.ParseIP(hostAddr) != nil {2. Stale doc comment on // ServiceManager returns the service manager if supported, nil otherwise.
// Docker provides this, Apple containers return nil.
ServiceManager() ServiceManagerApple containers do provide a Minor observation: plain string replacement
Overall: the fix is correct for its primary target (Apple containers on custom networks), the refactoring is clean, and tests are thorough. Issue #1 is a real behavioral change for Docker+macOS that could be guarded cheaply; issue #2 is a pre-existing stale comment worth cleaning up since the PR is already touching that area. |
Only rewrite proxy env vars when hostAddr is an IP address (Apple containers). Docker on macOS uses host.docker.internal which already resolves correctly from any bridge network. Also fix stale ServiceManager doc comment (Apple provides it too).
📦 Previous Review (superseded by newer review)Claude finished @dpup's task in 2m 43s —— View job Code Review
The fix is correct and well-structured. The previous review feedback was addressed: Docker Two issues worth addressing: 1. Rewrite scope is too broad — user env vars and secrets are subject to replacement —
If any of these values contain an IP address that has the gateway as a substring prefix (e.g. The proxy-specific vars that actually need rewriting are a small, known set: 2. Substring replacement can corrupt IPs that share a prefix with the gateway — result[i] = e[:idx+1] + strings.ReplaceAll(e[idx+1:], oldHost, newHost)
For proxy URLs the A simple fix is to replace only exact IP occurrences by anchoring the substitution (e.g. using a regexp like Minor: result := make([]string, len(env)) // len(nil) == 0 → returns []string{}, not nilThe Overall: the fix is correct for its primary target. The address-rewrite approach works for Apple container custom networks. Issues #1 and #2 are the same underlying problem from two angles — the rewrite is broader and less precise than it needs to be. Fixing one (scoping to proxy vars only) would largely eliminate the risk from the other. |
The test hardcoded `docker ps` to verify service container cleanup, which fails on Apple containers where Docker isn't running. Extract a serviceContainerExists helper that tries Docker first, then falls back to the Apple container CLI.
|
Claude finished @dpup's task in 4m 36s —— View job Code Review
The core fix is correct. The previous iteration addressed the main issues: Docker Two open issues from the previous review were not addressed in the latest iteration — noting them here in case they weren't resolved intentionally: 1. Rewrite applies to user-supplied env vars —
Practical risk is low — users very rarely hardcode gateway IPs — but it's worth noting this is a silent corruption path with no log warning. 2. result[i] = e[:idx+1] + strings.ReplaceAll(e[idx+1:], oldHost, newHost)
Combined with issue #1: if a user sets Minor: return strings.Contains(string(out), name)If looking for Overall: The fix works correctly for the primary use case (Apple containers on custom networks). Issues #1 and #2 are the same underlying concern from two angles, only triggered by an unusual combination (user env vars containing the same-subnet gateway IP). If those are knowingly deferred, this is ready to merge. |
Summary
NetworkGateway()toNetworkManagerinterface — inspects a network's actual IPv4 gateway at runtimeHTTP_PROXY,HTTPS_PROXY,NO_PROXY,ANTHROPIC_BASE_URL,MOAT_SSH_TCP_ADDR, etc.) to use the correct gatewayinspectAppleNetworkGateway()shared helper to deduplicate Apple gateway inspection betweenprobeDefaultGatewayandNetworkGatewayreplaceHostInEnvto only replace in env var values (after first=), not keysTest plan
replaceHostInEnv: normal rewrite, no-change, empty input, key-not-replacedmake lintpasses (0 issues)make test-unitpasses (Docker-specific tests excluded — no daemon on dev machine)moat runwith service dependencies on Apple containers — debug logs confirm gateway rewrite (e.g.192.168.64.1 → 192.168.73.1), network traces show 16 requests completing with credential injection🤖 Generated with Claude Code