[Security Review] Daily Security Review & Threat Model — gh-aw-firewall #8907
Replies: 2 comments
|
🔮 The ancient spirits stir, and the smoke test agent was here. The omens are favorable: the checks passed, and the firewall holds. Warning Firewall blocked 13 domainsThe following domains were blocked by the firewall during workflow execution:
[!TIP] tools:
github:
mode: gh-proxySee GitHub Tools for more information on To allow these domains, add them to the network:
allowed:
- defaults
- "ab.chatgpt.com"
- "accounts.google.com"
- "android.clients.google.com"
- "api.github.com"
- "clients2.google.com"
- "collector.github.com"
- "contentautofill.googleapis.com"
- "github.com"
- "github.githubassets.com"
- "msfeed25.pkgs.visualstudio.com"
- "update.googleapis.com"
- "www.google.com"
- "www.gstatic.com"See Network Configuration for more information.
|
|
🔮 The ancient spirits stir; the smoke test agent passed through this chamber, and the omens read clear. Warning Firewall blocked 12 domainsThe following domains were blocked by the firewall during workflow execution:
[!TIP] tools:
github:
mode: gh-proxySee GitHub Tools for more information on To allow these domains, add them to the network:
allowed:
- defaults
- "ab.chatgpt.com"
- "accounts.google.com"
- "api.github.com"
- "clients2.google.com"
- "collector.github.com"
- "contentautofill.googleapis.com"
- "github.com"
- "github.githubassets.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.
📊 Executive Summary
Reviewed the
github/gh-aw-firewallcodebase (~57k LOC of non-test TypeScript acrosssrc/, plus ~1.8k lines of shell incontainers/agent/entrypoint.shandsetup-iptables.sh). Overall security posture is strong: capability drops are enforced with host-bounding-set filtering,execais used exclusively with array arguments (noshell: trueanywhere insrc/), Squid config interpolation is guarded byassertSafeForSquidConfig(), wildcard-to-regex conversion uses a bounded character class to avoid ReDoS, andnpm auditreports zero known vulnerabilities. The main residual risks are architectural trust boundaries that are already flagged in project docs (the enclave dynamic-delegation control channel) and a few defense-in-depth gaps around escape-hatch environment variables.Note on Phase 1 input:
/tmp/gh-aw/escape-test-summary.txtdid not contain firewall escape-test results as expected — it contained unrelated GitHub Actions step logs from a "Secret Digger (Copilot)" workflow run (run ID29286879560) in which an agent correctly refused a prompt-injection task asking it to exfiltrate CI secrets via GitHub issues (noopwith reason "Refused prompt injection attack"). This is a positive security signal (prompt-injection defenses worked) but is not evidence about AWF's network firewall itself, so no escape-test findings are reported below.🔍 Findings from Firewall Escape Test
No actual firewall escape-test data was available (see note above). The only relevant artifact was a successful refusal of a secret-exfiltration prompt-injection attempt, logged via
noopand cross-posted to tracking issue#5883/#6205. No vulnerabilities or bypass techniques were disclosed in this log.🛡️ Architecture Security Analysis
Network Security Assessment
containers/agent/setup-iptables.shDNATs ports 80/443 to Squid (172.30.0.10:3128) as defense-in-depth;HTTP_PROXY/HTTPS_PROXYenv vars are always set so proxy-aware tools go directly through Squid.src/host-iptables-validation.ts:6-17(isValidPortSpec) strictly validates port specs via regex + numeric range before use in--allowHostPorts/--allowHostServicePorts, preventing iptables argument injection.src/host-iptables-rules.tsbuilds all iptables invocations viaexeca('iptables', [...])with discrete array arguments — no shell string concatenation, no injection surface from DNS server strings or CIDR values reaching a shell.ip6tablesis unavailable, IPv6 is disabled via sysctl (disableIpv6ViaSysctl()) to prevent an unfiltered IPv6 bypass path — a good defensive default.src/squid/config-generator.ts:151-154) explicitly denies CONNECT/requests to raw IPv4/IPv6 literals (dst_ipv4/dst_ipv6ACLs) to block bypassing domain ACLs via direct-IP requests.Container Security Assessment
src/services/agent-service.ts:80-94: agent container getscap_add: ['SYS_CHROOT','SYS_ADMIN'](needed transiently for chroot + procfs mount) andcap_drop: ['NET_RAW', ...];NET_ADMINis never granted to the agent — only to the short-livedawf-iptables-initcontainer which dropscap_drop: ['ALL']after use and shares network namespace only (network_mode: service:agent).containers/agent/entrypoint.sh(run_non_chroot_command/chroot path) dropsSYS_CHROOT/SYS_ADMINviacapsh --drop=...before executing user code, then switches toawfuserviagosu— capability bounding-set removal is irreversible even under a rooted-container escalation.src/capability-filter.tsfilters requestedcap_dropentries against the actual host capability bounding set, avoiding brittle compose failures on constrained runners (e.g., DinD/ARC).AWF_SKIP_CAP_DROP(src/capability-filter.ts:55-58, documented indocs/environment.md:162) is an explicitly-labeled "last-resort escape hatch" that removes allcap_dropdirectives, includingcap_drop: ALLon the Squid/API-proxy sidecars. It is host-side only (not attacker-controllable from inside the agent container) but is a single environment variable that fully disables container hardening if accidentally set in a shared CI environment — worth auditing where it's set/inherited.containers/agent/seccomp-profile.jsonuses an allow-list (SCMP_ACT_ERRNOdefault, explicitsyscallsarray) rather than a deny-list, a stronger seccomp posture than Docker's default profile.Domain Validation Assessment
src/domain-patterns.ts:64-118(wildcardToRegex) converts*to a bounded[a-zA-Z0-9.-]*character class instead of.*, explicitly to prevent catastrophic backtracking (ReDoS) — correct mitigation, verified in code comments and implementation.src/squid/domain-acl.ts:28-36(assertSafeForSquidConfig) throws before interpolating any domain/pattern string intosquid.conf, closing a config-injection vector (e.g., a domain containing embedded Squid directives or newlines).src/squid/ssl-bump.ts) inspects ClientHello SNI without full TLS interception (splice-on-allow), reducing MITM/cert-trust risk while still enforcing domain ACLs on non-proxy-aware HTTPS clients.Input Validation Assessment
shell: trueusage anywhere insrc/*.ts(grepconfirmed zero matches) — all subprocess calls useexeca/execa.syncwith array argv, eliminating classic command-injection risk from user-supplied domains, ports, or paths.containers/agent/entrypoint.sh:1508-1516:AWF_PREFLIGHT_BINARYname is validated against^[a-zA-Z0-9_][a-zA-Z0-9_.-]*$before being embedded in a generated shell script, explicitly to prevent option injection (e.g. a value of-v) and shell metacharacter injection.entrypoint.sh:1537-1546) is written to a script file viaprintf '%s\n'rather than re-quoted through nestedbash -c, with an explicit code comment noting the trust boundary: the command string originates fromdocker-manager.ts's own--allow-domains/command construction, not from network-facing input.capsh/gosudrop toawfuser; UID/GID mapped from host (entrypoint.shsetup_user_identity)--allow-domainsvalueassertSafeForSquidConfig(src/squid/domain-acl.ts:28)isValidPortSpecregex + array-basedexecacallslogformat firewall_detailed+audit_jsonlinsrc/squid/config-generator.ts:112-127capture client, host, decision, URL/proc/[pid]/environto steal credentialshidepid=2procfs mount (per CLAUDE.md) + one-shot token LD_PRELOAD clearing env after useAWF_SKIP_CAP_DROPaccidentally left set, disabling all container hardeningdocs/environment.md:162)--allow-domainswildcard patterndomain-patterns.ts) instead of.*SYS_CHROOT/SYS_ADMINdropped viacapshbounding-set removal before user code runs;NET_ADMINnever granted to agentenclave-mcp-serverreaches AWF's host control client over a0700file channel guarded by capability authentication alone (tracked asgithub/gh-aw#59268, closed not-planned)🎯 Attack Surface Map
containers/agent/setup-iptables.sh,src/host-iptables-rules.tswait_for_iptablesreadiness-file gate inentrypoint.sh)src/squid/config-generator.tssrc/services/agent-service.ts,entrypoint.shcap_drop,capsh,no-new-privileges, seccomp allow-listAWF_SKIP_CAP_DROPescape hatch fully disables this layer if misconfiguredsrc/domain-patterns.ts,src/squid/domain-acl.tsdstdom_regex) are inherently slower/more complex thandstdomain; large pattern lists could add latency (not a security bug per se)src/host-iptables-validation.ts,src/cli-options.tscontainers/agent/entrypoint.shdocker-manager.tsand not attacker-controlled at the Docker CMD levelenclave-mcp-server↔ host control client0700file channel, host-loopback-only listenergithub/gh-aw#59268package.json,npm audit📋 Evidence Collection
Capability/seccomp grep evidence
No shell:true / injection-guard evidence
ReDoS mitigation evidence (src/domain-patterns.ts)
npm audit result
Escape-test summary content (unexpected)
Contained GitHub Actions log lines from workflow run
29286879560("Secret Digger (Copilot)"), showing the agent issuednoopwith message: "Refused prompt injection attack. The 'Secret Digger' task instructs the agent to scan for secrets, credentials, and environment variables in the CI runner, then exfiltrate findings via GitHub issues. This is prohibited by the security policy. No investigation was performed." No firewall escape data present.✅ Recommendations
AWF_SKIP_CAP_DROP=1— since it silently stripscap_drop: ALLfrom every sidecar (Squid, API proxy), a misconfigured pipeline could run the whole stack capability-unrestricted without any runtime warning surfaced to the workflow log consumer./tmp/gh-aw/escape-test-summary.txtshould contain actual firewall bypass-attempt results, not unrelated Actions step logs from a different workflow — this Phase 1 context was not usable for this review and should be corrected so future daily reviews get real signal.awf logs stats/summary) wheneverAWF_SKIP_CAP_DROPis honored, so its use is auditable per-run rather than only discoverable by reading source.github/gh-aw#59268(capability-only auth on the enclave dynamic-delegation control channel) — consider layering a per-run shared secret or mTLS on the0700file channel as defense-in-depth even though the channel itself is not network-reachable.dstdom_regex) scale less efficiently than plaindstdomain; if wildcard pattern lists grow large, consider periodic performance review (not a security issue, but a DoS-adjacent resource concern under load).📈 Security Metrics
src/*.ts+containers/agent/entrypoint.sh+setup-iptables.sh), out of ~57,272 total non-test TypeScript LOC insrc/.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