Skip to content

Conversation

@Mossaka
Copy link
Collaborator

@Mossaka Mossaka commented Feb 6, 2026

Summary

  • When --enable-host-access is used, MCP gateway HTTP traffic to host.docker.internal gets DNAT'd to Squid's intercept port via iptables, where ORIGINAL_DST lookup fails (different network namespace), causing Squid crashes under heavy MCP load (~13,000 requests)
  • Adds iptables RETURN rule for the host gateway IP when AWF_ENABLE_HOST_ACCESS env var is set, so traffic to host.docker.internal bypasses Squid entirely
  • Passes AWF_ENABLE_HOST_ACCESS=1 from docker-manager.ts to the agent container when enableHostAccess is true

Changes

  1. containers/agent/setup-iptables.sh: After the Squid proxy bypass rule, resolves host.docker.internal and adds iptables -t nat RETURN + iptables OUTPUT ACCEPT rules for that IP
  2. src/docker-manager.ts: Sets AWF_ENABLE_HOST_ACCESS=1 env var in the agent container when config.enableHostAccess is true
  3. src/docker-manager.test.ts: 3 new tests verifying the env var is set/unset correctly

Test plan

  • npm run build succeeds
  • npm test — all 736 tests pass (including 3 new)
  • CI: build, lint, unit tests
  • CI: integration/smoke tests (validates host access + MCP gateway under load)

🤖 Generated with Claude Code

When --enable-host-access is used, MCP gateway HTTP traffic to
host.docker.internal gets DNAT'd to Squid's intercept port via iptables.
Squid's ORIGINAL_DST lookup fails because the NAT translation happens in
a different network namespace, causing Squid crashes under heavy MCP load
(~13,000 requests) with assertion failures in comm.cc.

Add iptables RETURN rule for the host gateway IP when AWF_ENABLE_HOST_ACCESS
is set, so traffic to host.docker.internal bypasses Squid entirely. Also add
a corresponding ACCEPT rule in the OUTPUT filter chain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 6, 2026 00:02
@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 82.12% 82.13% ➡️ +0.01%
Statements 82.16% 82.17% 📈 +0.01%
Functions 81.95% 81.95% ➡️ +0.00%
Branches 75.48% 75.48% ➡️ +0.00%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 83.0% → 83.0% (+0.04%) 82.3% → 82.3% (+0.04%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Smoke Test Results: Claude

Last 2 PRs: #5 (mossaka/log filtering), #1 (Improve links in readme)
✅ GitHub MCP
✅ Playwright (title: "GitHub · Change is constant...")
✅ File Writing
✅ Bash Tool

Status: PASS

AI generated by Smoke Claude

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Security Review Findings

I've identified a security concern with this PR that significantly extends the firewall's security boundary.

Issue: Complete Bypass of L7 Filtering for Host Traffic

Location: containers/agent/setup-iptables.sh:127-139

Code:

if [ -n "$AWF_ENABLE_HOST_ACCESS" ]; then
  HOST_GATEWAY_IP=$(getent hosts host.docker.internal | awk 'NR==1 { print $1 }')
  if [ -n "$HOST_GATEWAY_IP" ]; then
    echo "[iptables] Allow direct traffic to host gateway (${HOST_GATEWAY_IP}) - bypassing Squid intercept..."
    iptables -t nat -A OUTPUT -d "$HOST_GATEWAY_IP" -j RETURN
    iptables -A OUTPUT -d "$HOST_GATEWAY_IP" -j ACCEPT
  fi
fi

Security Impact:

  1. Unrestricted Host Access: When --enable-host-access is enabled, this rule allows the agent container to access ANY service running on the host machine without Squid's domain filtering:

    • Databases (PostgreSQL, MySQL, Redis on localhost)
    • Admin panels and local web services
    • Any TCP service listening on the host
    • No port restrictions - applies to all ports, not just MCP gateway
  2. Bypasses Core Security Model: The firewall's purpose is L7 egress control via domain allowlisting. This change exempts all host traffic from that control, similar to how Squid itself (172.30.0.10) is exempted via host-level rules in src/host-iptables.ts:216-220.

  3. Extends Security Boundary: The entire host machine becomes an implicit part of the allowlist when this feature is enabled.

Example Attack Scenario:

# User enables host access for legitimate MCP gateway on port 8080
awf --enable-host-access --allow-domains github.com 'curl (host.docker.internal/redacted)

# But the agent can now access ANY service on the host
awf --enable-host-access --allow-domains github.com 'curl (host.docker.internal/redacted)  # PostgreSQL
awf --enable-host-access --allow-domains github.com 'curl (host.docker.internal/redacted)  # Redis

Suggested Mitigations

  1. Port-Specific Bypass (Recommended): Only bypass Squid for specific ports used by MCP gateway:

    if [ -n "$AWF_ENABLE_HOST_ACCESS" ] && [ -n "$AWF_ALLOW_HOST_PORTS" ]; then
      # Parse ports and only bypass those specific ports
      iptables -t nat -A OUTPUT -d "$HOST_GATEWAY_IP" -p tcp -m multiport --dports "$AWF_ALLOW_HOST_PORTS" -j RETURN
      iptables -A OUTPUT -d "$HOST_GATEWAY_IP" -p tcp -m multiport --dports "$AWF_ALLOW_HOST_PORTS" -j ACCEPT
    fi
  2. Documentation Update: Clearly document in README.md and --help that --enable-host-access allows unrestricted access to ALL host services, not just MCP gateway.

  3. Consider Explicit Proxy: Instead of transparent interception (ORIGINAL_DST), use HTTP_PROXY/HTTPS_PROXY environment variables for MCP gateway traffic to avoid the namespace issue.

Decision

This is a defense-in-depth trade-off. While the change solves the Squid crash issue under load, it significantly weakens the security boundary for users who enable host access. The team should evaluate whether:

  • The use case justifies this security trade-off
  • Users understand the implications of --enable-host-access
  • Port-specific filtering is feasible as a middle ground

I recommend at minimum implementing suggestion #1 (port-specific bypass) to limit the exposure to only the intended ports.

AI generated by Security Guard

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Squid proxy crashes that occur when using --enable-host-access with MCP gateway traffic to host.docker.internal. The crashes happen because iptables redirects this traffic to Squid's intercept port, where ORIGINAL_DST lookup fails due to network namespace differences, causing instability under heavy load (~13,000 requests). The fix adds iptables rules to bypass Squid intercept for host gateway traffic entirely.

Changes:

  • Passes AWF_ENABLE_HOST_ACCESS environment variable to the agent container when enableHostAccess is enabled
  • Adds iptables NAT and OUTPUT rules to bypass Squid proxy for traffic destined to the host gateway IP
  • Includes comprehensive test coverage for the new environment variable behavior

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/docker-manager.ts Sets AWF_ENABLE_HOST_ACCESS environment variable when enableHostAccess is true
src/docker-manager.test.ts Adds 3 tests verifying AWF_ENABLE_HOST_ACCESS is set/unset correctly based on enableHostAccess config
containers/agent/setup-iptables.sh Resolves host.docker.internal and adds iptables rules to bypass Squid intercept for that IP when AWF_ENABLE_HOST_ACCESS is set

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// Enable host.docker.internal for agent when --enable-host-access is set
if (config.enableHostAccess) {
agentService.extra_hosts = ['host.docker.internal:host-gateway'];
environment.AWF_ENABLE_HOST_ACCESS = '1';
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with other boolean AWF environment variables (AWF_CHROOT_ENABLED and AWF_SSL_BUMP_ENABLED), this should use 'true' instead of '1'. While the shell script's test [ -n "$AWF_ENABLE_HOST_ACCESS" ] works with either value, maintaining consistency with existing patterns improves code maintainability.

Copilot uses AI. Check for mistakes.
if [ -n "$HOST_GATEWAY_IP" ]; then
echo "[iptables] Allow direct traffic to host gateway (${HOST_GATEWAY_IP}) - bypassing Squid intercept..."
iptables -t nat -A OUTPUT -d "$HOST_GATEWAY_IP" -j RETURN
iptables -A OUTPUT -d "$HOST_GATEWAY_IP" -j ACCEPT
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This OUTPUT filter chain rule is placed in the NAT configuration section. While it will function correctly, it should be moved to the OUTPUT filter chain rules section (after line 227, before the DROP rule at line 232) for better code organization and maintainability. The NAT section should only contain NAT table rules (lines 56-207), and the OUTPUT filter section should contain all OUTPUT filter rules (lines 209-232).

Copilot uses AI. Check for mistakes.
@Mossaka Mossaka closed this Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant