[Security Review] Daily Security Review and Threat Modeling — 2026-03-31 #1511
Replies: 3 comments
-
|
This discussion was automatically closed because it expired on 2026-04-07T14:24:02.192Z.
|
Beta Was this translation helpful? Give feedback.
-
|
🔮 The ancient spirits stir in the firewall halls; this smoke-test agent has walked these runes. The omens are recorded, the circuit is witnessed, and the watchful wards remain awake.
|
Beta Was this translation helpful? Give feedback.
-
|
🔮 The ancient spirits stir in the firewall’s lattice.
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
📊 Executive Summary
This review covers a comprehensive security analysis of the AWF (Agentic Workflow Firewall) codebase conducted on 2026-03-31. The system implements defense-in-depth with multiple layers: host-level iptables, Squid L7 proxy, container capability restriction, seccomp profiling, and LD_PRELOAD credential isolation. The overall security posture is strong, with one Critical finding and several medium/low observations.
🔍 Phase 1: Firewall Escape Test Status
The
security-reviewworkflow is compiled and active. Nofirewall-escape-testworkflow was found in the workflow registry. The existingsecret-digger-*workflows (claude, codex, copilot variants) run hourly and cover credential exfiltration scenarios.🛡️ Architecture Security Analysis
Network Security Assessment
✅ Strengths:
The iptables architecture is well-layered:
src/host-iptables.ts) — creates a dedicatedFW_WRAPPERchain that restricts egress from the Docker bridge. The rule ordering is correct: allow Squid → allow DNS → allow API proxy → block UDP → default-deny. Evidence:Container NAT DNAT redirect (
containers/agent/setup-iptables.sh) — all port 80/443 traffic is force-redirected to Squid at172.30.0.10:3128before the agent starts, covering proxy-unaware tools.IPv6 hardening — when
ip6tablesis unavailable, IPv6 is disabled via sysctl to prevent an unfiltered bypass path (src/host-iptables.ts:119-129).DNS exfiltration prevention — DNS queries are restricted to configured upstream servers only; non-configured servers are rejected by the DOCKER-USER chain (
src/host-iptables.ts:363-388).Metadata endpoint blocking — link-local
169.254.0.0/16(AWS/GCP metadata service) is explicitly blocked (src/host-iptables.ts:499-503).The FW_WRAPPER chain allows any TCP traffic that exits through Squid. However, Squid only allows traffic to whitelisted domains by name. If an agent resolves an internal RFC-1918 IP address through a domain on the allow list, Squid's domain ACL enforcement by hostname means the IP-level traffic would depend on Squid's DNS resolving correctly. This is an accepted trade-off in the current architecture.
Container Security Assessment
✅ Strengths:
NET_ADMINis never granted to the agent container; it is isolated to the short-livedawf-iptables-initinit container that exits after iptables setup.NET_RAWis explicitly dropped from the agent (src/docker-manager.ts:1172), preventing raw socket creation that could bypass DNAT rules.no-new-privileges:trueis applied to all containers (src/docker-manager.ts:1183).6g/ 1000 PIDs, Squid/init get128m/ 50 PIDs (src/docker-manager.ts:1191, 1325).SYS_CHROOTandSYS_ADMINare dropped viacapshbefore user code runs (containers/agent/entrypoint.sh:353).docker-stub.shblocks Docker-in-Docker by replacing thedockerbinary with an error stub.apparmor:unconfined:The agent container explicitly disables AppArmor confinement. This removes a key kernel-level MAC layer that would otherwise prevent privilege escalation techniques like
mountnamespace manipulation. Combined withSYS_ADMINbeing temporarily present (dropped just before user code), this represents a window of risk.unshare,mount, andsetnsallowed in seccomp profile:# Evidence from analysis Status of security-sensitive syscalls: unshare: ALLOWED mount: ALLOWED setns: ALLOWED ptrace: BLOCKED process_vm_readv: BLOCKEDmountis intentionally allowed for procfs mounting in chroot mode. However,unshare(create new namespace) combined withsetns(join another namespace) are allowed. Without AppArmor, a process with sufficient capabilities could use these to attempt network namespace manipulation during the window whenSYS_ADMINis present.The seccomp profile (
containers/agent/seccomp-profile.json) hasdefaultAction: SCMP_ACT_ERRNO(allow-list model) which is correct. However,mount,unshare, andsetnsare all in the allow list.Domain Validation Assessment — 🚨 CRITICAL FINDING
CRITICAL — Squid configuration injection via newline-embedded domain names:
validateDomainOrPatterninsrc/domain-patterns.tsdoes not reject domain strings containing newline characters (\n,\r\n) or other whitespace. Since domains are interpolated directly into Squid's configuration file, an attacker who controls the--allow-domainsargument can inject arbitrary Squid directives.Impact: An
http_access allow allline injected before thehttp_access deny allterminal rule causes Squid to allow traffic to any domain, completely bypassing the whitelist. The same injection works via--block-domains.Attack vector: This is exploitable when:
awfis invoked from a CI/CD pipeline or script where the domain list is sourced from an untrusted input (e.g., an issue body, PR description, environment variable set by a workflow trigger, or agentic task parameter).awfinvocation.Proof of exploitability (
src/docker-manager.ts:1744): The Squid config is base64-encoded and injected as an environment variable — the malicious newline survives encoding/decoding intact.Files affected:
src/domain-patterns.ts(missing newline check invalidateDomainOrPattern),src/squid-config.ts(no sanitization on domain interpolation).Input Validation Assessment
✅ Strengths:
isValidPortSpec()preventing numeric injection.escapeShellArg()properly single-quote-escapes arguments (src/cli.ts:936).[a-zA-Z0-9.-]*instead of.*to prevent ReDoS (src/domain-patterns.ts:87).src/domain-patterns.ts:246).containers/agent/entrypoint.sh:25-31).The DLP system (
src/dlp.ts) uses Squidurl_regexACLs, which match only the URL of HTTP requests. Credential values embedded in POST body JSON (e.g.,{"token": "ghp_xxxxx..."}) are not inspected. An agent that exfiltrates credentials through a POST body to an allowed domain (e.g., a GitHub API endpoint) would not be detected by DLP.✅ Recommendations
🔴 Critical — Fix Immediately
C1: Reject newlines and whitespace in domain names (
src/domain-patterns.ts)Add a check in
validateDomainOrPatternbefore the existing checks:Additionally, add a sanitization guard in
generateSquidConfigas defense-in-depth — escape or reject any domain that would produce a multi-line Squid ACL entry when interpolated.🟠 High — Fix Soon
H1: Enable AppArmor or provide a custom profile for the agent container
Rather than
apparmor:unconfined, consider using Docker's default AppArmor profile (docker-default) or a custom profile that restrictsmountand namespace syscalls. The current justification forunconfinedshould be documented if it is intentional. Ifunconfinedis required for chroot to work on all host distributions (some AppArmor profiles block chroot), document this and add a compensating control.H2: Extend DLP to inspect request body content via ICAP or Squid response_body_max_size
The current DLP implementation only inspects URLs. Adding an ICAP adapter (e.g., C-ICAP) to Squid would allow credential pattern detection in POST bodies. Alternatively, document this limitation prominently so operators understand that
--enable-dlpdoes not protect against body-level exfiltration.🟡 Medium — Plan to Address
M1: Add caller authentication to the API proxy sidecar
The API proxy (
containers/api-proxy/server.js) accepts any unauthenticated HTTP request from the172.30.0.0/24subnet. While the Squid firewall restricts which endpoints the proxy can reach, a compromised agent could still make arbitrary API calls through it. Adding a shared secret (e.g., a UUID injected as an environment variable and required as a bearer token) would prevent other processes that might gain access to the network subnet from using the proxy.M2: Restrict
unshareandsetnsin the seccomp profileThese syscalls are not needed by the agent's user workloads. Blocking them in
containers/agent/seccomp-profile.jsonreduces the attack surface during theSYS_ADMINcapability window without breaking any documented functionality.M3: Document the window between
startContainersand iptables-init completionThe init container writes
/tmp/awf-init/readyand the agent'sentrypoint.shwaits for it before executing the user command. This architecture correctly ensures the agent cannot run before iptables is set up. However, this guarantee should be documented inAGENTS.mdas it is a critical security invariant.🟢 Low — Nice to Have
L1: Consider structured logging with log integrity (append-only, signed)
Squid logs currently use Unix timestamps and are written to a shared volume. Adding log signing or shipping to an append-only store would improve forensic non-repudiation (STRIDE: Repudiation).
L2: Add rate-limiting at the Squid layer in addition to the API proxy layer
The API proxy has per-provider rate limits, but the Squid proxy itself has no connection or bandwidth limits. A compromised agent could flood outbound connections to allowed domains. Squid's
delay_poolsormax_filedesccan be used for this.L3: Document
apparmor:unconfinedas a known trade-offAdd a comment in
src/docker-manager.ts:1185explaining why AppArmor is disabled (e.g., cross-distro compatibility with chroot operations) and what compensating controls exist (seccomp, capability drop, no-new-privs).📈 Security Metrics
cli.ts,docker-manager.ts,host-iptables.ts,squid-config.ts,domain-patterns.ts,dlp.ts)entrypoint.sh,setup-iptables.sh,docker-stub.sh,seccomp-profile.json,server.js)Beta Was this translation helpful? Give feedback.
All reactions