[Security Review] Daily Security Review and Threat Modeling — 2026-04-06 #1700
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-04-13T13:15:23.894Z.
|
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 the
gh-aw-firewallcodebase at version 0.23.1. The architecture demonstrates strong security-first design: layered network controls (host-level DOCKER-USER chain + container-level iptables + Squid L7 ACLs), meaningful capability dropping, one-shot token protection, and DLP scanning. No critical vulnerabilities were found. Two medium-severity findings deserve attention — Squid port binding scope and incomplete container-level ICMP filtering.No prior "Firewall Escape Test Agent" workflow was found in the workflow registry (no matching workflow name). The security review is based entirely on source analysis.
mount/unshare/setnsallowed in seccomp while AppArmor is unconfined--enable-dindgrants Docker socket / firewall bypass🛡️ Architecture Security Analysis
Network Security Assessment
The network stack uses a defense-in-depth model:
Host-level DOCKER-USER chain (
src/host-iptables.ts) — all container egress hitsFW_WRAPPER, which allows only: Squid IP, DNS to configured upstreams, established/related, localhost, and explicitly allowed host gateway IPs. All other traffic is REJECTed at line 528-530:Container-level iptables (
containers/agent/setup-iptables.sh) — DNAT redirects HTTP/HTTPS to Squid, blocks dangerous ports (22/25/3306/6379 etc.), default-drops TCP and UDP.Squid L7 ACL (
src/squid-config.ts) — domain allowlist enforced beforehttp_access allow localnet, direct-IP CONNECT denied, safe-port enforcement.Finding 1 — Medium: Squid port 3128 bound to all host interfaces
Followed by
http_access allow localnet(line 601) after domain deny rules. This means a co-located process or another container on the host's Docker bridge could use this Squid instance as a forward proxy for the whitelisted domains. The domain whitelist still limits blast radius, but unintended proxy access is possible.Recommendation: Bind Squid only to the AWF bridge IP: change
ports: ["3128:3128"]toports: ["172.30.0.10:3128:3128"]. Alternatively, restrictacl localnetto only172.30.0.0/24.Finding 2 — Medium: Container OUTPUT chain does not block ICMP
setup-iptables.shonly adds explicit DROP rules for TCP and UDP:The container's default OUTPUT policy is ACCEPT (Docker default), so ICMP egress (including ICMP tunnel tools like
icmptunnel,ptunnel) is not blocked at the container firewall level. ICMP data exfiltration is blocked by the host-levelFW_WRAPPERdefault REJECT (src/host-iptables.ts:528-530), but the container's own defense-in-depth is incomplete.Recommendation: Add explicit ICMP blocks in
setup-iptables.sh:Container Security Assessment
Capability Model — Well-designed
The init container (
awf-iptables-init) takesNET_ADMIN+NET_RAWand sets up iptables. The agent container never receivesNET_ADMIN. Chroot mode grantsSYS_CHROOT+SYS_ADMIN(for procfs mount) then drops them viacapshbefore user code:Finding 3 — Low-Medium:
mount/unshare/setnsin seccomp while AppArmor is unconfinedAppArmor is disabled because Docker's default AppArmor profile blocks
mount(needed for/host/proc). With AppArmor unconfined andmountallowed in seccomp, the only enforcement preventing namespace operations post–SYS_ADMINdrop is the Linux capability system. IfSYS_ADMINdrop viacapshwere to fail (e.g., binary not found on some host configurations, capsh path confusion), the syscall-level guardrail would be absent.Recommendation: Consider a custom AppArmor profile that permits only the specific
procfilesystem mount rather thanunconfined. This would re-enable AppArmor defense without blocking the required procfs mount.Finding 4 — Low: 5-second token exposure window
Tokens (ANTHROPIC_API_KEY, GITHUB_TOKEN, etc.) remain in
/proc/1/environfor 5 seconds after the agent subprocess starts. A malicious process that reads/proc/1/environbeforesleep 5completes can capture them. This is partially mitigated by the one-shot-token library (LD_PRELOAD=/usr/local/lib/one-shot-token.so), which makes the values unavailable for re-read. However, the parent process (PID 1 in the container) still holds them in its environ for the sleep duration.Recommendation: Reduce the sleep window or replace it with a synchronization mechanism (e.g., the agent writes a ready signal once tokens are cached).
Finding 5 — High (opt-in):
--enable-dindgrants Docker socket and firewall bypassInput Validation / Injection Assessment — Good
execais used throughoutsrc/docker-manager.tswith argument arrays (not shell strings), preventing shell injection in Docker commands. Domain parameters are validated before Squid config interpolation. UID/GID values are validated as numeric and non-zero inentrypoint.sh:Container ICMP not blocked (setup-iptables.sh:415-418)
iptables -A OUTPUT -p tcp -j DROP iptables -A OUTPUT -p udp -j DROP # No ICMP rule — default policy is ACCEPTSeccomp mount/unshare/setns allowed
Token unset timing (entrypoint.sh:764-768)
📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions