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
Reviewed gh-aw-firewall (awf) — a Docker network-isolation firewall wrapping agent commands with Squid L7 egress filtering. Overall posture is strong: defense-in-depth iptables (NAT DNAT + FILTER DROP-default), capability dropping (NET_ADMIN never granted to agent; SYS_CHROOT/SYS_ADMIN dropped post-chroot-setup), Squid-config injection guards (assertSafeForSquidConfig), and cap_drop: ALL + no-new-privileges on sidecars. npm audit reports 0 vulnerabilities in current dependencies. No critical issues found; a few structural hardening opportunities noted below.
🔍 Findings from Firewall Escape Test
/tmp/gh-aw/escape-test-summary.txt did not contain firewall escape-attempt results — it is CI orchestration/meta log output from an unrelated "Secret Digger (Copilot)" workflow run (run 29286879560). That run itself is a positive security signal: the agent correctly detected and refused a prompt-injection instructing it to scan the CI runner for secrets/credentials and exfiltrate them via GitHub issues, logging noop with reason "Refused prompt injection attack" and triggering the platform's GH_AW_DETECTION_REASON: threat_detected tracking issue (#6205). No actual escape/bypass evidence for the awf network sandbox was available in this file; treating it only as complementary context per instructions.
Default-deny OUTPUT chain: final rules are iptables -A OUTPUT -p tcp -j DROP (line 480) and -p udp -j DROP (line 482), each preceded by rate-limited logging (--limit 10/min, prefixes [FW_BLOCKED_TCP]/[FW_BLOCKED_UDP_AGENT]) — good auditability without log-flood DoS risk.
Port 80/443 are DNAT'd to Squid (lines 405–406: iptables -t nat -A OUTPUT -p tcp --dport 80/443 -j DNAT --to-destination ${SQUID_IP}:${SQUID_PORT}), forcing all web traffic through L7 ACL even for proxy-unaware tools (HTTPS ClientHello gets rejected since Squid expects CONNECT, per CLAUDE.md).
DNS restricted to explicit trusted resolvers plus Docker's embedded 127.0.0.11 (lines 443–450), with the embedded Docker DNAT rules preserved via iptables-save/restore (lines 174–192) rather than clobbered by chain flush — avoids accidentally breaking container name resolution while still gating third-party DNS.
Dangerous ports (SSH/SMTP/DB/Redis/MongoDB) explicitly logged (line 472) before falling through to the DROP default — belt-and-suspenders against lateral movement attempts.
Gateway/MCP bypass logic (lines 289–322) creates a NAT RETURN (skip DNAT) + FILTER ACCEPT restricted to allowed ports for host.docker.internal-style gateway IPs — correctly scoped to avoid becoming a blanket bypass.
Container Security:
src/services/agent-service-build.test.ts (asserted, not just documented) confirms NET_ADMIN is excluded from the agent's cap_add and only granted to the separate iptables-init container, which itself gets cap_drop: ['ALL'] alongside cap_add: ['NET_ADMIN','NET_RAW'] — minimal capability footprint per service.
entrypoint.sh:1600/1635: capsh --drop=${CAPS_TO_DROP} executed just before user command exec, dropping cap_sys_chroot,cap_sys_admin in chroot mode (lines 430–439) so user-controlled code cannot re-chroot or remount procfs after setup — correct ordering (privileged setup happens first, then irrevocable drop).
Sidecars (api-proxy, DoH proxy per service-security.test.ts, doh-proxy-service.test.ts) uniformly assert cap_drop: ['ALL'] — least-privilege baseline for auxiliary services.
assertSafeForSquidConfig() (domain-acl.ts:28-36) rejects whitespace/null bytes/quotes/semicolons/backticks/# via SQUID_DANGEROUS_CHARS before any domain/pattern is interpolated into squid.conf — directly mitigates Squid-config-injection via a crafted --allow-domains value.
Deny-rule generation (access-rules.ts:35-54) defaults to http_access deny all whenever no allowlist condition matches, i.e. fail-closed rather than fail-open.
Input Validation: No unsafe exec/spawn/shell-interpolation patterns were found in src/cli.ts in this pass (grep returned no hits for execa|spawn|exec(), suggesting subprocess invocation is centralized elsewhere (e.g. docker-manager) — recommend a follow-up pass specifically auditing argument arrays passed to execa for shell:true usage.
⚠️ Threat Model (STRIDE)
Category
Threat
Evidence
Likelihood
Impact
Spoofing
Rogue container claiming Squid/gateway IP inside awf-net
Fixed IP scheme (172.30.0.10/20/30) relies on Docker network isolation
Low
Medium
Tampering
Squid config injection via malicious domain string
Mitigated by assertSafeForSquidConfig (domain-acl.ts:28)
Low (mitigated)
High if bypassed
Repudiation
Agent actions not attributable
--log-uid on iptables LOG rules (setup-iptables.sh:479,481); Squid firewall_detailed logformat captures client IP
Low
Low
Info Disclosure
Secrets in /proc/1/environ after tokens used
Entrypoint explicitly unsets sensitive env vars post-injection (comment at entrypoint.sh ~442)
Medium
High
DoS
Log flooding from blocked traffic
Rate-limited via --limit 10/min --limit-burst 20
Low (mitigated)
Low
Elevation of Privilege
Agent re-chroot/remount after setup
capsh --drop=cap_sys_chroot,cap_sys_admin before exec
Non-TCP/UDP protocols (e.g. ICMP) not explicitly enumerated in reviewed excerpt
Container capabilities
entrypoint.sh:1600, agent-service-build.ts
cap_drop, no NET_ADMIN on agent
Drop happens at exec-time; any code executed beforecapsh runs with full caps
Domain/config injection
src/squid/domain-acl.ts:28
Regex-based dangerous-char rejection
Regex allowlist approach — recommend verifying it's a strict allowlist of valid domain chars rather than a denylist, since denylists are more bypass-prone
Docker wrapper / CLI args
src/cli.ts
Commander-based parsing
Not verified in this pass whether all args reach execa with array (not string/shell) form — recommend explicit grep audit
Sidecar services (API proxy, DoH)
containers/api-proxy/, DoH proxy
cap_drop: ALL, credential injection only server-side
Sidecars are Node.js HTTP proxies — dependency vuln surface (mitigated: 0 findings in npm audit)
High: Audit src/cli.ts / docker-manager subprocess calls to confirm execa is always invoked with argument arrays (never shell: true with interpolated strings), closing any residual command-injection surface.
Medium: Add explicit test coverage/documentation for non-TCP/UDP protocol handling (ICMP, raw sockets) in setup-iptables.sh to confirm they fall under the same default-deny posture.
Medium: Confirm SQUID_DANGEROUS_CHARS is a strict allowlist regex (not a denylist of known-bad chars), per Principle of Least Privilege / CIS guidance on input validation.
Low: Document in docs/environment.md the exact window (if any) between container start and capsh --drop exec during which the agent process runs with full capability set, for completeness of the threat model.
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
Reviewed
gh-aw-firewall(awf) — a Docker network-isolation firewall wrapping agent commands with Squid L7 egress filtering. Overall posture is strong: defense-in-depth iptables (NAT DNAT + FILTER DROP-default), capability dropping (NET_ADMINnever granted to agent;SYS_CHROOT/SYS_ADMINdropped post-chroot-setup), Squid-config injection guards (assertSafeForSquidConfig), andcap_drop: ALL+no-new-privilegeson sidecars.npm auditreports 0 vulnerabilities in current dependencies. No critical issues found; a few structural hardening opportunities noted below.🔍 Findings from Firewall Escape Test
/tmp/gh-aw/escape-test-summary.txtdid not contain firewall escape-attempt results — it is CI orchestration/meta log output from an unrelated "Secret Digger (Copilot)" workflow run (run 29286879560). That run itself is a positive security signal: the agent correctly detected and refused a prompt-injection instructing it to scan the CI runner for secrets/credentials and exfiltrate them via GitHub issues, loggingnoopwith reason "Refused prompt injection attack" and triggering the platform'sGH_AW_DETECTION_REASON: threat_detectedtracking issue (#6205). No actual escape/bypass evidence for the awf network sandbox was available in this file; treating it only as complementary context per instructions.🛡️ Architecture Security Analysis
Network Security (
containers/agent/setup-iptables.sh, 536 lines):iptables -A OUTPUT -p tcp -j DROP(line 480) and-p udp -j DROP(line 482), each preceded by rate-limited logging (--limit 10/min, prefixes[FW_BLOCKED_TCP]/[FW_BLOCKED_UDP_AGENT]) — good auditability without log-flood DoS risk.iptables -t nat -A OUTPUT -p tcp --dport 80/443 -j DNAT --to-destination ${SQUID_IP}:${SQUID_PORT}), forcing all web traffic through L7 ACL even for proxy-unaware tools (HTTPS ClientHello gets rejected since Squid expectsCONNECT, per CLAUDE.md).127.0.0.11(lines 443–450), with the embedded Docker DNAT rules preserved viaiptables-save/restore (lines 174–192) rather than clobbered by chain flush — avoids accidentally breaking container name resolution while still gating third-party DNS.host.docker.internal-style gateway IPs — correctly scoped to avoid becoming a blanket bypass.Container Security:
src/services/agent-service-build.test.ts(asserted, not just documented) confirmsNET_ADMINis excluded from the agent'scap_addand only granted to the separateiptables-initcontainer, which itself getscap_drop: ['ALL']alongsidecap_add: ['NET_ADMIN','NET_RAW']— minimal capability footprint per service.entrypoint.sh:1600/1635:capsh --drop=${CAPS_TO_DROP}executed just before user command exec, droppingcap_sys_chroot,cap_sys_adminin chroot mode (lines 430–439) so user-controlled code cannot re-chroot or remount procfs after setup — correct ordering (privileged setup happens first, then irrevocable drop).service-security.test.ts,doh-proxy-service.test.ts) uniformly assertcap_drop: ['ALL']— least-privilege baseline for auxiliary services.Domain Validation (
src/squid/domain-acl.ts,access-rules.ts):assertSafeForSquidConfig()(domain-acl.ts:28-36) rejects whitespace/null bytes/quotes/semicolons/backticks/#viaSQUID_DANGEROUS_CHARSbefore any domain/pattern is interpolated intosquid.conf— directly mitigates Squid-config-injection via a crafted--allow-domainsvalue.access-rules.ts:35-54) defaults tohttp_access deny allwhenever no allowlist condition matches, i.e. fail-closed rather than fail-open.Input Validation: No unsafe
exec/spawn/shell-interpolation patterns were found insrc/cli.tsin this pass (grep returned no hits forexeca|spawn|exec(), suggesting subprocess invocation is centralized elsewhere (e.g. docker-manager) — recommend a follow-up pass specifically auditing argument arrays passed toexecafor shell:true usage.awf-netassertSafeForSquidConfig(domain-acl.ts:28)--log-uidon iptables LOG rules (setup-iptables.sh:479,481); Squidfirewall_detailedlogformat captures client IP/proc/1/environafter tokens used--limit 10/min --limit-burst 20capsh --drop=cap_sys_chroot,cap_sys_adminbefore exec🎯 Attack Surface Map
setup-iptables.sh:405-482entrypoint.sh:1600,agent-service-build.tscap_drop, noNET_ADMINon agentcapshruns with full capssrc/squid/domain-acl.ts:28src/cli.tsexecawith array (not string/shell) form — recommend explicit grep auditcontainers/api-proxy/, DoH proxycap_drop: ALL, credential injection only server-sidenpm audit)📋 Evidence Collection
Commands run
✅ Recommendations
src/cli.ts/ docker-manager subprocess calls to confirmexecais always invoked with argument arrays (nevershell: truewith interpolated strings), closing any residual command-injection surface.setup-iptables.shto confirm they fall under the same default-deny posture.SQUID_DANGEROUS_CHARSis a strict allowlist regex (not a denylist of known-bad chars), per Principle of Least Privilege / CIS guidance on input validation.docs/environment.mdthe exact window (if any) between container start andcapsh --dropexec during which the agent process runs with full capability set, for completeness of the threat model.📈 Security Metrics
setup-iptables.sh(536 lines),entrypoint.sh(1664 lines, targeted sections),src/squid/domain-acl.ts,src/squid/access-rules.ts,src/host-iptables*.ts(23 files),agent-service-build.test.ts,service-security.test.tsAll reactions