From 0739a0030984f81438b6de141df786962b739ab0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:11:33 +0000 Subject: [PATCH 1/4] Initial plan From 3cd46b39c560e692c848b4d92031fb00f85774ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:16:12 +0000 Subject: [PATCH 2/4] fix: add topology peers to no_proxy --- .../agent-environment-options.test.ts | 15 +++++++++++++ .../agent-environment/proxy-environment.ts | 21 +++++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/services/agent-environment-options.test.ts b/src/services/agent-environment-options.test.ts index a01af459e..ad2b79e94 100644 --- a/src/services/agent-environment-options.test.ts +++ b/src/services/agent-environment-options.test.ts @@ -484,6 +484,21 @@ describe('agent environment: options', () => { expect(env.NO_PROXY).toContain('host.docker.internal'); }); + it('should append topology peer hostnames to NO_PROXY', () => { + const configWithTopologyPeers = { + ...mockConfig, + networkIsolation: true, + topologyAttach: ['awmg-mcpg', 'awmg-cli-proxy'], + }; + const result = generateDockerCompose(configWithTopologyPeers, mockNetworkConfig); + const agent = result.services.agent; + const env = agent.environment as Record; + + expect(env.NO_PROXY).toContain('awmg-mcpg'); + expect(env.NO_PROXY).toContain('awmg-cli-proxy'); + expect(env.no_proxy).toBe(env.NO_PROXY); + }); + it('should sync no_proxy when --env overrides NO_PROXY', () => { const configWithEnv = { ...mockConfig, diff --git a/src/services/agent-environment/proxy-environment.ts b/src/services/agent-environment/proxy-environment.ts index dd7cdeee3..9a84a656b 100644 --- a/src/services/agent-environment/proxy-environment.ts +++ b/src/services/agent-environment/proxy-environment.ts @@ -10,21 +10,30 @@ interface ProxyEnvironmentParams { export function buildProxyEnvironment(params: ProxyEnvironmentParams): void { const { config, networkConfig, environment } = params; - environment.NO_PROXY = `localhost,127.0.0.1,::1,0.0.0.0,${networkConfig.squidIp},${networkConfig.agentIp}`; - environment.no_proxy = environment.NO_PROXY; + const noProxyEntries = [ + 'localhost', + '127.0.0.1', + '::1', + '0.0.0.0', + networkConfig.squidIp, + networkConfig.agentIp, + ]; if (config.enableHostAccess) { const subnetBase = networkConfig.subnet.split('/')[0]; const parts = subnetBase.split('.'); const networkGatewayIp = `${parts[0]}.${parts[1]}.${parts[2]}.1`; - environment.NO_PROXY += `,host.docker.internal,${networkGatewayIp}`; - environment.no_proxy = environment.NO_PROXY; + noProxyEntries.push('host.docker.internal', networkGatewayIp); } if (config.enableApiProxy && networkConfig.proxyIp) { // Include both IP and Docker service hostname — Node.js undici matches // NO_PROXY against the request hostname string, not the resolved IP. - environment.NO_PROXY += `,${networkConfig.proxyIp},api-proxy`; - environment.no_proxy = environment.NO_PROXY; + noProxyEntries.push(networkConfig.proxyIp, 'api-proxy'); } + + noProxyEntries.push(...(config.topologyAttach || [])); + + environment.NO_PROXY = [...new Set(noProxyEntries)].join(','); + environment.no_proxy = environment.NO_PROXY; } From ce5534fe73e58ed78ed4143dc76d5a5bf6a801d0 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Mon, 20 Jul 2026 09:51:26 -0700 Subject: [PATCH 3/4] fix: scope topology-peer NO_PROXY exemption to compose agents Only exempt topology-attached peers (awmg-mcpg, awmg-cli-proxy) from the agent's proxy routing when the agent actually shares awf-net, i.e. a compose-managed runtime (runc, gVisor). For microVM backends (Docker sbx) the agent runs off awf-net and cannot resolve or route to these container hostnames, so injecting them into NO_PROXY there would turn a Squid 403 into an unroutable direct connection. - Gate the exemption on networkIsolation && runtimeUsesComposeAgent so it applies to gVisor and ARC/DinD compose isolation but not sbx. - Defensively exempt difcProxyHost in case the DIFC/cli-proxy is configured independently of topologyAttach. - Document the hostname-only limitation (peer IPs are unknown at config-write time; MCP URLs address peers by hostname). - Add matrix tests: compose isolation (included), difcProxyHost (included), non-isolation (excluded), sbx microVM (excluded). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 33d45308-d07a-4a8a-887a-dfe16c75efa4 --- .../proxy-environment.test.ts | 46 +++++++++++++++++++ .../agent-environment/proxy-environment.ts | 32 ++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/services/agent-environment/proxy-environment.test.ts b/src/services/agent-environment/proxy-environment.test.ts index ffae83928..f2caa443b 100644 --- a/src/services/agent-environment/proxy-environment.test.ts +++ b/src/services/agent-environment/proxy-environment.test.ts @@ -35,4 +35,50 @@ describe('buildProxyEnvironment', () => { expect(env.NO_PROXY.split(',')).toContain('172.30.0.1'); expect(env.NO_PROXY.split(',')).toContain('host.docker.internal'); }); + + describe('topology-attached peers', () => { + it('exempts topology peers from proxy routing for a compose agent in isolation mode', () => { + const env = run({ + ...baseConfig, + networkIsolation: true, + topologyAttach: ['awmg-mcpg', 'awmg-cli-proxy'], + }); + expect(env.NO_PROXY.split(',')).toContain('awmg-mcpg'); + expect(env.NO_PROXY.split(',')).toContain('awmg-cli-proxy'); + expect(env.no_proxy).toBe(env.NO_PROXY); + }); + + it('exempts the DIFC/cli-proxy host even when not listed in topologyAttach', () => { + const env = run({ + ...baseConfig, + networkIsolation: true, + difcProxyHost: 'awmg-cli-proxy', + }); + expect(env.NO_PROXY.split(',')).toContain('awmg-cli-proxy'); + }); + + it('does NOT exempt topology peers outside network-isolation mode', () => { + const env = run({ + ...baseConfig, + networkIsolation: false, + topologyAttach: ['awmg-mcpg'], + }); + expect(env.NO_PROXY.split(',')).not.toContain('awmg-mcpg'); + }); + + it('does NOT exempt topology peers for a microVM (sbx) agent', () => { + // The sbx microVM runs off awf-net and cannot resolve or route to these + // container hostnames; NO_PROXY there would break the connection rather + // than fix it, so peers are deliberately omitted for microVM backends. + const env = run({ + ...baseConfig, + containerRuntime: 'sbx', + networkIsolation: true, + topologyAttach: ['awmg-mcpg'], + difcProxyHost: 'awmg-cli-proxy', + }); + expect(env.NO_PROXY.split(',')).not.toContain('awmg-mcpg'); + expect(env.NO_PROXY.split(',')).not.toContain('awmg-cli-proxy'); + }); + }); }); diff --git a/src/services/agent-environment/proxy-environment.ts b/src/services/agent-environment/proxy-environment.ts index 5f017dd13..83001d930 100644 --- a/src/services/agent-environment/proxy-environment.ts +++ b/src/services/agent-environment/proxy-environment.ts @@ -1,7 +1,7 @@ import { WrapperConfig } from '../../types'; import { NetworkConfig } from '../squid-service'; import { buildNoProxyValue } from '../no-proxy-utils'; -import { runtimeUsesIptables } from '../../container-runtime'; +import { runtimeUsesIptables, runtimeUsesComposeAgent } from '../../container-runtime'; interface ProxyEnvironmentParams { config: WrapperConfig; @@ -12,7 +12,35 @@ interface ProxyEnvironmentParams { export function buildProxyEnvironment(params: ProxyEnvironmentParams): void { const { config, networkConfig, environment } = params; - const noProxyHosts: string[] = ['0.0.0.0', networkConfig.squidIp, networkConfig.agentIp, ...(config.topologyAttach || [])]; + const noProxyHosts: string[] = ['0.0.0.0', networkConfig.squidIp, networkConfig.agentIp]; + + // Network-isolation (topology) mode has no iptables-init container, so the + // only lever to keep intra-`awf-net` peer traffic out of Squid is NO_PROXY. + // Exempt topology-attached peers (e.g. the MCP gateway `awmg-mcpg` and the + // DIFC/cli-proxy `awmg-cli-proxy`) so proxy-aware clients (rmcp/undici) + // connect to them directly instead of being routed through Squid and denied. + // + // Only meaningful when the agent actually shares `awf-net`, i.e. a + // compose-managed agent (runc, gVisor). For microVM backends (Docker sbx) the + // agent runs off `awf-net` and cannot resolve or route to these container + // hostnames — adding them to NO_PROXY there would turn a Squid 403 into an + // unroutable direct connection, so they are deliberately skipped. sbx reaches + // gateway peers through its own proxy-chaining path, not NO_PROXY. + // + // Hostname-only by design: peer IPs are not known at config-write time (they + // are discovered after the network attach in cli-workflow's onNetworkReady), + // and MCP URLs address peers by hostname (e.g. http://awmg-mcpg:8080), which + // is what undici matches NO_PROXY against. + if (config.networkIsolation && runtimeUsesComposeAgent(config.containerRuntime)) { + if (config.topologyAttach) { + noProxyHosts.push(...config.topologyAttach); + } + // The DIFC/cli-proxy is normally also listed in topologyAttach, but exempt + // it explicitly in case it is configured independently. + if (config.difcProxyHost) { + noProxyHosts.push(config.difcProxyHost); + } + } // The MCP gateway is served on the network gateway (e.g. 172.30.0.1). In // standard mode an iptables NAT RETURN rule bypasses Squid for it. Runtimes From 1206d69d772a9cf0e30ef7c8c53511c78ea6ec56 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Mon, 20 Jul 2026 09:58:07 -0700 Subject: [PATCH 4/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/services/agent-environment/proxy-environment.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/services/agent-environment/proxy-environment.ts b/src/services/agent-environment/proxy-environment.ts index 83001d930..885153dfa 100644 --- a/src/services/agent-environment/proxy-environment.ts +++ b/src/services/agent-environment/proxy-environment.ts @@ -35,11 +35,6 @@ export function buildProxyEnvironment(params: ProxyEnvironmentParams): void { if (config.topologyAttach) { noProxyHosts.push(...config.topologyAttach); } - // The DIFC/cli-proxy is normally also listed in topologyAttach, but exempt - // it explicitly in case it is configured independently. - if (config.difcProxyHost) { - noProxyHosts.push(config.difcProxyHost); - } } // The MCP gateway is served on the network gateway (e.g. 172.30.0.1). In