[Security Review] Daily Security Review and Threat Modeling – 2026-03-30 #1503
Replies: 3 comments
-
|
This discussion was automatically closed because it expired on 2026-04-06T14:07:34.992Z.
|
Beta Was this translation helpful? Give feedback.
-
|
🔮 The ancient spirits stir at the edge of the firewall.
|
Beta Was this translation helpful? Give feedback.
-
|
🔮 The ancient spirits stir in the firewall halls.
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
📊 Executive Summary
This review covers a comprehensive evidence-based analysis of the
gh-aw-firewallcodebase as of 2026-03-30. The firewall implements a well-layered defence architecture (iptables + Squid proxy + container hardening + seccomp). No critical vulnerabilities were found. Two medium-severity issues were confirmed through proof-of-concept testing, and several lower-priority hardening opportunities were identified.🔍 Findings from Firewall Escape Test
The
firewall-escape-testworkflow was not found under that name;security-reviewis the closest scheduled workflow. Logs could not be retrieved due togitnot being in PATH in the runner environment. This review is therefore independent, not comparative.🛡️ Architecture Security Analysis
Network Security Assessment
Dual-layer filtering is correctly implemented:
src/host-iptables.ts) — A dedicatedAWF-FIREWALLchain is inserted at position 1 inDOCKER-USER, ensuring all container egress goes through it before Docker's default rules. The chain terminates with a catch-allREJECT --reject-with icmp-port-unreachable.containers/agent/setup-iptables.sh) — The iptables init container DNATs HTTP/HTTPS to Squid; the OUTPUT filter chain has explicitDROPfor TCP and UDP.dstdomainanddstdom_regexACLs enforce an allowlist; the default rule denies all unconfigured domains.IPv6 is handled: when
ip6tablesis unavailable,sysctl net.ipv6.conf.all.disable_ipv6=1disables IPv6 system-wide. When available, rules mirror IPv4.DNS exfiltration is mitigated: DNS traffic is restricted to configured upstream servers in both the host-level chain and the container-level OUTPUT chain; Docker embedded DNS (
127.0.0.11) is the only resolver inside the container.Cloud metadata (
169.254.0.0/16) is explicitly blocked byhost-iptables.ts:497.File:
containers/agent/setup-iptables.sh:400-407Confirmed: Yes (code inspection)
The container-level OUTPUT filter chain only drops TCP and UDP; ICMP is silently passed. The iptables default OUTPUT policy is
ACCEPT, so ICMP packets egress the container unrestricted from the network namespace perspective. They are ultimately rejected by the host-level catch-allREJECTinAWF-FIREWALL, but:icmptunnel,ptunnel) becomes possible if the host rules are ever absent.Recommendation: Add
iptables -A OUTPUT -p icmp -j DROP(and the equivalentip6tablesrule) after the UDP DROP line insetup-iptables.sh.Container Security Assessment
Capabilities are carefully managed:
SYS_ADMIN,SYS_PTRACE,NET_RAW,SETFCAP, etc.).SYS_CHROOT + SYS_ADMIN(needed for procfs mount), butentrypoint.shcallscapsh --drop=cap_sys_chroot,cap_sys_adminbefore executing user code.NET_ADMINis never granted to the agent.NET_ADMIN + NET_RAW,cap_drop: ALL,no-new-privileges:true, and is ephemeral (exits after writing the ready signal).cap_drop: ALLandno-new-privileges:true.no-new-privileges:trueis applied to the agent container (src/docker-manager.ts:1183).apparmor:unconfinedis set to allow the procfs mount before capability drop (src/docker-manager.ts:1185). This is a deliberate trade-off with documented justification.Resource limits: Memory (
6g), PID limit (1000), andcpu_sharesare set. Hard CPU quota (cpus) is not configured (see Low findings below).--allow-urlsPatternFile:
src/squid-config.ts:153-156,src/cli.ts:1627-1668Confirmed: Yes (proof-of-concept demonstrated)
URL patterns passed to
--allow-urlsare validated for protocol prefix, path presence, and broad-pattern blocklists, but not sanitised for embedded newlines. The patterns are inserted verbatim into the Squid config:This injects
http_access allow allinto the Squid config, bypassing all domain ACL filtering.Exploitability context: An attacker must control the
--allow-urlsCLI argument. In interactive use this is low-risk (self-inflicted). In agentic workflows whereawfis invoked with arguments derived from untrusted inputs (issue bodies, repository names, etc.), this becomes a meaningful injection vector.Recommendation: Sanitise URL patterns by rejecting any string containing
\n,\r, or whitespace other than within the URL itself before use ingenerateSquidConfig. For example:Similarly, add sanitisation in
generateSslBumpConfig/generateSquidConfigas a defence-in-depth backstop.Domain Validation Assessment
src/domain-patterns.tsdemonstrates careful security thinking:*,*.*,*.*.*) are rejected with clear error messages.*, avoiding ReDoS via[a-zA-Z0-9.-]*instead of.*.(redacted)https://`) ACLs are correctly generated.No significant issues found here.
Input Validation Assessment
isValidPortSpec()before insertion into iptables commands.validateDomainOrPattern()before Squid config generation.js-yamlserialisation (not string templates), preventing YAML injection.$character inagentCommandis escaped to$$for Docker Compose interpolation (src/docker-manager.ts:1209).--allow-urlsnewlinesrc/squid-config.ts:154setup-iptables.sh:400-407--enable-dind)src/docker-manager.ts:944unshare/setnssrc/docker-manager.ts:1194cpu_sharesonly)src/docker-manager.ts:1169🎯 Attack Surface Map
--allow-domainscli.ts:1467validateDomainOrPattern(), regex--allow-urlscli.ts:1627--block-domainscli.ts:1545agentCommandcli.ts:1209$escaped, not shell-interpolated, passed as arraydocker-manager.ts:946--enable-dindopt-in only; warning logged--env/--env-alldocker-manager.ts:494--allow-host-portssetup-iptables.sh:337is_valid_port_spec()validationurlPatterns→squid.confsquid-config.ts:154📋 Evidence Collection
Commands run and key outputs
ICMP check (container iptables):
URL injection PoC:
Seccomp dangerous syscalls allowed:
Lines analysed: 6,289 lines across 5 core security files.
✅ Recommendations
🔴 Medium – Fix Soon
1. Sanitise
--allow-urlspatterns for embedded newlinesFile:
src/cli.ts(validation block around line 1634) andsrc/squid-config.ts:154Add a check that rejects any URL pattern containing
\ror\n, and as backstop strip newlines ingenerateSslBumpConfigbefore writing to the config file. This prevents Squid config injection from controlling the URL regex input.2. Block ICMP at container level (defence-in-depth)
File:
containers/agent/setup-iptables.sh(after line 407)Add explicit ICMP drop rules:
This makes the container-level firewall self-sufficient, removing dependence on the host-level fallback for ICMP filtering.
🟡 Low – Plan to Address
3. Set hard CPU quota
File:
src/docker-manager.tsagent service definitionAdd
cpus: '2'(or a configurable value) alongsidecpu_shares.cpu_sharesonly affects scheduling weight when the system is under contention; a hardcpuslimit prevents CPU-based DoS.4. Consider removing
open_by_handle_atfrom seccomp allowlistFile:
containers/agent/seccomp-profile.jsonopen_by_handle_atwas the foundation of the "shocker" container escape. While modern kernels and capability restrictions mitigate this, it is rarely needed by standard development toolchains. Removing it reduces the kernel attack surface with minimal compatibility impact.5. Restore AppArmor confinement after procfs mount
Explanation: The agent container runs
apparmor:unconfinedto allowmount(2)for procfs beforecapshdropsSYS_ADMIN. Since capabilities are dropped before user code runs, AppArmor confinement could be reinstated at that point viaapparmor_parseror by using a custom AWF-specific AppArmor profile that permits only thechrootand procfs mount operations while blocking everything else.🔵 Info
6. Document
--enable-dindfirewall bypass prominentlyThe warning at
src/docker-manager.ts:944is logged atWARNlevel but does not appear in the help text. Consider adding a more visible WARNING to--enable-dindhelp text noting that this grants the agent complete network bypass capability viadocker run.7. No prior escape test results found
The
firewall-escape-testworkflow name was not present in the repository. If escape testing is done under a different name, consider making the naming consistent so audit tools can cross-reference results.📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions