[Security Review] Daily Security Review and Threat Modeling — 2026-04-03 #1644
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-04-10T13:55:05.629Z.
|
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 analyzed 8,179 lines of security-critical code across 9 files covering network egress filtering, container hardening, domain validation, and credential protection. The overall security posture is strong, with multiple defense-in-depth layers. No critical vulnerabilities were identified. Three medium findings and two low findings were discovered, each with existing partial mitigations.
No Firewall Escape Test agent workflow found (
firewall-escape-testnot in workflow inventory). The closest related workflows aresecret-digger-*(hourly) andsecurity-guard(per PR). This review is self-contained.🔍 Escape Test Agent Report
No dedicated firewall-escape-test workflow exists in the repository. The security-review workflow (
security-review.md) itself IS this daily report. Secret digger workflows (claude, codex, copilot) run hourly and attempt to detect accidental credential exposure.🛡️ Architecture Security Analysis
Network Security Assessment
STRONG — Dual-layer egress control with defense-in-depth.
The system implements two independent firewall layers:
src/host-iptables.ts):DOCKER-USER → FW_WRAPPERchain enforces allowlist-only egress for all containers on theawf-net(172.30.0.0/24) bridge. Rules verified at lines 316, 489–530.containers/agent/setup-iptables.sh): NAT DNAT redirects all port 80/443 traffic to Squid:3128. Falls through to TCP/UDP DROP default deny.Evidence:
IPv6 is disabled at both layers:
setup-iptables.sh:sysctl -w net.ipv6.conf.all.disable_ipv6=1(line 48)src/host-iptables.ts:disableIpv6ViaSysctl()when ip6tables unavailableFW_WRAPPER_V6chain provides equivalent filteringDNS exfiltration prevention is robust: only whitelisted DNS servers (default: 8.8.8.8, 8.8.4.4) allowed; container resolv.conf points only to Docker embedded DNS (127.0.0.11).
IMDS/metadata endpoint protection:
169.254.0.0/16explicitly rejected at host layer (src/host-iptables.ts:498) and multicast (224.0.0.0/4) blocked.ICMP traffic (protocol 1) bypasses the container-level OUTPUT chain. It is caught by the host-level FW_WRAPPER REJECT rule, but defense-in-depth suggests blocking at both layers. ICMP tunneling tools (e.g.,
ptunnel-ng) could exploit any gap between host and container filtering.Container Security Assessment
STRONG — Multiple privilege reduction mechanisms applied in sequence.
Container security configuration (
src/docker-manager.ts:1195-1220):SYS_CHROOTandSYS_ADMINare dropped viacapshbefore user code runs (entrypoint.sh:357-760):NET_ADMINis never granted to the agent container — iptables setup is handled by the separateawf-iptables-initinit container that shares the network namespace. This cleanly separates privilege.One-shot token library (
containers/agent/one-shot-token/one-shot-token.c) interceptsgetenv()calls for sensitive tokens, caches on first call, and unsets from/proc/self/environ. XOR-obfuscated token names preventstringsreconnaissance.unshareandsetnssyscalls allowed in seccomp profileunshare --user --map-root-usercreates a user namespace where the process appears as UID 0. Whileno-new-privileges:trueprevents gaining capabilities, user namespaces have historically been vectors for kernel privilege escalation (CVE-2022-0492, CVE-2021-22555, etc.).setnscould allow entering another container's network namespace if the process can access/proc/(pid)/ns/netof a container without proper restrictions.Mitigations in place:
no-new-privileges:true,NET_ADMINnever granted,NET_RAWexplicitly dropped. The practical risk is low on patched kernels but represents a non-minimal attack surface.In non-chroot mode,
cap_sys_chrootandcap_sys_adminare present at runtime sincecapsh --drop=is a no-op with an empty list. The agent's command inherits these capabilities. Whileno-new-privileges:truelimits further escalation, running withSYS_ADMINallows mounting operations.Domain Validation Assessment
STRONG — Comprehensive input validation with defense-in-depth sanitization.
validateDomainOrPattern()(src/domain-patterns.ts) enforces:DOMAIN_DANGEROUS_CHARS:/[\s\0"';#\]/` (including backslash)*,*.*,*.*.*(too many wildcards)[a-zA-Z0-9.-]*character class (not.*)isDomainMatchedByPattern()assertSafeForSquidConfig()re-validates before Squid config interpolation (src/squid-config.ts:64):subdomains: falsein YAML rulesets has no effectSquid config always adds subdomain matching for plain domains (
src/squid-config.ts:73-79). A user who creates a ruleset withsubdomains: falseexpecting exact-match-only behavior will unknowingly allow all subdomains. This is documented in code comments but the API contract is misleading.Example: A ruleset entry
{ domain: "internal.example.com", subdomains: false }still allowsevil.internal.example.comin Squid ACL.Input Validation Assessment
STRONG — User command passed via Docker CMD array (no shell interpolation).
The user command goes through
src/cli.ts → src/docker-manager.tsas a Docker CMD array, not shell expansion.escapeShellArg()(src/cli.ts:938-951) properly single-quotes arguments.AWF_HOST_PATHinterpolated unquoted into shell heredocCommand 4: subdomains field no-op
✅ Zero known dependency vulnerabilities.
✅ Recommendations
🔴 Critical
None found.
🟠 High
None found.
🟡 Medium
M1 — Block ICMP at container-level iptables (Finding NET-1)
Add an explicit ICMP DROP rule to
containers/agent/setup-iptables.shbefore the final TCP/UDP drops:# After the existing DNS allow rules, before TCP/UDP DROP: iptables -A OUTPUT -p icmp -j DROPThis closes the ICMP covert channel (ptunnel-ng) at the container level as a defense-in-depth measure, rather than relying solely on the host FW_WRAPPER chain.
M2 — Remove or restrict
unshareandsetnsfrom seccomp ALLOW (Finding SEC-1)In
containers/agent/seccomp-profile.json, moveunshareandsetnsto the SCMP_ACT_ERRNO group (Group 3), or add conditions (e.g., only allowunsharewith non-CLONE_NEWUSERflags via seccomp arg filtering):{ "names": ["unshare", "setns"], "action": "SCMP_ACT_ERRNO", "comment": "Block namespace creation/entry - not needed for development tools" }Verify this doesn't break JVM, .NET CLR, or container runtimes that use
unsharefor sandboxing their own processes.M3 — Fix
subdomains: falseYAML ruleset field to actually work (Finding DOMAIN-1)In
src/rules.ts, implement the intended behavior:And update
src/squid-config.tsto generatedstdomain exact.com(without leading dot) for exact-match entries. Until fixed, add a warning in the CLI when a ruleset containssubdomains: false.🔵 Low
L1 — Quote
AWF_HOST_PATHinterpolation in heredoc (Finding INJ-1)In
containers/agent/entrypoint.sh, use a quoted heredoc for the PATH line to prevent variable expansion:Alternatively, validate
AWF_HOST_PATHinsrc/docker-manager.tsto reject values containing shell metacharacters.L2 — Drop
SYS_ADMINin non-chroot mode too (Finding SEC-2)Even in non-chroot mode,
SYS_ADMINcapability is not needed after container initialization. Consider removing it fromcap_addwhen chroot is not requested, or explicitly dropping it in the non-chroot path:This reduces the blast radius if another container escape mechanism is found.
📈 Security Metrics
🏅 Notable Security Strengths
NET_ADMINnever granted to agent — iptables init container pattern is elegant and correct/proc/self/environexposureptraceexplicitly ERRNO'd in both seccomp (Group 2) andcap_drop(SYS_PTRACE) — double-blocked/dev/nullbind mount — DinD requires explicit opt-inno-new-privileges:trueprevents setuid/setcap escalationassertSafeForSquidConfig()validates all interpolated valuesBeta Was this translation helpful? Give feedback.
All reactions