[Security Review] π‘οΈ Daily Security Review β gh-aw-firewall (2026-09-11) #8452
Closed
Replies: 1 comment
|
This discussion was automatically closed because it expired on 2026-09-18T12:40:16.407Z.
|
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
AWF's core L7 egress-control design is sound: least-privilege capability drops, dedicated
iptables-initcontainer forNET_ADMINisolation, chroot + selective bind mounts, non-root proxy/sidecar users, ReDoS-safe wildcard-to-regex conversion, and IP-literal CONNECT blocking. No critical, exploitable vulnerabilities were found in the code paths reviewed. Findings below are hardening opportunities (defense-in-depth gaps, documented residual risks, and an anomaly in the escape-test pipeline itself).Note on Phase 1 input:
/tmp/gh-aw/escape-test-summary.txtdid not contain firewall escape-test results. It contained an unrelated CI log excerpt for a different workflow ("Secret Digger (Copilot)", run29286879560), showing a prompt-injection attempt that the agent correctly refused vianoop. This is reported as a pipeline/tooling anomaly, not a firewall finding β see "Anomaly" note below.π Findings from Firewall Escape Test (data anomaly)
The pre-fetched file was not an escape-test summary. Command run:
Output was CI log lines for
Secret Digger (Copilot)(workflowsecret-digger-copilot, run29286879560), which correctly triggeredthreat_detectedand anooprefusal ("scan for secrets... exfiltrate via GitHub issues" β prohibited). This confirms the safe-outputs / threat-detection pipeline is functioning as designed, but it means no genuine firewall-escape evidence was available to cross-reference in this run. Recommend the upstream step that populatesescape-test-summary.txtbe checked for a caching/path bug pointing at the wrong workflow's artifact.π‘οΈ Architecture Security Analysis
Network Security
containers/agent/setup-iptables.shbuilds NAT/FILTER rules: DNAT 80/443βSquid, explicitDANGEROUS_PORTSblock (SSH/SMTP/DB ports), DNS locked to configured upstreams only + Docker embedded DNS (127.0.0.11), IPv6 explicitly disabled inside the agent netns (disable_ipv6()) to prevent IPv6 egress bypassing IPv4-only DNAT rules.is_valid_port_spec) is a second, fail-closed validator forAWF_VALID_ALLOW_HOST_PORTS/AWF_VALID_HOST_SERVICE_PORTS, defending against a stale/mismatched CLI passing raw unvalidated env vars β good defense-in-depth.src/squid/config-generator.tsblocks CONNECT to raw IPv4/IPv6 literals (dst_ipv4/dst_ipv6regex ACLs) to stop domain-allowlist bypass via direct IP.allow_host_access_to_gateway) are scoped to ports 80/443 + explicit--allow-host-ports, not a blanket allow β reduces the blast radius of the MCP/host-access exception.Container Security
SYS_CHROOT+SYS_ADMINadded (for chroot + procfs mount withhidepid=2),NET_ADMINexplicitly not granted (moved to the short-livediptables-initsidecar) β confirmed insrc/services/agent-service-build.test.ts:100-118andsrc/services/agent-service.ts:72-76.USER proxy/apiproxy/cliproxy) withcap_drop: ALL(or targeted drops) andno-new-privileges(src/services/service-security.test.ts).containers/agent/seccomp-profile.json) is an allowlist (SCMP_ACT_ERRNOdefault) rather than a denylist β stronger posture.AWF_SKIP_CAP_DROP(src/capability-filter.ts:55-109) is an escape hatch that removescap_droprequirements entirely when set. It exists to accommodate restricted CI runners lacking certain capabilities in their bounding set, but it is a env-var-gated global weakening of container hardening with no runtime warning banner found in the reviewed code β flagged below.Domain Validation
src/domain-patterns.ts:70-122(wildcardToRegex) uses a bounded character class ([a-zA-Z0-9.-]*) instead of.*specifically to prevent ReDoS β a deliberate, well-documented mitigation.Input Validation
entrypoint.sh:setup_user_identity()validatesAWF_USER_UID/AWF_USER_GIDare numeric and explicitly rejects0(root) β prevents privilege-drop defeat via crafted env vars.entrypoint.shAPI-key-helper / Maven / Gradle config writers usenode -ewith JSON.parse/stringify (not string concatenation into shell) when merging into existing JSON files, reducing injection risk in the common path; the initial-create fallback paths (echo "{\"apiKeyHelper\":\"$CLAUDE_CODE_API_KEY_HELPER\"}" > "$config_file") interpolate an env var into a JSON literal without escaping quotes/backslashes β low risk since the value is host-supplied, not attacker-controlled from inside the sandbox, but noted as fragile.execa/execFileusage throughoutsrc/consistently passes argv arrays (not shell strings), avoiding shell-injection;eslint-rules/no-unsafe-execa.test.jsindicates a custom lint rule enforces this pattern β good preventative control.setup-iptables.sh"Bypass Squid for self-directed traffic" RETURN rule keyed on$AGENT_IP/etc/resolv.confafter container start to point at attacker DNSentrypoint.shwrites resolv.conf once at startup; no runtime immutability (chattr +i) observedUser-Agentinaudit_jsonlformat ("could break JSON parsing")config-generator.tslogformat commentAWF_SKIP_CAP_DROP/AWF_SKIP_IPTABLES_INITsilently downgrade isolationcapability-filter.ts:55,entrypoint.sh:163cache_mem 64 MB/ no explicitmaxconnlimits observed in reviewed excerptconfig-generator.tsenclave-mcp-servercontrol channel guarded by "capability authentication alone" (per CLAUDE.md, tracked asgithub/gh-aw#59268, closed not-planned)π― Attack Surface Map
containers/agent/setup-iptables.sh(DNAT 80/443βSquid)--allow-host-ports/AWF_ENABLE_HOST_ACCESSusedsrc/squid/config-generator.ts,src/domain-patterns.tscontainers/agent/entrypoint.sh,src/capability-filter.tsAWF_SKIP_CAP_DROPglobal bypass; UID/GID remap trusts numeric env var (validated against 0, not against other privileged ranges)src/host-iptables*.ts,containers/agent/setup-iptables.shcontainers/agent/entrypoint.sh(1767 lines)github-repository-delegation-v1, AWF host-loopback control listenergithub/gh-aw#59268) as accepted riskπ Evidence Collection
Commands run (collapsed)
β Recommendations
High
AWF_SKIP_CAP_DROPorAWF_SKIP_IPTABLES_INITis active, so degraded-isolation runs are unmistakable in CI logs and audit trails.Medium
chattr +i) of/etc/resolv.confpost-boot to reduce runtime DNS-exfil tampering risk, or document why it's accepted (single-use container to reduce standing risk).entrypoint.shfallback config writers (configure_claude_api_key), even though the values are host-supplied today.User-Agenttoaudit_jsonlwith proper JSON string escaping (e.g., via Squid's%{User-Agent}>hcombined with a sanitizing wrapper) to close the forensic gap noted in the logformat comment.Low
entrypoint.sh(1767 lines) into sourced modules by concern (DNS, SSL, JVM proxy, Claude config) to reduce review/maintenance risk, mirroring the modular pattern already used insrc/squid/andsrc/services/.github/gh-aw#59268(capability-only auth on the enclave control listener) even though currently closed as not-planned; revisit if enclave dynamic-repo admission usage increases.π Security Metrics
setup-iptables.sh(~300+ lines),entrypoint.sh(~420 of 1767 lines read directly, remainder referenced via grep),src/squid/config-generator.ts(217 lines),src/domain-patterns.ts(137 lines),src/capability-filter.ts, container Dockerfiles (squid/api-proxy/cli-proxy/agent/enclave/build-tools), seccomp profile.npm auditcould not run against the public registry in this sandbox (403 Forbidden from internal package mirror) β dependency-vulnerability coverage is not verified this cycle; recommend runningnpm auditin an environment with registry access.Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
msfeed25.pkgs.visualstudio.comTo allow these domains, add them to the
network.allowedlist in your workflow frontmatter:See Network Configuration for more information.
All reactions