Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions containers/agent/setup-iptables.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
10 changes: 9 additions & 1 deletion src/squid-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Loading