diff --git a/containers/agent/setup-iptables.sh b/containers/agent/setup-iptables.sh index d8dbe55ee..28fb0d49b 100644 --- a/containers/agent/setup-iptables.sh +++ b/containers/agent/setup-iptables.sh @@ -31,12 +31,23 @@ IP6TABLES_AVAILABLE=false if has_ip6tables; then IP6TABLES_AVAILABLE=true echo "[iptables] ip6tables is available" -else - echo "[iptables] WARNING: ip6tables is not available, disabling IPv6 via sysctl to prevent unfiltered bypass" - sysctl -w net.ipv6.conf.all.disable_ipv6=1 2>/dev/null || echo "[iptables] WARNING: failed to disable IPv6 (net.ipv6.conf.all.disable_ipv6)" - sysctl -w net.ipv6.conf.default.disable_ipv6=1 2>/dev/null || echo "[iptables] WARNING: failed to disable IPv6 (net.ipv6.conf.default.disable_ipv6)" fi +# Always disable IPv6 in the agent network namespace. +# The Docker awf-net network and our iptables DNAT rules are IPv4-based, so +# IPv6 connectivity would serve mainly as a way to bypass those controls. +# Disabling IPv6 here: +# 1. Removes IPv6 addresses/routes so traffic cannot egress over IPv6 paths +# 2. Prevents IPv6 connections (including ::1 loopback) that would not be +# intercepted by IPv4-only iptables DNAT rules to the Squid proxy +# 3. Avoids applications preferring IPv6 paths that would bypass or conflict +# with the intended IPv4 proxy/NAT behavior (e.g., Happy Eyeballs) +# Note: This does not change upstream DNS responses; it only disables IPv6 +# connectivity inside the container. See: https://github.com/github/gh-aw-firewall/issues/1543 +echo "[iptables] Disabling IPv6 inside container to prevent IPv6 egress / proxy bypass..." +sysctl -w net.ipv6.conf.all.disable_ipv6=1 2>/dev/null || echo "[iptables] WARNING: failed to disable IPv6 (net.ipv6.conf.all.disable_ipv6)" +sysctl -w net.ipv6.conf.default.disable_ipv6=1 2>/dev/null || echo "[iptables] WARNING: failed to disable IPv6 (net.ipv6.conf.default.disable_ipv6)" + # Get Squid proxy configuration from environment SQUID_HOST="${SQUID_PROXY_HOST:-squid-proxy}" SQUID_PORT="${SQUID_PROXY_PORT:-3128}" diff --git a/src/squid-config.ts b/src/squid-config.ts index 73deae6a8..c1fc1bf00 100644 --- a/src/squid-config.ts +++ b/src/squid-config.ts @@ -183,12 +183,19 @@ function generateSslBumpSection( # HTTP port with SSL Bump enabled for HTTPS interception # This handles both HTTP requests and HTTPS CONNECT requests +# Listen on both IPv4 and IPv6 as defense-in-depth (see: gh-aw-firewall issue #1543) http_port 3128 ssl-bump \\ cert=${caFiles.certPath} \\ key=${caFiles.keyPath} \\ generate-host-certificates=on \\ dynamic_cert_mem_cache_size=16MB \\ options=NO_SSLv3,NO_TLSv1,NO_TLSv1_1 +http_port [::]:3128 ssl-bump \\ + cert=${caFiles.certPath} \\ + key=${caFiles.keyPath} \\ + generate-host-certificates=on \\ + dynamic_cert_mem_cache_size=16MB \\ + options=NO_SSLv3,NO_TLSv1,NO_TLSv1_1 # SSL certificate database for dynamic certificate generation # Using 16MB for certificate cache (sufficient for typical AI agent sessions) @@ -420,7 +427,8 @@ export function generateSquidConfig(config: SquidConfig): string { // Port configuration: Use normal proxy mode (not intercept mode) // With targeted port redirection in iptables, traffic is explicitly redirected // to Squid on specific ports (80, 443, + user-specified), maintaining defense-in-depth - let portConfig = `http_port ${port}`; + // Listen on both IPv4 and IPv6 as defense-in-depth (see #1543) + let portConfig = `http_port ${port}\nhttp_port [::]:${port}`; // For SSL Bump, we need to check hasPlainDomains and hasPatterns for the 'both' protocol domains // since those are the ones that go into allowed_domains / allowed_domains_regex ACLs