Skip to content

fix: restrict host gateway iptables bypass to allowed ports only#558

Open
Mossaka wants to merge 1 commit intomainfrom
fix/host-gateway-port-restriction
Open

fix: restrict host gateway iptables bypass to allowed ports only#558
Mossaka wants to merge 1 commit intomainfrom
fix/host-gateway-port-restriction

Conversation

@Mossaka
Copy link
Collaborator

@Mossaka Mossaka commented Feb 6, 2026

Summary

  • Restrict host gateway ACCEPT rule to ports 80, 443, and --allow-host-ports only (was: all ports)
  • Apply same port restriction to network gateway bypass
  • Add IPv4 format validation for dynamically resolved bypass IPs before iptables use
  • Mount chroot-hosts as read-only (:ro) since entry is pre-injected before mount

Security context

When --enable-host-access is set, setup-iptables.sh adds an iptables -A OUTPUT -d $HOST_GATEWAY_IP -j ACCEPT rule with no --dport restriction. This allows agent code to reach any service on the host machine — including databases (port 5432/3306/6379), admin panels, and other MCP servers — completely bypassing domain filtering and the dangerous-ports blocklist, with zero Squid logging.

Before (all ports open)

iptables -A OUTPUT -d 172.17.0.1 -j ACCEPT   # ALL ports!

After (restricted to allowed ports)

iptables -A OUTPUT -p tcp -d 172.17.0.1 --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp -d 172.17.0.1 --dport 443 -j ACCEPT
# + each port from --allow-host-ports

The NAT RETURN rule is unchanged — MCP traffic still bypasses Squid (which crashes on Streamable HTTP/SSE). Non-allowed port traffic now hits the final DROP rule in the FILTER chain.

Smoke test compatibility

  • smoke-chroot: uses --allow-domains localhost which auto-configures --allow-host-ports 3000,3001,...,9090 — all gateway ports are in the ACCEPT list
  • smoke-copilot/claude: MCP gateway runs on port 80 — always in the ACCEPT list
  • localhost keyword: auto-enables --allow-host-ports with common dev ports, so no change needed

Local verification

# Before fix: agent reaches host services on ALL ports
curl -sf http://host.docker.internal:7777 → SECRET_DATA_ON_PORT_7777 (EXIT=0)
curl -sf http://host.docker.internal:5432 → FAKE_POSTGRES_ON_PORT_5432 (EXIT=0)

# After fix: unauthorized ports blocked
curl -sf http://host.docker.internal:7777 → EXIT=28 (timeout/DROP)
curl -sf http://host.docker.internal:5432 → EXIT=28 (timeout/DROP)

# --allow-host-ports still works
awf --allow-host-ports 7777 ... → SECRET_DATA_ON_PORT_7777 (EXIT=0)

Test plan

  • npm test — 735/735 pass (updated test expects :ro mount)
  • npm run lint — pass
  • Local verification: unauthorized ports blocked, allowed ports work
  • CI: smoke-chroot, smoke-copilot, smoke-claude workflows

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings February 6, 2026 22:38
@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.05% 82.05% ➡️ +0.00%
Statements 82.09% 82.09% ➡️ +0.00%
Functions 81.95% 81.95% ➡️ +0.00%
Branches 75.54% 75.54% ➡️ +0.00%

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

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

C++ Build Test Results

Project CMake Build Status
fmt PASS
json PASS

Overall: PASS

All C++ projects built successfully.

AI generated by Build Test C++

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Node.js Build Test Results

Project Install Tests Status
clsx PASS PASS
execa PASS PASS
p-limit PASS PASS

Overall: PASS

All Node.js projects built and tested successfully.

AI generated by Build Test Node.js

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Smoke Test Results for Copilot

Status: PASS

cc @Mossaka

AI generated by Smoke Copilot

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Go Build Test Results

Project Download Tests Status
color 1/1 PASS
env 1/1 PASS
uuid 1/1 PASS

Overall: PASS

All Go projects successfully downloaded dependencies and passed tests.

AI generated by Build Test Go

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Deno Build Test Results

Project Tests Status
oak 1/1 ✅ PASS
std 1/1 ✅ PASS

Overall: ✅ PASS

All Deno tests completed successfully.

AI generated by Build Test Deno

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Build Test: Bun - FAILED ❌

Status: Test execution failed due to environment limitations

Test Results

Project Install Tests Status
elysia N/A FAILED
hono N/A FAILED

Overall: FAIL

Error Details

Root Cause: Bun requires a functional /proc filesystem which is not available in this execution environment.

Error: An internal error occurred (NotDir)

Environment Check:

  • Bun version: 1.3.8 ✅
  • Bun binary: Valid ELF executable ✅
  • /proc filesystem: Not mounted ❌

Impact: Unable to run bun install in any directory, preventing dependency installation and test execution for both elysia and hono projects.


This test cannot proceed without /proc access. Consider running in an environment with full /proc filesystem support.

AI generated by Build Test Bun

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Smoke Test: Claude Engine

Last 2 merged PRs:

Results:

  • ✅ GitHub MCP: Fetched PRs successfully
  • ❌ Playwright: Navigation timeout (60s)
  • ✅ File Write: Created /tmp/gh-aw/agent/smoke-test-claude-21768434701.txt
  • ✅ Bash: Verified file contents

Status: FAIL (Playwright timeout)

AI generated by Smoke Claude

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Chroot Version Comparison Test Results

Runtime Host Version Chroot Version Match?
Python 3.12.12 3.12.3 ❌ NO
Node.js v24.13.0 v20.20.0 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall Result: ❌ FAILED (not all runtimes matched)

The chroot environment successfully uses host-installed Go, but Python and Node.js versions differ between host and chroot environments.

AI generated by Smoke Chroot

Copy link

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

Tightens host-access networking controls by narrowing the iptables host/network gateway bypass to approved ports, adding basic IP validation before rule insertion, and hardening the chroot /etc/hosts mount mode.

Changes:

  • Restrict host/network gateway OUTPUT ACCEPT bypass to TCP ports 80/443 plus --allow-host-ports.
  • Add IPv4-format validation for dynamically resolved gateway IPs before using them in iptables.
  • Mount the generated chroot-hosts file as read-only and update the associated unit test.

Reviewed changes

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

File Description
src/docker-manager.ts Changes chroot /etc/hosts mount to read-only when using generated chroot-hosts.
src/docker-manager.test.ts Updates test expectation to match the new :ro mount.
containers/agent/setup-iptables.sh Adds IPv4 validation and restricts gateway bypass ACCEPT rules to allowed ports only.
Comments suppressed due to low confidence (1)

containers/agent/setup-iptables.sh:151

  • AWF_ALLOW_HOST_PORTS supports range syntax like "3000-3010" (per CLI/docs), but these ACCEPT rules pass port_spec directly to iptables --dport. iptables’ TCP/UDP --dport range syntax is typically START:END, so a hyphenated range may error and abort setup-iptables.sh (set -e). Consider normalizing "START-END" to "START:END" (and handling single ports separately) before invoking iptables.
        port_spec=$(echo "$port_spec" | xargs)
        echo "[iptables]   Allow host gateway port $port_spec"
        iptables -A OUTPUT -p tcp -d "$HOST_GATEWAY_IP" --dport "$port_spec" -j ACCEPT
      done

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

}
fs.chmodSync(chrootHostsPath, 0o644);
agentVolumes.push(`${chrootHostsPath}:/host/etc/hosts`);
agentVolumes.push(`${chrootHostsPath}:/host/etc/hosts:ro`);
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.

Mounting chroot-hosts as read-only can break host.docker.internal injection fallback: entrypoint.sh appends the container’s host.docker.internal line into /host/etc/hosts when AWF_ENABLE_HOST_ACCESS=1 and the entry is missing. With a :ro mount, that append will fail if docker-manager couldn’t resolve/append the gateway IP (the try/catch allows continuing), potentially leaving chroot without host.docker.internal resolution. Consider keeping this mount writable, or make docker-manager injection guaranteed and update entrypoint logic accordingly.

Suggested change
agentVolumes.push(`${chrootHostsPath}:/host/etc/hosts:ro`);
agentVolumes.push(`${chrootHostsPath}:/host/etc/hosts:rw`);

Copilot uses AI. Check for mistakes.
# Function to validate an IPv4 address format (e.g., 172.17.0.1)
is_valid_ipv4() {
local ip="$1"
echo "$ip" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'
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.

is_valid_ipv4 only checks a dotted-quad format, so values like 999.999.999.999 will pass and then cause iptables to error (and the script runs with set -e). It should validate each octet is 0–255 (and ideally reject 0.0.0.0/broadcast) to avoid failing open/DoS when name resolution returns unexpected output.

Suggested change
echo "$ip" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'
# Split into octets
local IFS='.'
read -r -a octets <<< "$ip"
# Must have exactly 4 octets
if [ "${#octets[@]}" -ne 4 ]; then
return 1
fi
# Validate each octet is numeric and in range 0–255
local octet
for octet in "${octets[@]}"; do
# Numeric only
if ! [[ "$octet" =~ ^[0-9]+$ ]]; then
return 1
fi
# Range check; use 10#$octet to avoid octal interpretation
if (( 10#$octet < 0 || 10#$octet > 255 )); then
return 1
fi
done
# Reject unspecified and all-hosts broadcast addresses
if [[ "$ip" == "0.0.0.0" || "$ip" == "255.255.255.255" ]]; then
return 1
fi
return 0

Copilot uses AI. Check for mistakes.
Comment on lines +145 to +151
if [ -n "$AWF_ALLOW_HOST_PORTS" ]; then
IFS=',' read -ra HOST_PORTS <<< "$AWF_ALLOW_HOST_PORTS"
for port_spec in "${HOST_PORTS[@]}"; do
port_spec=$(echo "$port_spec" | xargs)
echo "[iptables] Allow host gateway port $port_spec"
iptables -A OUTPUT -p tcp -d "$HOST_GATEWAY_IP" --dport "$port_spec" -j ACCEPT
done
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.

The gateway-bypass ACCEPT rules use AWF_ALLOW_HOST_PORTS values without validating range/format or excluding DANGEROUS_PORTS. Because AWF_ALLOW_HOST_PORTS can be user-controlled (e.g., via env override) this can re-open blocked ports on the host gateway/network gateway (e.g., 5432/6379) and bypass Squid + the dangerous-ports protection. Recommend validating/sanitizing port specs here (numeric/range within 1–65535) and explicitly rejecting any port(s) in the DANGEROUS_PORTS list before adding ACCEPT rules.

This issue also appears on line 148 of the same file.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Build Test: Java - Environment Issue ❌

Status: FAILED - Environment Configuration Error

Problem

The test could not run because Java/Maven are not functional in the current AWF chroot environment.

Technical Details

  • Repository clone: ✅ SUCCESS
  • Java/Maven execution: ❌ FAILED

Root Cause: The AWF chroot environment has /proc/self mounted but Java binaries cannot execute properly. All attempts to run Java result in bash version output instead of Java execution.

$ java -version
GNU bash, version 5.2.21(1)-release (x86_64-pc-linux-gnu)
...

Environment detected:

  • AWF_CHROOT_ENABLED=true
  • /proc/self/exe/bin/bash (incorrect for Java execution)
  • Java binaries exist but cannot execute

Required Actions

One of the following solutions is needed:

  1. Fix chroot /proc configuration - Ensure Java can determine its own executable path
  2. Disable chroot mode for this workflow - Run without --enable-chroot flag
  3. Install Java in the chroot container - Make Java available within the container environment

Test Projects Status

Project Compile Tests Status
gson ❌ NOT RUN - BLOCKED
caffeine ❌ NOT RUN - BLOCKED

Overall: BLOCKED - Environment issue prevents testing

AI generated by Build Test Java

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Build Test: Rust - Results

Project Build Tests Status
fd 1/1 PASS
zoxide 1/1 PASS

Overall: PASS

All Rust projects built and tested successfully.

AI generated by Build Test Rust

The --enable-host-access flag added an iptables ACCEPT rule for
host.docker.internal with no port restriction, allowing agent code
to reach ANY service on the host (databases, admin panels, etc.)
and bypassing the dangerous-ports blocklist entirely.

Changes:
- Restrict host gateway FILTER ACCEPT to ports 80, 443, and any
  ports from --allow-host-ports (was: all ports)
- Apply same port restriction to network gateway bypass
- Add IPv4 format validation for dynamically resolved IPs before
  using them in iptables rules
- Mount chroot-hosts as read-only (:ro) since host.docker.internal
  is pre-injected by docker-manager.ts before mounting

The NAT RETURN rule (which prevents DNAT to Squid) is unchanged,
so MCP traffic still bypasses Squid correctly. Non-allowed port
traffic hits the final DROP rule in the FILTER chain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Mossaka Mossaka force-pushed the fix/host-gateway-port-restriction branch from d0fea8c to 12683ac Compare February 6, 2026 23:46
@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

📰 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

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

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Build Test: Java - Results

Project Compile Tests Status
gson N/A FAIL
caffeine N/A FAIL

Overall: FAIL

Error Details

Both projects failed during Maven dependency resolution with SSL/network errors:

Could not transfer artifact from/to central (https://repo.maven.apache.org/maven2): 
Unsupported or unrecognized SSL message

Root Cause: Maven Central repository (repo.maven.apache.org) appears to be blocked or inaccessible through the firewall.

Action Required: Add repo.maven.apache.org to the firewall's allowed domains list to enable Maven dependency downloads.

AI generated by Build Test Java

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Go Build Test Results

Project Download Tests Status
color 1/1 PASS
env 1/1 PASS
uuid 1/1 PASS

Overall: PASS

All Go projects successfully downloaded dependencies and passed tests.

AI generated by Build Test Go

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Node.js Build Test Results

Project Install Tests Status
clsx PASS PASS
execa PASS PASS
p-limit PASS PASS

Overall: PASS

All Node.js projects installed successfully and all tests passed.

AI generated by Build Test Node.js

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Deno Build Test Results

Project Tests Status
oak 1/1 ✅ PASS
std 1/1 ✅ PASS

Overall: ✅ PASS

All Deno tests completed successfully.

AI generated by Build Test Deno

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Smoke Test Results

✅ GitHub MCP: #557, #556
✅ Playwright: GitHub homepage verified
✅ File Writing: Test file created
✅ Bash Tools: File verified

Status: PASS

cc: @Mossaka

AI generated by Smoke Copilot

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

C++ Build Test Results

Project CMake Build Status
fmt PASS
json PASS

Overall: PASS

All C++ projects built successfully.

AI generated by Build Test C++

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Build Test: Bun - Results

Project Install Tests Status
elysia 1/1 PASS
hono 1/1 PASS

Overall: PASS

All Bun build tests completed successfully.

AI generated by Build Test Bun

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Smoke Test: Claude Engine

✅ GitHub MCP - Last 2 merged PRs:

✅ Playwright - Page title verified: "GitHub · Change is constant. GitHub keeps you ahead. · GitHub"

✅ File Writing - Created /tmp/gh-aw/agent/smoke-test-claude-21769918672.txt

✅ Bash Tool - File read successful

Status: PASS

AI generated by Smoke Claude

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Chroot Version Comparison Test Results

Runtime Host Version Chroot Version Match?
Python 3.12.12 3.12.3 ❌ NO
Node.js v24.13.0 v20.20.0 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall Status: Tests did not pass - only Go versions matched.

The chroot environment successfully accessed host binaries but version mismatches were detected for Python and Node.js, likely due to different installation paths or environment configurations being used.

AI generated by Smoke Chroot

@Mossaka Mossaka added the smoke label Feb 7, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 7, 2026

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

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