[Security Review] Daily Security Review and Threat Modeling — 2026-03-04 #1143
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-03-11T13:51:09.044Z.
|
Beta Was this translation helpful? Give feedback.
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
This report covers a comprehensive, evidence-based security review of the
gh-aw-firewallrepository, conducted against the codebase on 2026-03-04. All findings are backed by specific file paths, line numbers, and command outputs.Security posture: Moderate — well-layered defense-in-depth with several gaps that should be addressed.
🔍 Context: Previous Firewall Escape Test Agent
No previous run of a dedicated "firewall-escape-test" workflow was found in the repository's workflow definitions. The
security-reviewworkflow itself is the primary security analysis mechanism. Log download was unavailable (unauthenticated environment), so this review is based entirely on fresh codebase analysis.🛡️ Architecture Security Analysis
Network Security Assessment
Strengths:
containers/agent/setup-iptables.sh): NAT DNAT redirects HTTP/HTTPS to Squid; TCP DROP defaultsrc/host-iptables.ts): DOCKER-USER chain viaFW_WRAPPER/FW_WRAPPER_V6rejects non-allowed traffic224.0.0.0/4,ff00::/8)172.30.0.0/24) with predictable IPs makes Squid IP bypass hardGaps:
Container Security Assessment
Strengths:
NET_ADMIN+SYS_CHROOT+SYS_ADMINdropped viacapsh --drop=cap_net_admin,cap_sys_chroot,cap_sys_admininentrypoint.shbefore any user code runsno-new-privileges:truesecurity option prevents SUID/SGID privilege escalation (src/docker-manager.ts:884)NET_RAWdropped from both Squid and agent containers, preventing raw socket creation and ping-based exfiltrationGaps:
Domain Validation Assessment
Strengths:
*,*.*) explicitly rejected (src/domain-patterns.ts:127-135)DOMAIN_CHAR_PATTERN = '[a-zA-Z0-9.-]*'used instead of.*in wildcard-to-regex conversion (src/domain-patterns.ts:68-72)src/domain-patterns.ts:248)Gaps:
Input Validation Assessment
Strengths:
'\$\{arg.replace(/'/g, "'\\''")}'(src/cli.ts:509)src/docker-manager.ts:31)https://prefix, path component, and broad-pattern checkGaps:
F1 — CRITICAL: Permissive Seccomp Profile (Allow-by-Default)
File:
containers/agent/seccomp-profile.jsonEvidence:
The custom seccomp profile uses
"defaultAction": "SCMP_ACT_ALLOW"— a blocklist approach where all syscalls are allowed unless explicitly denied. Only 28 syscalls are blocked. Docker's built-in default profile uses the opposite approach:SCMP_ACT_ERRNO(deny-by-default) with an allowlist of ~300+ safe syscalls.Dangerous syscalls NOT blocked by this profile:
mountunshare--usernamespace doesn't require capabilities on many kernelscloneCLONE_NEWNETflag, could create a new network namespace (requires CAP_NET_ADMIN, which IS dropped)bpfperf_event_openuserfaultfdio_uring_setupsetnskcmpWhy it matters: Although
no-new-privileges:trueand capability dropping provide compensating controls, the seccomp profile is the primary syscall-level boundary. Replacing Docker's hardened default profile with a permissive one significantly reduces the syscall-level attack surface protection.Recommendation: Replace the custom profile with either Docker's default profile (by removing the
seccomp=security option) or changedefaultActiontoSCMP_ACT_ERRNOand provide an explicit allowlist of required syscalls.F2 — HIGH: SYS_ADMIN Capability During Container Initialization
File:
src/docker-manager.ts:870The agent container starts with
SYS_ADMIN, one of the most powerful Linux capabilities (equivalent to "superuser for the kernel"). It is needed for mounting procfs at/host/procinentrypoint.sh. However, there is a race condition window between container start and thecapsh --dropcall in the entrypoint where the fullSYS_ADMINcapability is available.Attack vector: If an attacker can inject code into the container before the
capshdrop (e.g., via a malicious Docker image, a compromised base layer, or a vulnerability in the entrypoint initialization steps that run before the drop), they would haveSYS_ADMINaccess.Mitigating factors:
awfuser— standard patternno-new-privileges:trueprevents SUID escalation after dropRecommendation: Consider using a minimal setup container that only mounts the procfs, then use
nsenteror Docker's--initto avoid needingSYS_ADMINin the main agent container. At minimum, document this as a known risk in the security documentation.F3 — HIGH: npm Dependency Vulnerabilities
Evidence:
minimatch(used by development tooling) has two ReDoS vulnerabilities. While these are in dev dependencies (commitlint), they run in CI pipelines and could affect automated workflows.Recommendation: Run
npm audit fixto resolve both. This is a low-effort fix.F4 — MEDIUM: UDP Not Explicitly Blocked at Container Level
File:
containers/agent/setup-iptables.sh:293The container's iptables OUTPUT filter only drops TCP. Non-DNS UDP traffic (ports other than 53) is not blocked at the container iptables level. UDP-based protocols (e.g., QUIC/HTTP3 on port 443 UDP, NTP, SNMP, custom UDP exfiltration channels) would bypass the container-level firewall.
Mitigating factor: The host-level
FW_WRAPPERchain (src/host-iptables.ts:479-491) does block all other UDP. However, this creates a single point of enforcement for UDP — if the host chain were to fail, be bypassed, or not set up before container traffic flows, UDP would be unrestricted.Recommendation: Add a UDP DROP rule in
setup-iptables.shafter the DNS ACCEPT rules:# After DNS allow rules and before end of script iptables -A OUTPUT -p udp -j DROPF5 — MEDIUM: IPv6 Egress Uncontrolled When ip6tables Unavailable
File:
containers/agent/setup-iptables.sh(multiple IPv6 guards)When
ip6tablesis unavailable inside the container, all IPv6 outbound traffic bypasses the Squid proxy and is not subject to domain whitelisting. An attacker who knows the IPv6 address of a target can make unrestricted outbound connections.Context: The host-level
FW_WRAPPER_V6chain provides some protection if ip6tables is available at the host level. However, if both container and host lack ip6tables, IPv6 is completely unrestricted.Recommendation: If ip6tables is unavailable, the container should either:
sysctl -w net.ipv6.conf.all.disable_ipv6=1F6 — MEDIUM: Wildcard Domain Validation Edge Case
File:
src/domain-patterns.ts:164-170A pattern like
*.b.*.d.comhaswildcardSegments=2,totalSegments=5. The check evaluates:2 > 1(true) AND2 >= 4(false) → pattern is accepted. This pattern would matchanything.b.anything.d.com— a very broad domain set.Recommendation: Tighten the validation to reject patterns with more than one wildcard segment:
F7 — MEDIUM: URL Pattern Values Embedded in Squid Config Without Sanitization
File:
src/squid-config.ts:117-120URL patterns from
--allow-urlsare embedded directly into the Squid configuration asurl_regexACL values. While basic validation occurs (must start withhttps://, must have a path, broad pattern rejection), Squid-specific injection characters (newlines, null bytes, config directives) are not sanitized. A crafted pattern likehttps://github.com/org/*\nhttp_access allow allcould inject Squid directives if the newline check is incomplete.Evidence: The validation in
src/cli.ts:1004-1034checks for broad patterns (.*) and path presence, but does not sanitize\n,\r, or other config-breaking characters.Recommendation: Sanitize URL patterns to strip non-printable characters and newlines before embedding them in Squid config:
F8 — LOW: Container-Level Logs May Not Capture All UDP Blocked Events
File:
containers/agent/setup-iptables.sh— no UDP LOG ruleThe container's iptables does not include
LOGrules for blocked UDP (since UDP blocking is handled at the host level). This means UDP blocking events only appear in host-level kernel logs (dmesg), not in container-accessible logs. For forensics, UDP exfiltration attempts would require host log access.F9 — LOW: Predictable Work Directory Pattern
File:
src/cli.ts:707The work directory follows a predictable timestamp-based pattern (
/tmp/awf-(milliseconds)). A malicious process running on the same host could predict or race to create/modify the work directory before AWF writes its configuration files, potentially injecting malicious squid.conf or docker-compose.yml content.Mitigating factors: Requires local host access (same privilege level), and container file integrity checks during startup would catch obvious tampering.
Recommendation: Use
crypto.randomUUID()or a cryptographically random suffix instead ofDate.now().*.x.*.y.compattern\nin--allow-urlsvalueio_uring,bpf,userfaultfd/tmp/awf-(timestamp)🎯 Attack Surface Map
--allow-domainsCLI flagsrc/cli.ts:870validateDomainOrPattern(), regex escaping--allow-urlsCLI flagsrc/cli.ts:989-1040--allow-host-portssrc/cli.ts:778--enable-host-accessrequiredsetup-iptables.sh:293src/host-iptables.tssrc/squid-config.tscontainers/agent/seccomp-profile.jsonsrc/docker-manager.ts:870,884setup-iptables.sh✅ Recommendations (Prioritized)
🔴 Critical — Address Immediately
1. Replace permissive seccomp profile with a deny-by-default allowlist
Option A (simplest): Remove the custom seccomp profile and rely on Docker's hardened default:
seccomp=\$\{config.workDir}/seccomp-profile.jsonfromsecurity_optinsrc/docker-manager.ts:885Option B (preserve custom blocks): Change
defaultActiontoSCMP_ACT_ERRNOand add an allowlist:{ "defaultAction": "SCMP_ACT_ERRNO", "syscalls": [ { "names": ["read", "write", "open", "close", "...300+ safe syscalls..."], "action": "SCMP_ACT_ALLOW" } ] }Docker's default profile can be used as a starting point and the specific blocks for
ptrace,keyctl, etc. preserved as additional hardening.🟠 High — Address Soon
2. Add UDP DROP at container iptables level (
containers/agent/setup-iptables.sh)After the DNS ACCEPT rules (line ~280), add before the TCP DROP:
# Block all non-DNS UDP traffic iptables -A OUTPUT -p udp -j DROP3. Fix IPv6 handling when ip6tables unavailable (
containers/agent/setup-iptables.sh)Add a hard-fail or sysctl disable when ip6tables is unavailable:
4. Update vulnerable npm dependencies (root directory)
This resolves both
minimatch(high, ReDoS) andajv(moderate, ReDoS).🟡 Medium — Plan to Address
5. Tighten multi-wildcard domain validation (
src/domain-patterns.ts:164-170)6. Sanitize URL patterns for Squid config injection (
src/squid-config.ts:117-120)🟢 Low — Nice to Have
7. Use cryptographically random work directory suffix (
src/cli.ts:707)8. Add container-level UDP logging (
containers/agent/setup-iptables.sh)Before the UDP DROP rule, add a LOG rule for forensic visibility:
iptables -A OUTPUT -p udp -j LOG --log-prefix "[FW_BLOCKED_UDP_CONTAINER] " --log-level 49. Document SYS_ADMIN initialization risk in security documentation
Add an explicit note in
docs/explaining the SYS_ADMIN capability window during initialization and the compensating controls that make it safe.📋 Evidence Collection
Seccomp profile analysis
npm audit output
Container iptables OUTPUT chain (UDP gap)
Host FW_WRAPPER chain structure
Wildcard validation logic
📈 Security Metrics
npm audit fix, F4: add UDP DROP, F7: sanitize URL patterns)no-new-privileges:true, capability dropping viacapsh)Beta Was this translation helpful? Give feedback.
All reactions