[Security Review] Daily Security Review and Threat Modeling — 2026-04-05 #1678
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-04-12T13:51:01.839Z.
|
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 deep, evidence-based security review of the
gh-aw-firewallcodebase. No prior "Firewall Escape Test Agent" workflow was found in the repository history; analysis relies entirely on code inspection.Overall Security Posture: Strong — The architecture demonstrates mature defense-in-depth thinking with multiple overlapping controls. Four findings are documented below; none are critical.
src/host-iptables.ts,containers/agent/setup-iptables.sh,src/squid-config.ts,src/domain-patterns.ts,src/cli.ts,containers/agent/entrypoint.sh,containers/agent/seccomp-profile.json,src/dlp.ts,src/docker-manager.ts🔍 Findings from Prior Firewall Escape Testing
No "firewall-escape-test" or equivalent workflow was found in the repository. The
security-reviewworkflow exists but logs were not accessible via the agentic-workflows tool in this environment. All findings below derive from direct codebase analysis.🛡️ Architecture Security Analysis
Network Security Assessment
Strengths:
DOCKER-USERchain + container-levelOUTPUTchainsysctlinside the container to prevent IPv6 egress bypass127.0.0.11) only; direct queries to non-whitelisted servers are blocked169.254.0.0/16) explicitly blocked in host-level chainContainer-level iptables flow (
containers/agent/setup-iptables.sh):ICMP: Not explicitly blocked at the container OUTPUT chain level (only TCP/UDP are dropped). However, the host-level catch-all
REJECTinFW_WRAPPERdoes intercept ICMP from the container bridge. Combined withNET_RAWcapability drop (preventing raw socket creation), ICMP tunneling is not practically exploitable.Container Security Assessment
Capabilities (agent container):
NET_ADMIN— never granted (handled by separateawf-iptables-initinit container)NET_RAW,SYS_PTRACE,SYS_MODULE,SYS_RAWIO,MKNOD— explicitly droppedSYS_CHROOT,SYS_ADMIN— granted initially, then dropped viacapshbefore user code runs (chroot mode)Seccomp profile (
containers/agent/seccomp-profile.json):SCMP_ACT_ERRNO(allow-list model)ptrace,process_vm_readv/writev,kexec_load,init_module,pivot_root,umount, kernel key operationsunshare,setns,mount,clone,socket,connect— all standard and required for containerized workloadsOne-shot token protection: Credentials are cached in-process via
LD_PRELOAD=/usr/local/lib/one-shot-token.so, then unset from the parent shell environment after a 5-second initialization window (sleep 5).Docker socket: Masked by bind-mounting
/dev/nullover/host/var/run/docker.sockand/host/run/docker.sockunless--enable-dindis explicitly used (documented as a firewall bypass vector).Domain Validation Assessment
validateDomainOrPattern()insrc/domain-patterns.tsrejects whitespace, null bytes, quotes, semicolons, backticks, hash characters, backslashes*,*.*, patterns of only wildcards and dots) are explicitly rejected[a-zA-Z0-9.-]*(not.*) to prevent ReDoSassertSafeForSquidConfig()insrc/squid-config.tsprovides a second validation pass before Squid config interpolation(redacted)https://`) is handled before domain validationInput Validation Assessment
execa()with array arguments (no shell interpolation)command:field, not via shellisValidPortSpec()(rejects leading zeros, validates range bounds)entrypoint.shSTRIDE Analysis
squid-config.tsandsetup-iptables.shcreates audit log gapunshare+setnsallowed in seccomp; user namespaces + network namespaces could in theory create bypasssrc/cli.ts:1360--enable-dlpflagcontainers/agent/entrypoint.sh:765,812apparmor:unconfined) removes MAC layer; relies entirely on DAC+capabilitiessrc/docker-manager.ts:1242--enable-dindmounts real Docker socket, allowing new containers without firewallsrc/docker-manager.ts:999-1006FW_BLOCKED_DANGEROUS_PORTaudit log omits 6 ports that Squid blocks🎯 Attack Surface Map
--allow-domainsCLI flagsrc/cli.ts,src/domain-patterns.ts--allow-host-portssrc/cli.ts:1337,setup-iptables.sh:320-358--enable-host-accesssrc/cli.ts:1335,setup-iptables.sh:181-295--enable-dindsrc/docker-manager.ts:999-1006--allow-host-service-portssrc/cli.ts:782,setup-iptables.sh:250-296--enable-api-proxy)src/host-iptables.ts:416-428AWF_SQUID_CONFIG_B64env varsrc/docker-manager.ts:475-487src/domain-patterns.ts:96-135*→ Squiddstdom_regexACL.*to prevent ReDoS--ssl-bumpCAsrc/squid-config.ts:138-245📋 Evidence Collection
F1: Dangerous ports list mismatch
Command:
Evidence in source:
src/squid-config.ts:16-30: DANGEROUS_PORTS includes 5984, 6984, 8086, 8088, 9200, 9300containers/agent/setup-iptables.sh:303-319: DANGEROUS_PORTS array omits these 6 portsAssessment: The comment at
setup-iptables.sh:305states "Dangerous ports list - matches DANGEROUS_PORTS in squid-config.ts" but they do not match. The security impact is low because the finaliptables -A OUTPUT -p tcp -j DROPstill blocks these ports. However:[FW_BLOCKED_DANGEROUS_PORT]audit log entries (they generate generic[FW_BLOCKED_TCP]instead), creating an audit gapF2: unshare/setns allowed in seccomp profile
Command:
Assessment:
unshareis allowed in the seccomp profile (containers/agent/seccomp-profile.json). In theory, an unprivileged user can callunshare(CLONE_NEWNET)after creating a user namespace (unshare --user --net), placing themselves in a new network namespace where none of the AWF iptables rules apply. This is a known container-in-namespace escape pattern.Mitigating factors:
awfuserwithno-new-privileges:trueafter capability dropNET_ADMINis never granted to the agent containerNET_ADMIN, creating a new network namespace withCLONE_NEWNETwithout an enclosingCLONE_NEWUSERrequires privileges the agent doesn't haveno-new-privilegesflag prevents gaining capabilities via setuid binariesuserns_allowed_uids(kernel-configured)Residual risk: If the Linux kernel on the runner has unprivileged user namespace creation enabled (
kernel.unprivileged_userns_clone=1, which is the default on Ubuntu 22.04), the agent could create a new user+network namespace and bypass firewall rules within that namespace. Network traffic from inside that namespace would still traverse the host network stack where the DOCKER-USER chain applies, but the iptables rules applied toOUTPUTfrom the container's original network namespace would not apply in the new namespace.F3: DLP scanning opt-in
src/cli.ts:1360:DLP patterns in
src/dlp.tscover GitHub PATs, OpenAI keys, Anthropic keys, AWS access key IDs, Google API keys. These patterns only activate when--enable-dlpis passed. Without it, a compromised agent could exfiltrate API keys embedded in URL query parameters (e.g.,?token=ghp_xxxx) to whitelisted domains.F4: API proxy port range includes port 10003
src/host-iptables.ts:416-428:The iptables port range
10000:10004includes port 10003, which is not a defined API proxy port. If any service accidentally starts on port 10003 of the API proxy container, it would be reachable from the agent. Impact is limited to the API proxy IP (172.30.0.30) only and only when--enable-api-proxyis used.✅ Recommendations
Medium Priority
M1 — Sync dangerous ports lists between
squid-config.tsandsetup-iptables.shAdd the 6 missing ports (5984, 6984, 8086, 8088, 9200, 9300) to
DANGEROUS_PORTSincontainers/agent/setup-iptables.sh. This ensures audit logs consistently identify these ports as dangerous (not just generic blocked TCP), and validates that the comment "matches DANGEROUS_PORTS in squid-config.ts" is accurate.M2 — Consider blocking unprivileged user namespaces in seccomp
Add a seccomp rule to restrict
unshareandclone/clone3withCLONE_NEWUSERflag. Docker's default seccomp profile blocksclone(CLONE_NEWUSER)for this reason. Currently, unprivileged user namespace creation (if enabled on the host) could allow the agent to create an isolated network namespace.Alternatively, document that
kernel.unprivileged_userns_clone=0should be set on host runners.Low Priority
L1 — Enable DLP by default or document credential-in-URL risk
DLP scanning (
--enable-dlp) is opt-in but provides important protection against credential exfiltration in URLs. Consider making it the default and providing--disable-dlpas an escape hatch, or add prominent documentation about the risk.L2 — Use multiport matching for API proxy in host-iptables
Replace the port range
10000:10004with explicit--dports 10000,10001,10002,10004using-m multiportto avoid inadvertently allowing port 10003.L3 — Increase or make configurable the one-shot token initialization window
The 5-second
sleep 5inentrypoint.shassumes agent initialization (and one-shot-token library caching) completes within 5 seconds. On cold starts with large runtimes (JVM, large Node.js bundles), this window may be insufficient. Consider using a signal file or polling/proc/PID/environto confirm the token is absent rather than a fixed sleep.Informational
I1 — Re-enable AppArmor where possible
The agent container uses
apparmor:unconfinedto allow procfs mounting. Explore whether a custom AppArmor profile can allowmountfor procfs while restricting other AppArmor-enforceable behaviors. Removing AppArmor entirely removes a MAC layer that complements DAC and capabilities.📈 Security Metrics
Beta Was this translation helpful? Give feedback.
All reactions