diff --git a/containers/agent/setup-iptables.sh b/containers/agent/setup-iptables.sh index d349d68f..f8fd2bb9 100644 --- a/containers/agent/setup-iptables.sh +++ b/containers/agent/setup-iptables.sh @@ -121,6 +121,20 @@ fi echo "[iptables] Allow traffic to Squid proxy (${SQUID_IP}:${SQUID_PORT})..." iptables -t nat -A OUTPUT -d "$SQUID_IP" -j RETURN +# Bypass Squid for host.docker.internal when host access is enabled. +# MCP gateway traffic to host.docker.internal gets DNAT'd to Squid, +# where Squid fails with "Invalid URL" because rmcp sends relative URLs. +if [ -n "$AWF_ENABLE_HOST_ACCESS" ]; then + HOST_GATEWAY_IP=$(getent hosts host.docker.internal | awk 'NR==1 { print $1 }') + if [ -n "$HOST_GATEWAY_IP" ]; then + echo "[iptables] Allow direct traffic to host gateway (${HOST_GATEWAY_IP}) - bypassing Squid..." + iptables -t nat -A OUTPUT -d "$HOST_GATEWAY_IP" -j RETURN + iptables -A OUTPUT -d "$HOST_GATEWAY_IP" -j ACCEPT + else + echo "[iptables] WARNING: host.docker.internal could not be resolved, skipping host gateway bypass" + fi +fi + # Block dangerous ports at NAT level (defense-in-depth with Squid ACL filtering) # These ports are explicitly blocked to prevent access to sensitive services # even if Squid ACL filtering fails. The ports RETURN from NAT (not redirected) diff --git a/src/docker-manager.test.ts b/src/docker-manager.test.ts index 541a7d69..e1a5f55e 100644 --- a/src/docker-manager.test.ts +++ b/src/docker-manager.test.ts @@ -971,6 +971,29 @@ describe('docker-manager', () => { expect(agent.extra_hosts).toBeUndefined(); expect(squid.extra_hosts).toBeUndefined(); }); + + it('should set AWF_ENABLE_HOST_ACCESS when enableHostAccess is true', () => { + const config = { ...mockConfig, enableHostAccess: true }; + const result = generateDockerCompose(config, mockNetworkConfig); + const env = result.services.agent.environment as Record; + + expect(env.AWF_ENABLE_HOST_ACCESS).toBe('1'); + }); + + it('should NOT set AWF_ENABLE_HOST_ACCESS when enableHostAccess is false', () => { + const config = { ...mockConfig, enableHostAccess: false }; + const result = generateDockerCompose(config, mockNetworkConfig); + const env = result.services.agent.environment as Record; + + expect(env.AWF_ENABLE_HOST_ACCESS).toBeUndefined(); + }); + + it('should NOT set AWF_ENABLE_HOST_ACCESS when enableHostAccess is undefined', () => { + const result = generateDockerCompose(mockConfig, mockNetworkConfig); + const env = result.services.agent.environment as Record; + + expect(env.AWF_ENABLE_HOST_ACCESS).toBeUndefined(); + }); }); describe('allowHostPorts option', () => { diff --git a/src/docker-manager.ts b/src/docker-manager.ts index ef1602c9..a3088c2a 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -557,6 +557,7 @@ export function generateDockerCompose( // Enable host.docker.internal for agent when --enable-host-access is set if (config.enableHostAccess) { agentService.extra_hosts = ['host.docker.internal:host-gateway']; + environment.AWF_ENABLE_HOST_ACCESS = '1'; } // Use GHCR image or build locally