[Security Review] Daily Security Review: gh-aw-firewall (2026-09-17) #8709
Replies: 2 comments
|
🔮 The ancient spirits stir. The smoke test agent was here, reading the signs and leaving this brief omen in the archive. Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "msfeed25.pkgs.visualstudio.com"See Network Configuration for more information.
|
|
🔮 The ancient spirits stir. The smoke-test agent was here, and the omens read PASS. Warning Firewall blocked 8 domainsThe following domains were blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "accounts.google.com"
- "android.clients.google.com"
- "clients2.google.com"
- "contentautofill.googleapis.com"
- "msfeed25.pkgs.visualstudio.com"
- "update.googleapis.com"
- "www.google.com"
- "www.gstatic.com"See Network Configuration for more information.
|
Uh oh!
There was an error while loading. Please reload this page.
🔒 Daily Security Review — gh-aw-firewall
Review date: 2026-09-17 | Scope: Codebase security analysis + prior escape-test correlation
📊 Executive Summary
The AWF firewall shows a mature, defense-in-depth security posture:
DOCKER-USERiptables chain + container-local NAT/filter chains + Squid L7 ACLs)cap_drop: ALLon sidecars;SYS_CHROOT/SYS_ADMINdropped on the agent before user code runs;NET_ADMINnever granted to the agent)shell: true; regex allowlisting of dynamic values interpolated into generated shell scripts).*)No critical vulnerabilities were found in this pass. Several medium-severity hardening gaps exist around gateway/self-IP bypass rules and defaults that merit tracking.
🔍 Findings from Firewall Escape Test (prior run correlation)
Source:
/tmp/gh-aw/escape-test-summary.txt(workflow run 29286879560, workflowsecret-digger-copilot.md).noopwith conclusionsuccess— no investigation was performed, no secrets were touched.GH_AW_DETECTION_CONCLUSION: warning,GH_AW_DETECTION_REASON: threat_detected, which created/updated a "[aw] Detection Runs" tracking issue ([aw] Detection Runs #6205) as designed.🛡️ Architecture Security Analysis
Network Security Assessment
src/host-iptables-rules.ts): Programs the DockerDOCKER-USERchain so egress filtering applies to all containers onawf-net, not just the ones AWF launches directly — closing a common Docker networking gap where per-container rules can be bypassed by joining the same bridge.addBlockRules,host-iptables-rules.ts:236-270).ip6tablesis unavailable, IPv6 is disabled via sysctl (host-iptables-rules.ts:93) to prevent unfiltered bypass — good fail-closed behavior.containers/agent/setup-iptables.sh, 540 lines): NAT DNAT of ports 80/443 to Squid, dangerous-port NAT blacklist (SSH/DB/mail ports, lines 106-121), and a final default-denyDROPin theOUTPUTfilter chain for both TCP and UDP (lines ~495-500).dstdom_regex, generated fromsrc/domain-patterns.tsusing a ReDoS-safe character class ([a-zA-Z0-9.-]*) instead of.*for wildcard expansion (domain-patterns.ts:60-118) — a solid mitigation for a previously plausible catastrophic-backtracking vector in user-supplied--allow-domainspatterns.Weakness — self/gateway bypass surface:
setup-iptables.shadds severalRETURN/ACCEPTrules that bypass Squid/DNAT entirely for specific IPs:iptables -A OUTPUT -p tcp -d "$AGENT_IP" -j ACCEPT, ~line 216) — intentional for test frameworks binding on0.0.0.0, but it means any TCP port to the container's own IP is unfiltered (low risk, since it's self-loopback-equivalent, but worth confirming it can't be abused to reach the container's own bind-mounted services unexpectedly).allow_host_access_to_gateway, ~line 288-303) is scoped to ports 80/443 plus explicit--allow-host-ports, which is appropriately narrow — good design, flagged here only because it's an exception path worth re-auditing whenever--enable-host-accesssemantics change.--allow-host-service-ports(configure_host_access_rules, ~line 345) explicitly bypasses theDANGEROUS_PORTSNAT blacklist for the host/network gateway IP only — by design for CI service containers (e.g., Postgres/Redis test services), but it is the single mechanism in the codebase that intentionally re-opens ports in the dangerous list. Confirm callers cannot set the host gateway resolution to an attacker-controlled address (currently resolved viagetent hosts host.docker.internal/ a CLI-supplied env var, not agent-controlled input).Container Security Assessment
src/services/agent-service.ts:80-90: agent getscap_add: ['SYS_CHROOT','SYS_ADMIN'](only for chroot mode) and acap_droplist;NET_ADMIN/NET_RAWare reserved exclusively for the short-livediptables-initcontainer (agent-service.ts:367-368), which itself getscap_drop: ['ALL']beyond those two — good separation of concerns (the process with kernel/network privilege is isolated from the process that runs untrusted agent commands).entrypoint.sh:453-465: capability drop is explicit and logged (CAPS_TO_DROP="cap_sys_chroot,cap_sys_admin"), executed viacapsh --drop=...immediately beforeexec'ing the user's command (line ~1698), and non-chroot mode drops the same set beforegosu awfuserde-escalates from root (line 1737).seccomp-profile.json: uses an allowlist model ("defaultAction": "SCMP_ACT_ERRNO"), i.e., syscalls not explicitly listed are denied — this is the safer default vs. a denylist model.squid-service.ts:96,service-security.ts:45, DoH/CLI-proxy configs): consistentlycap_drop: ['ALL']+no-new-privileges:true.Domain Validation Assessment
parseDomainWithProtocolandwildcardToRegex(domain-patterns.ts) correctly escape all regex metacharacters other than the intentional*, and anchor the resulting pattern with^...$, preventing partial-match domain bypass (e.g.,evil-github.commatching angithub.comACL)..*quantifiers.Input Validation Assessment
setup-iptables.sh) are validated twice: once in TypeScript (parseValidPortSpecs,host-iptables-validation.ts) before being passed as env vars, and again defense-in-depth in bash (is_valid_port_spec, rejecting leading zeros and out-of-range values) — the shared fixture filetests/port-spec-fixtures.jsonkeeps both implementations in sync, a good practice against divergence bugs.AWF_PREFLIGHT_BINARY(entrypoint.sh:1509-1517) is validated against^[a-zA-Z0-9_][a-zA-Z0-9_.-]*$before being interpolated into a generated shell script, explicitly to prevent option injection (e.g., a value of-v) and shell metacharacter injection — well-reasoned and documented inline.execa(..., { shell: true })usage was found insrc/; all subprocess invocations pass argument arrays, avoiding shell-metacharacter injection from CLI-supplied values (domains, ports, paths).host.docker.internalresolution could theoretically redirect the "host gateway" bypass to an attacker-influenced addresssetup-iptables.shconfigure_host_access_rules, resolves viagetent hosts/ env var, not user command inputNET_ADMINnever granted to agent container; onlyiptables-init(separate short-lived container) has itfirewall_detailedlogformat + iptables LOG rules ([FW_BLOCKED_*]prefixes) with--log-uid; rate-limited to avoid flooding/etc/shadowexplicitly excluded; only whitelisted$HOMEsubdirs mounted; sensitive API keys withheld from agent env when--enable-api-proxyis active-m limit --limit 5-10/min --limit-burston all audit LOG rules (setup-iptables.sh~line 490+) prevents log-flood DoSSYS_CHROOT/SYS_ADMINafter dropcapsh --dropimmediately beforeexecof user code (irreversible without additional privilege)github/gh-aw#59268, closed as not planned) — a known, accepted residual risk, not fixed in this codebase pass🎯 Attack Surface Map
src/host-iptables-rules.ts:249(setupHostIptables)DOCKER-USERchain covers all containers onawf-net; default-deny with LOG--network-subnetoverride could theoretically create gaps (mitigated by explicit subnet-mismatch error,host-iptables-network.ts:44-50)containers/agent/setup-iptables.shsrc/domain-patterns.ts, Squiddstdom_regexcontainers/agent/entrypoint.sh,src/services/agent-service.tscap_drop, seccomp allowlist, chroot +SYS_CHROOT/SYS_ADMINdrop before user codecapsh(validated incheck_chroot_prereqs, fails closed if missing)src/host-iptables-validation.ts,entrypoint.shregex guardssrc/enclave/dynamic-delegation-handoff.ts0700file-permission channel, host-loopback-only listenergithub/gh-aw#59268)📋 Evidence Collection
Commands run and key outputs (click to expand)
✅ Recommendations
Critical: None identified.
High:
github/gh-aw#59268) remains the most notable structurally-accepted residual risk and should stay on the roadmap for a stronger channel-binding mechanism if enclave usage expands.Medium:
iptables -A OUTPUT -p tcp -d "$AGENT_IP" -j ACCEPTinsetup-iptables.sh) to confirm it cannot be leveraged to reach unintended locally-bound services with elevated trust, or scope it to specific ports if test frameworks allow.npm audit(ornpm audit --omit=devvia an environment with registry access) in CI, since this sandbox could not reach the package registry to confirm current dependency CVE status.Low:
wildcardToRegexandparseValidPortSpecs/is_valid_port_specparity given the shared-fixture test design — a good pattern to keep exercising as new edge cases are found.--allow-host-service-portsmore prominently indocs/troubleshooting.mdordocs/environment.md, since it is the one path that reopensDANGEROUS_PORTSby design; a clear "this is scoped to the host gateway IP only" callout reduces the chance of future contributors loosening it inadvertently.📈 Security Metrics
host-iptables-rules.ts~330,host-iptables-network.ts~75,setup-iptables.sh540,entrypoint.sh1,767,domain-patterns.ts~150 read)Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
msfeed25.pkgs.visualstudio.comTo allow these domains, add them to the
network.allowedlist in your workflow frontmatter:See Network Configuration for more information.
All reactions