You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This security review covers a comprehensive, evidence-based analysis of the gh-aw-firewall codebase as of 2026-04-01. The firewall implements a solid defense-in-depth architecture with multiple independent security layers. One Critical finding was discovered (Squid config injection via newline in URL patterns). The overall security posture is good, with well-implemented iptables filtering, capability dropping, seccomp profiles, and Docker socket hardening. The most significant risk surface is the SSL Bump / --allow-urls feature path.
~4,000+ lines of security-critical code analyzed across 6 source files
🔍 Findings from Firewall Escape Test Agent
No "firewall-escape-test" workflow was found in the repository's workflow listing. The closest related workflows are security-review (daily) and security-guard (PR-triggered). The absence of an automated escape-test agent means active adversarial testing of the firewall (e.g., DNS tunneling, ICMP exfil, HTTP smuggling attempts) is not continuously exercised in CI.
🛡️ Architecture Security Analysis
Network Security Assessment
Strengths:
Dual-layer enforcement: iptables DNAT redirects all TCP port 80/443 traffic to Squid, then Squid enforces domain ACLs. Either layer independently blocks disallowed traffic.
Direct IP blocking in Squid: dst_ipv4 and dst_ipv6 ACLs block CONNECT requests to raw IP addresses, preventing domain-name bypass via direct IP.
Dangerous port blacklist: Ports 22 (SSH), 25 (SMTP), 3306 (MySQL), 5432 (PostgreSQL), 6379 (Redis), 27017 (MongoDB), and others have explicit NAT RETURN + OUTPUT DROP rules.
DNS exfiltration prevention: Only configured DNS servers (default 8.8.8.8,8.8.4.4) are allowed; all other UDP port 53 traffic is dropped. Docker embedded DNS (127.0.0.11) handles container name resolution.
IPv6 disabled at init: Both host (src/host-iptables.ts) and container (containers/agent/setup-iptables.sh:48-49) disable IPv6 via sysctl when ip6tables is unavailable, preventing IPv6 as a bypass path.
Defense-in-depth for non-proxy-aware tools: iptables DNAT catches HTTP/HTTPS from tools that ignore HTTP_PROXY/HTTPS_PROXY env vars.
Weakness — ICMP not explicitly blocked in container OUTPUT chain:
The container's OUTPUT filter chain (setup-iptables.sh lines 411-418) only adds DROP rules for TCP and UDP. ICMP traffic is not explicitly blocked. While NET_RAW is dropped from the agent container (preventing raw socket creation), ICMP echo requests could still be sent via the standard ping binary if it has cap_net_raw set via SUID (which is present in Ubuntu 22.04). The host-level DOCKER-USER chain uses REJECT --reject-with icmp-port-unreachable only on specific REJECT rules, not as a default-deny for ICMP. This creates a potential low-bandwidth covert channel.
# setup-iptables.sh:411-418 — only TCP and UDP are dropped
iptables -A OUTPUT -p tcp -j DROP
iptables -A OUTPUT -p udp -j DROP
# ICMP is not mentioned — not blocked in OUTPUT filter
Container Security Assessment
Strengths:
no-new-privileges:true applied to all containers (src/docker-manager.ts:1183, 1395, 1499)
Custom seccomp profile with defaultAction: SCMP_ACT_ERRNO (deny-by-default)
NET_ADMIN is only granted to the awf-iptables-init init container, never to the agent
SYS_CHROOT and SYS_ADMIN are added to agent only for chroot setup, then dropped via capsh before user code runs
NET_RAW dropped from agent — prevents raw socket creation for iptables bypass
Docker socket mapped to /dev/null by default (only exposed with explicit --enable-dind)
Memory limits: agent 6g (default), iptables-init 128m, API proxy 512m
pids_limit: 1000 for agent, 50 for init/Squid containers
Weakness — --enable-dind exposes Docker socket with only a warning:
When --enable-dind is specified, the real /var/run/docker.sock is bind-mounted into the chroot with read-write access. A comment in docker-manager.ts:944 states "agent can run docker commands (firewall bypass possible)" — this is correctly warned about, but the feature makes it trivial to spawn a new container without AWF network restrictions. This should be treated as a complete firewall bypass when used.
Domain Validation Assessment
Strengths:
validateDomainOrPattern() in src/domain-patterns.ts blocks *, *.*, and patterns matching /^[*.]+$/
Wildcard-to-regex conversion uses [a-zA-Z0-9.-]* (not .*) to prevent ReDoS
Protocol-specific restrictions ((domain/redacted) vs (domain/redacted)`) correctly handled
Protocol stripping before validation prevents bypass via protocol prefix manipulation
Input length capped at 512 chars for regex matching (isDomainMatchedByPattern)
Weakness — Multiple wildcard segments check could be bypassed:
The check wildcardSegments >= totalSegments - 1 only blocks patterns where most segments are wildcards. A pattern like *.*.example.com (2 wildcards, 4 segments: 2 >= 4-1=2 >= 3 = false) passes validation. While less than two wildcard segments is generally acceptable, the intent of the check could lead to inconsistent security reasoning.
Input Validation Assessment
Strengths:
Port specs validated with isValidPortSpec() before Squid config and iptables injection
Port values additionally sanitized with port.replace(/[^0-9-]/g, '') before Squid config embedding
Shell arguments escaped via escapeShellArg() using single-quote wrapping with inner single-quote escaping
UID/GID validated as numeric and rejected if 0 (root) in containers/agent/entrypoint.sh:15-33
URL patterns validated for https:// prefix and dangerous broad-patterns before processing
CRITICAL FINDING — Squid config injection via URL pattern newlines:
The parseUrlPatterns() function in src/ssl-bump.ts:337 does not strip or reject newline characters. These patterns are then directly embedded into the generated Squid configuration via generateSslBumpSection() in src/squid-config.ts:154:
Attack scenario: If --allow-urls values originate from user-controlled input (e.g., a CI environment variable, a config file, a GitHub Actions input), an attacker can inject arbitrary Squid directives including http_access allow all, completely disabling domain filtering. The validation in src/cli.ts:1634-1666 checks for dangerous broad patterns but does NOT check for embedded newlines.
⚠️ Threat Model
ID
Category
Threat
Severity
Likelihood
Impact
T1
Tampering
Squid config injection via --allow-urls newline characters
Critical
Medium
Critical — complete firewall bypass
T2
Elevation of Privilege
ICMP data exfiltration through absent OUTPUT ICMP DROP rule
High
Low
Medium — low-bandwidth covert channel
T3
Elevation of Privilege
--enable-dind bypasses firewall entirely via new container spawn
High
Low
Critical — total firewall bypass
T4
Information Disclosure
--env-all leaks all host env vars (including secrets) to agent container
Medium
Medium
High — credential exposure
T5
Information Disclosure
Squid JSONL audit log may contain URL fragments with sensitive data
Medium
Medium
Medium — URL query parameters logged
T6
DoS
Memory exhaustion via agent with 6g limit in shared CI runner
Medium
Low
Medium — runner OOM
T7
Spoofing
Agent can forge Host: header for allowed domains to reach disallowed IPs
Low
Low
Low — blocked by Squid ACL
T8
Repudiation
iptables LOG rate-limited (5/min), may miss burst attacks
Low
Medium
Low — audit gap
T9
Tampering
*.*.example.com domain pattern validation edge case
Impact: Prevents complete Squid domain-filter bypass via config injection.
🟠 High — Should Fix Soon
2. Explicitly drop ICMP in the container OUTPUT filter chain
Add an ICMP DROP rule to containers/agent/setup-iptables.sh after the UDP DROP rule (line 418):
# Block ICMP (prevents low-bandwidth data exfiltration via ping timing/payload)
iptables -A OUTPUT -p icmp -j DROP
This is belt-and-suspenders alongside the already-dropped NET_RAW capability. Ubuntu's ping binary has cap_net_raw+ep file capability, which may be available in the chroot path even after capsh --drop=NET_RAW if the bounding set removal wasn't applied before exec.
Impact: Closes potential ICMP covert channel / data exfiltration path.
🟡 Medium — Plan to Address
3. Add explicit security warning for --enable-dind with firewall bypass documentation
--enable-dind represents a complete, intentional firewall bypass. The current warning in docker-manager.ts:944 is a single logger.warn call. Consider:
Adding a --confirm-dind-bypasses-firewall required flag to make the bypass intent explicit
Documenting in docs/ that DinD completely voids the firewall guarantee
Adding audit log events when DinD is enabled
4. Protect --env-all with secret-name filtering
When --env-all is used, common secret variable names (*_TOKEN, *_KEY, *_SECRET, *_PASSWORD, GITHUB_TOKEN, etc.) should be excluded by default or warned about individually. The current implementation just logs a generic warning.
🔵 Low — Nice to Have
5. Increase iptables LOG rate limits for burst detection
The current --limit 5/min --limit-burst 10 (line 409) and --limit 10/min --limit-burst 20 (lines 415, 417) may miss burst firewall probe patterns. Consider increasing burst limits for better audit coverage.
6. Add explicit ICMP blocking in host-level DOCKER-USER chain
The setupHostIptables() function in src/host-iptables.ts does not add an explicit default-deny for ICMP traffic. While the DOCKER-USER chain is focused on TCP/UDP traffic, adding an explicit ICMP drop from the container network would add defense-in-depth.
7. Document the *.*.example.com wildcard validation edge case
The validateDomainOrPattern() function permits *.*.example.com (2 wildcards, 4 segments) since 2 >= 4-1 is false. This is probably fine (it matches any two-label prefix before .example.com), but should be documented explicitly.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
📊 Executive Summary
This security review covers a comprehensive, evidence-based analysis of the
gh-aw-firewallcodebase as of 2026-04-01. The firewall implements a solid defense-in-depth architecture with multiple independent security layers. One Critical finding was discovered (Squid config injection via newline in URL patterns). The overall security posture is good, with well-implemented iptables filtering, capability dropping, seccomp profiles, and Docker socket hardening. The most significant risk surface is the SSL Bump /--allow-urlsfeature path.Key metrics:
--enable-dindfirewall bypass advisory +--env-allcredential leak risk🔍 Findings from Firewall Escape Test Agent
No "firewall-escape-test" workflow was found in the repository's workflow listing. The closest related workflows are
security-review(daily) andsecurity-guard(PR-triggered). The absence of an automated escape-test agent means active adversarial testing of the firewall (e.g., DNS tunneling, ICMP exfil, HTTP smuggling attempts) is not continuously exercised in CI.🛡️ Architecture Security Analysis
Network Security Assessment
Strengths:
dst_ipv4anddst_ipv6ACLs block CONNECT requests to raw IP addresses, preventing domain-name bypass via direct IP.8.8.8.8,8.8.4.4) are allowed; all other UDP port 53 traffic is dropped. Docker embedded DNS (127.0.0.11) handles container name resolution.src/host-iptables.ts) and container (containers/agent/setup-iptables.sh:48-49) disable IPv6 via sysctl whenip6tablesis unavailable, preventing IPv6 as a bypass path.HTTP_PROXY/HTTPS_PROXYenv vars.Evidence:
Weakness — ICMP not explicitly blocked in container OUTPUT chain:
The container's OUTPUT filter chain (setup-iptables.sh lines 411-418) only adds DROP rules for TCP and UDP. ICMP traffic is not explicitly blocked. While
NET_RAWis dropped from the agent container (preventing raw socket creation), ICMP echo requests could still be sent via the standardpingbinary if it hascap_net_rawset via SUID (which is present in Ubuntu 22.04). The host-levelDOCKER-USERchain usesREJECT --reject-with icmp-port-unreachableonly on specific REJECT rules, not as a default-deny for ICMP. This creates a potential low-bandwidth covert channel.Container Security Assessment
Strengths:
no-new-privileges:trueapplied to all containers (src/docker-manager.ts:1183, 1395, 1499)defaultAction: SCMP_ACT_ERRNO(deny-by-default)NET_ADMINis only granted to theawf-iptables-initinit container, never to the agentSYS_CHROOTandSYS_ADMINare added to agent only for chroot setup, then dropped viacapshbefore user code runsNET_RAWdropped from agent — prevents raw socket creation for iptables bypass/dev/nullby default (only exposed with explicit--enable-dind)6g(default), iptables-init128m, API proxy512mpids_limit: 1000for agent,50for init/Squid containerstmpfsoverlays (MCP logs dir, workDir,/dev/shm)/etc/shadowis not bind-mounted into the chroot (only whitelisted/etcfiles)Evidence — capability dropping in entrypoint.sh:349-350:
Evidence — Docker socket hiding in docker-manager.ts:954:
Weakness —
--enable-dindexposes Docker socket with only a warning:When
--enable-dindis specified, the real/var/run/docker.sockis bind-mounted into the chroot with read-write access. A comment indocker-manager.ts:944states "agent can run docker commands (firewall bypass possible)" — this is correctly warned about, but the feature makes it trivial to spawn a new container without AWF network restrictions. This should be treated as a complete firewall bypass when used.Domain Validation Assessment
Strengths:
validateDomainOrPattern()insrc/domain-patterns.tsblocks*,*.*, and patterns matching/^[*.]+$/[a-zA-Z0-9.-]*(not.*) to prevent ReDoS(domain/redacted) vs(domain/redacted)`) correctly handledisDomainMatchedByPattern)Weakness — Multiple wildcard segments check could be bypassed:
The check
wildcardSegments >= totalSegments - 1only blocks patterns where most segments are wildcards. A pattern like*.*.example.com(2 wildcards, 4 segments:2 >= 4-1=2 >= 3= false) passes validation. While less than two wildcard segments is generally acceptable, the intent of the check could lead to inconsistent security reasoning.Input Validation Assessment
Strengths:
isValidPortSpec()before Squid config and iptables injectionport.replace(/[^0-9-]/g, '')before Squid config embeddingescapeShellArg()using single-quote wrapping with inner single-quote escapingcontainers/agent/entrypoint.sh:15-33https://prefix and dangerous broad-patterns before processingCRITICAL FINDING — Squid config injection via URL pattern newlines:
The
parseUrlPatterns()function insrc/ssl-bump.ts:337does not strip or reject newline characters. These patterns are then directly embedded into the generated Squid configuration viagenerateSslBumpSection()insrc/squid-config.ts:154:Reproduced and verified:
Attack scenario: If
--allow-urlsvalues originate from user-controlled input (e.g., a CI environment variable, a config file, a GitHub Actions input), an attacker can inject arbitrary Squid directives includinghttp_access allow all, completely disabling domain filtering. The validation insrc/cli.ts:1634-1666checks for dangerous broad patterns but does NOT check for embedded newlines.--allow-urlsnewline characters--enable-dindbypasses firewall entirely via new container spawn--env-allleaks all host env vars (including secrets) to agent containerHost:header for allowed domains to reach disallowed IPs*.*.example.comdomain pattern validation edge case🎯 Attack Surface Map
--allow-urlsCLI argsrc/cli.ts:1625-1666,src/ssl-bump.ts:337src/docker-manager.ts:946-956--env-allflagsrc/cli.ts:1269,src/docker-manager.ts:490-510--allow-domainswildcardsrc/domain-patterns.ts:100-200--allow-host-portssrc/host-iptables.ts:448,setup-iptables.sh:176-189AWF_USER_UID/GIDenvcontainers/agent/entrypoint.sh:15-33src/cli.ts:936-954escapeShellArg()single-quote wrappingsrc/host-iptables.ts:363-391containers/agent/setup-iptables.shsrc/squid-config.ts:573-576dst_ipv4/dst_ipv6ACL deny📋 Evidence Collection
Command: Confirm URL pattern injection
Command: Verify injection in generated Squid config
Command: Confirm ICMP not dropped in container OUTPUT
Command: npm audit (no vulnerabilities)
Command: Capability configuration
✅ Recommendations
🔴 Critical — Must Fix Immediately
1. Strip newlines from URL patterns before Squid config injection
In
src/ssl-bump.ts:parseUrlPatterns()(line 337), add newline sanitization:Additionally add validation in
src/cli.tsURL pattern validation loop (around line 1634):Impact: Prevents complete Squid domain-filter bypass via config injection.
🟠 High — Should Fix Soon
2. Explicitly drop ICMP in the container OUTPUT filter chain
Add an ICMP DROP rule to
containers/agent/setup-iptables.shafter the UDP DROP rule (line 418):# Block ICMP (prevents low-bandwidth data exfiltration via ping timing/payload) iptables -A OUTPUT -p icmp -j DROPThis is belt-and-suspenders alongside the already-dropped
NET_RAWcapability. Ubuntu'spingbinary hascap_net_raw+epfile capability, which may be available in the chroot path even aftercapsh --drop=NET_RAWif the bounding set removal wasn't applied before exec.Impact: Closes potential ICMP covert channel / data exfiltration path.
🟡 Medium — Plan to Address
3. Add explicit security warning for
--enable-dindwith firewall bypass documentation--enable-dindrepresents a complete, intentional firewall bypass. The current warning indocker-manager.ts:944is a single logger.warn call. Consider:--confirm-dind-bypasses-firewallrequired flag to make the bypass intent explicitdocs/that DinD completely voids the firewall guarantee4. Protect
--env-allwith secret-name filteringWhen
--env-allis used, common secret variable names (*_TOKEN,*_KEY,*_SECRET,*_PASSWORD,GITHUB_TOKEN, etc.) should be excluded by default or warned about individually. The current implementation just logs a generic warning.🔵 Low — Nice to Have
5. Increase iptables LOG rate limits for burst detection
The current
--limit 5/min --limit-burst 10(line 409) and--limit 10/min --limit-burst 20(lines 415, 417) may miss burst firewall probe patterns. Consider increasing burst limits for better audit coverage.6. Add explicit ICMP blocking in host-level DOCKER-USER chain
The
setupHostIptables()function insrc/host-iptables.tsdoes not add an explicit default-deny for ICMP traffic. While the DOCKER-USER chain is focused on TCP/UDP traffic, adding an explicit ICMP drop from the container network would add defense-in-depth.7. Document the
*.*.example.comwildcard validation edge caseThe
validateDomainOrPattern()function permits*.*.example.com(2 wildcards, 4 segments) since2 >= 4-1is false. This is probably fine (it matches any two-label prefix before.example.com), but should be documented explicitly.📈 Security Metrics
cli.ts,domain-patterns.ts,squid-config.ts,ssl-bump.ts,host-iptables.ts,docker-manager.ts,dlp.ts)entrypoint.sh,setup-iptables.sh)Beta Was this translation helpful? Give feedback.
All reactions