[Security Review] Daily Security Review – 2026-04-14 #1973
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-04-21T13:12:55.248Z.
|
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 review covers a comprehensive, evidence-based security analysis of the
gh-aw-firewallrepository as of 2026-04-14. The codebase implements a layered defense strategy: host-level iptables via aDOCKER-USERchain, an in-container DNAT/filter stack, domain ACL enforcement through Squid, capability drops, seccomp, and one-shot token protection. The overall posture is strong with several well-designed defence-in-depth mechanisms. Six findings are reported, two rated medium severity and four low.Security lines analysed: 4,773 across seven critical files
Attack surfaces identified: 9
Findings: 2 Medium · 4 Low · 0 High/Critical
🔍 Findings from Firewall Escape Test
The most recent
Secret Digger (Copilot)run (workflow run24273493151, completed 2026-04-11) concluded withGH_AW_SECRET_VERIFICATION_RESULT: successand only noop outputs. The agent found no accessible secrets, confirming that credential-hiding via/dev/nulloverlays and one-shot token protection are working as intended. No novel escape vectors were discovered in that run.🛡️ Architecture Security Analysis
Network Security Assessment
Host-level (DOCKER-USER chain —
src/host-iptables.ts)The
FW_WRAPPERchain correctly allows only:Container-level (setup-iptables.sh)
IPv6 is explicitly disabled via sysctl at line 46:
The default DROP at the end of the container filter chain and the host-level default REJECT still block these ports for all non-HTTP/HTTPS traffic. The practical risk is low because only ports 80 and 443 (and any explicitly allowed ports) are ever DNAT'd to Squid. However, the inconsistency could introduce a gap if the overall default-deny rules are ever changed.
Finding L-1 — ICMP not explicitly blocked at container iptables level (Low)
setup-iptables.shonly addsDROPrules for TCP and UDP. ICMP is not explicitly handled in the container's OUTPUT chain. The host-levelFW_WRAPPERdefault REJECT catches all other traffic (including ICMP), so external ICMP exfiltration is blocked. Within the AWF subnet (e.g., pinging Squid at 172.30.0.10), ICMP is reachable from the agent. This is a very limited channel with no practical exfiltration value but deviates from the principle of explicit deny.Container Security Assessment
Capability management (
src/docker-manager.tslines 1385–1410)This is a well-designed split: NET_ADMIN never enters the agent container. SYS_CHROOT and SYS_ADMIN are granted to the agent for the procfs mount and chroot, then dropped via
capsh --dropbefore user code runs (entrypoint.sh lines 806–840).Finding M-2 — AppArmor set to
unconfinedon agent container (Medium)ptrace,process_vm_readv,process_vm_writev, andkexec_loadare correctly blocked.mount,unshare,setns, andcloneare allowed because they are required. Without CAP_SYS_ADMIN (dropped before user code) and without CAP_NET_ADMIN (never granted), the attack surface from these syscalls is materially limited.Finding L-2 —
unshare/setnsallowed in seccomp + user namespace consideration (Low)unsharewithoutCLONE_NEWUSERrequires capabilities that are dropped before user code runs. However,CLONE_NEWUSER(user namespace creation) does not require any capability in the kernel default configuration. An attacker could useunshare --userto create a new user namespace with a UID 0 mapping, then attempt further privilege escalation. Docker's default--userns-remapis not in use here. The seccomp profile does not restrictcloneflags. The practical risk is mitigated byno-new-privileges:trueand the seccomp profile, but a dedicated seccomp block onCLONE_NEWUSERwould be a stronger control.Domain Validation Assessment
src/domain-patterns.tslines 153–210The
validateDomainOrPattern()function rejects:\because URL patterns legitimately use it, but the domain validator adds it*,*.*, patterns matching only[*.]+The
assertSafeForSquidConfig()insquid-config.tsprovides a defence-in-depth check at the point of interpolation. This two-layer validation is well-designed.Finding L-3 — Unicode/IDN domain normalization not validated (Low)
The validators check ASCII special characters but do not normalise or reject Unicode/IDN domains. An attacker with control of
--allow-domainsvalues could supply an IDN domain (e.g.,xn--githu-yta.com) that looks similar to a legitimate domain but reaches a different host. This is low risk because--allow-domainsis a trusted CLI input (operator-controlled), but an IDN spoofing check would improve supply-chain trust.Input Validation Assessment
Shell argument handling (
src/cli.tslines 1578–1601)The comment at line 1597 explains the design choice clearly. Single-argument mode passes the command string verbatim to
bash -c, which is correct for shell variable expansion. The trust assumption is documented. No injection vectors from user-controlled external sources (e.g., issue body, PR title) were found in the input path.execausage: All subprocess calls insrc/docker-manager.tsuseexecawith argument arrays (not shell interpolation), preventing shell injection.Finding L-4 —
AWF_USER_UID/AWF_USER_GIDvalidated only for format, not minimum value (Low)AppArmor unconfined rationale
Escape test run context
From
/tmp/gh-aw/escape-test-summary.txt(workflow run 24273493151):Secret Digger (Copilot), run 2026-04-11GH_AW_AGENT_CONCLUSION: successGH_AW_SECRET_VERIFICATION_RESULT: success✅ Recommendations
🔴 Critical
None identified.
🟠 High
None identified.
🟡 Medium
M-1 — Sync DANGEROUS_PORTS between
setup-iptables.shandsquid-config.tsAdd the 6 missing ports (5984, 6984, 8086, 8088, 9200, 9300) to the
DANGEROUS_PORTSarray incontainers/agent/setup-iptables.sh. Consider extracting a single source-of-truth and auto-generating both lists from it (e.g., a JSON file read at build time).M-2 — Replace
apparmor:unconfinedwith a minimal custom AppArmor profileCreate a custom AppArmor profile that permits
mountonly forprocfilesystem types and only beforecapsh --dropexecutes. This eliminates the unprotected window during container startup without impacting functionality.🟢 Low
L-1 — Add explicit ICMP DROP to container iptables
Add
iptables -A OUTPUT -p icmp -j DROPtosetup-iptables.shfor completeness and defence-in-depth, even though the host-level FW_WRAPPER already catches this.L-2 — Restrict
CLONE_NEWUSERin seccomp profileConsider using a seccomp
argsfilter oncloneandunshareto denyCLONE_NEWUSERflag (value0x10000000) while still permitting other clone/unshare operations. This closes the unprivileged user namespace creation vector.L-3 — Add IDN/Punycode normalization to domain validation
In
validateDomainOrPattern(), run inputs through an IDN normalization step (or rejectxn--prefixes from user-supplied plain domains) to prevent IDN homograph attacks in the domain whitelist.L-4 — Block system UIDs (1-999) in entrypoint.sh
Mirror the TypeScript validation at
docker-manager.ts:109incontainers/agent/entrypoint.shto rejectHOST_UID < 1000when the image is used directly.📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions