[Security Review] Daily Security Review: Firewall Architecture & Threat Model — 2026-09-12 #8488
Closed
Replies: 1 comment
|
This discussion was automatically closed because it expired on 2026-09-19T12:35:34.376Z.
|
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
📊 Executive Summary
Daily security review of
github/gh-aw-firewallcombining (1) complementary context from a pre-fetched CI log (/tmp/gh-aw/escape-test-summary.txt) and (2) direct codebase analysis of the firewall's network, container, domain-validation, and injection-defense layers.Overall posture: Strong. The codebase shows deliberate, layered defense-in-depth: host-level
DOCKER-USERiptables + in-container NAT/filter chains + Squid L7 ACLs + seccomp/capability drops + a custom ESLint rule (no-unsafe-execa) that fails CI on command-injection-proneexeca()patterns. No critical or high-confidence exploitable vulnerabilities were found in the code paths reviewed. Findings below are hardening opportunities and one previously-tracked architectural risk, not newly discovered exploits.Metrics: ~2,990 lines across
host-iptables-*.ts, ~2,620 lines acrosssrc/squid/*.ts, 540-linesetup-iptables.sh, plus dedicatedagent-security-config/agent-servicecapability-hardening modules — all covered by adjacent*.test.tsfiles (evidence of active, test-driven security maintenance).🔍 Findings from Firewall Escape Test
Correction of scope:
/tmp/gh-aw/escape-test-summary.txtdoes not contain firewall network-escape test results. It is a raw GitHub Actions step-log excerpt (workflow run29286879560, workflowsecret-digger-copilot.md) for an unrelated "Secret Digger" prompt-injection test. Key evidence lines:Interpretation: the agent's own instruction-following layer correctly identified and refused a secret-exfiltration prompt injection, and gh-aw's detection pipeline independently flagged the run (
threat_detected→ posted to a tracking issue). This is a positive control-plane signal (defense worked as intended), not a firewall bypass. No escape attempts, blocked/allowed domain lists, or squid access-log excerpts are present in this file, so no firewall-specific evidence could be cross-referenced. Recommendation: the workflow step that populatesescape-test-summary.txtshould be verified — it appears to be sourcing the wrong artifact/run for this review's stated purpose.🛡️ Architecture Security Analysis
Network Security Assessment
src/host-iptables-rules.ts): builds theFW_WRAPPERchain injected intoDOCKER-USER, so all containers onawf-netare subject to filtering regardless of what runs inside them (evidence:insertDockerUserJumpRule(CHAIN_NAME, bridgeName)at end ofsetupHostIptables). Explicit ACCEPT rules exist only for: Squid source, conntrack ESTABLISHED/RELATED, loopback/127.0.0.0/8, DNS to configured upstreams, Squid destination, optional API/CLI-proxy/DoH sidecars, and optional host-gateway ports. Default-deny withREJECT/LOGcloses the chain (addBlockRules).containers/agent/setup-iptables.sh, 540 lines): dual NAT+filter enforcement — NAT DNATs ports 80/443 to Squid (configure_http_dnat), filter chain independently ACCEPTs only Squid/DNS/proxy-sidecar traffic and DROPs everything else with rate-limited audit logging ([FW_BLOCKED_TCP],[FW_BLOCKED_UDP_AGENT],--log-uid).DANGEROUS_PORTSarray (SSH 22, SMTP 25, SMB 445, MSSQL 1433, Oracle 1521, MySQL 3306, RDP 3389, PostgreSQL 5432, Redis 6379, MongoDB 27017/27018/28017) is NAT-RETURNed so it is never redirected to Squid and falls through to the filter DROP — defense-in-depth even if Squid ACLs were misconfigured.disable_ipv6()insetup-iptables.shanddisableIpv6ViaSysctl()inhost-iptables-shared.tsproactively disable IPv6 whenip6tablesis unavailable, closing a documented bypass class (issue Squid proxy rejects IPv6 localhost connections from chroot (transaction-end-before-headers) #1543 referenced in code comments) where IPv4-only DNAT rules would not intercept IPv6 egress.AWF_DNS_SERVERS(default8.8.8.8,8.8.4.4) plus Docker's embedded resolver127.0.0.11; all other UDP is dropped, closing the classic DNS-exfiltration channel.Container Security Assessment
src/services/agent-service.ts:cap_add: ['SYS_CHROOT', 'SYS_ADMIN'](needed for chroot + procfs mount for .NET/Java/proc/self/exe), explicitly excludesNET_ADMINfrom the agent (comment: "iptables setup is performed by the awf-iptables-init service which shares the agent's network namespace").cap_dropremovesNET_RAW, SYS_PTRACE, SYS_MODULE, SYS_RAWIO, MKNOD.security_optsetsno-new-privileges:true+ custom seccomp profile (defaultAction: SCMP_ACT_ERRNO, 5 allow-listed syscalls) +apparmor:unconfined— the AppArmor exception is explicitly justified in-code: SYS_CHROOT/SYS_ADMIN are dropped viacapshinentrypoint.shbefore user code executes, so the temporary AppArmor relaxation only covers privileged, code-controlled entrypoint steps, not user-supplied commands.network_mode: service:agent) is the only container holdingNET_ADMIN/NET_RAW, and it exits before user code runs (cap_drop: ['ALL']afterward peragent-service-build.test.ts:117-118).agent-security-config.test.ts,agent-service-build.test.ts,service-security.test.ts) directly asserts these capability/seccomp/security_opt invariants — meaning a regression that re-addsNET_ADMINto the agent or removescap_dropwould fail CI.Domain Validation Assessment
src/squid/domain-acl.ts+src/domain-validation.ts:assertSafeForSquidConfig()rejectsSQUID_DANGEROUS_CHARS(whitespace, null bytes, quotes, semicolons, backticks,#) before any domain/pattern string is interpolated intosquid.conf, directly preventing Squid-config-injection via a malicious--allow-domainsvalue. Both plain domains and wildcard patterns pass through this assertion viaformatDomainForSquid().parseDomainConfig) — reduces config bloat but is a correctness feature, not itself a security boundary.Input Validation Assessment
src/squid/validation.ts:validateProxyIp()uses a strict anchored octet regex (rejects malformed IPs including leading zero/out-of-range octets);validateAndSanitizeHostAccessPort()and the parallel bashis_valid_port_spec()insetup-iptables.shboth reject ports outside 1-65535 and explicitly re-check againstDANGEROUS_PORTS/DANGEROUS_PORTS_LIST, so--allow-host-ports/--allow-host-service-portscannot be used to punch a hole to SSH/DB ports even via a range spec (loop-checks every port in a range).execa(..., {shell: true})usage found anywhere insrc/, and a custom ESLint rule (eslint-rules/no-unsafe-execa.js) statically flags template-literal/concatenated/variable commands passed toexeca— this is an unusually strong, automated command-injection guardrail baked into the lint pipeline itself.awf-netimpersonating Squid IP172.30.0.10) + host chain ACCEPTs only that IPsquid.confvia crafted domain stringassertSafeForSquidConfig/SQUID_DANGEROUS_CHARS(src/squid/domain-acl.ts,src/domain-validation.ts)execa()args built from user inputshell:trueusage foundLOGrules with--log-uidforFW_BLOCKED_*classes;dump_audit_state()persists fulliptables-savedumpsrc/enclave/dynamic-delegation-handoff.ts,manager.ts(0700/0600 file perms); tracked asgithub/gh-aw#59268, closed as not planned--enable-api-proxynot used--limit 5/min/10/min --limit-burst) insetup-iptables.shSYS_ADMIN/SYS_CHROOTpost-entrypointcapsh --drop=...before user command runs (entrypoint.sh), asserted by testsdisable_ipv6(), issue #1543)🎯 Attack Surface Map
src/host-iptables-rules.ts:setupHostIptables(DOCKER-USER chain)getNetworkBridgeName) succeeding; failure path throws, but a race during network recreation is conceivablecontainers/agent/setup-iptables.sh(540 lines,main())allow_host_access_to_gateway, sidecar rules) is a candidate for future misconfigurationsrc/squid/domain-acl.ts,src/squid/acl-generator.tsassertSafeForSquidConfig, dedup/overlap logic, protocol-scoped rulesdomain-patterns.ts) is security-critical; any future permissive regex could allow domain-confusion bypass — worth periodic fuzzingsrc/squid/validation.ts,host-iptables-validation.ts, bashis_valid_port_spectests/port-spec-fixtures.json)containers/agent/entrypoint.sh,docker-manager.tsapparmor:unconfinedis a real (justified, but non-zero) relaxation of one hardening layersrc/enclave/dynamic-delegation-handoff.ts,manager.tsgithub/gh-aw#59268(closed, not planned)package.json(execa, ajv, commander, js-yaml)npm auditcould not be run in this sandbox (outbound registry call blocked by the review environment's own proxy — expected, not a repo defect); recommend running audit in an unrestricted CI job📋 Evidence Collection
Escape-test summary excerpt (click to expand)
host-iptables-rules.ts — default-deny closing rules
setup-iptables.sh — dangerous ports blacklist
squid/domain-acl.ts — config-injection guard
agent-service.ts — capability hardening
npm audit — blocked by review sandbox's own network policy (informational, not a repo finding)
✅ Recommendations
Critical
High
escape-test-summary.txtfor this daily review — it currently delivers an unrelated prompt-injection test log instead of firewall escape-test results, undermining the "cross-reference with escape test" requirement of this review.npm audit(and equivalent for container-image OS packages) in an environment with full registry access, since this review's sandbox network policy blocked it; treat any high/critical advisories onexeca,ajv,commander, orjs-yamlas release-blocking given their central role in command execution and config generation.Medium
github/gh-aw#59268(enclave delegation-control listener guarded by capability/path secrecy alone) — consider adding a lightweight shared-secret or mTLS check even though it's loopback-only, as defense-in-depth against local privilege confusion.domain-patterns.tswildcard/regex matching to guard against future domain-confusion bypasses (e.g., unintended matches likeevil-github.comagainst a.github.compattern) — no defect found, but this is the highest-leverage single point of failure in the domain-allowlist model.Low
host-iptables-validation.tsvs.setup-iptables.sh:is_valid_port_spec) behind a single generated source of truth beyond the existing shared JSON fixtures, to reduce future drift risk noted in code comments.apparmor:unconfinedjustification (already present in code comments) indocs/environment.mdor the container-security section of the README so downstream users auditing the image are not alarmed by the setting without context.📈 Security Metrics
host-iptables-*.ts) + ~2,620 lines (src/squid/*.ts) + 540 lines (setup-iptables.sh) + capability/seccomp modules insrc/services/*.tsWarning
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