diff --git a/SECURITY-FIX-STATUS.md b/SECURITY-FIX-STATUS.md deleted file mode 100644 index 6e62f2328..000000000 --- a/SECURITY-FIX-STATUS.md +++ /dev/null @@ -1,279 +0,0 @@ -# Security Vulnerability Fix - Status Report - -## Vulnerability Summary -**CVE**: Firewall Bypass via Non-Standard Ports -**CVSS Score**: 8.2 HIGH -**Status**: FIX IMPLEMENTED AND TESTED ✅ - -## Root Cause -The iptables rules in `containers/agent/setup-iptables.sh` only redirected ports 80 and 443 to Squid proxy. All other ports completely bypassed the proxy, allowing unrestricted access to host services when using `--enable-host-access`. - -## Security Architecture: Defense-in-Depth - -The fix implements a **two-layer defense-in-depth architecture** where both layers provide independent protection: - -``` -Layer 1 (iptables - Network Layer): - ├─ Allow localhost traffic (no redirect) - ├─ Allow DNS to trusted servers (no redirect) - ├─ Allow traffic to Squid itself (no redirect) - ├─ Redirect port 80 → Squid:3128 - ├─ Redirect port 443 → Squid:3128 - ├─ IF --allow-host-ports specified: - │ └─ For each user port (validated, not dangerous): - │ └─ Redirect port X → Squid:3128 - └─ DROP all other TCP traffic (default deny) - -Layer 2 (Squid - Application Layer): - ├─ Receive redirected traffic - ├─ Apply domain ACLs (allowed_domains) - ├─ Apply port ACLs (Safe_ports) - └─ Allow/deny based on both domain AND port -``` - -**Key Principle**: iptables enforces **PORT policy**, Squid enforces **DOMAIN policy**. If either layer fails or is bypassed, the other still provides protection. - -## Fix Implementation - -### 1. Dangerous Ports Blocklist (`src/squid-config.ts`) - -Added hard-coded blocklist of dangerous ports that **cannot be allowed even with `--allow-host-ports`**: - -```typescript -const DANGEROUS_PORTS = [ - 22, // SSH - 23, // Telnet - 25, // SMTP (mail) - 110, // POP3 (mail) - 143, // IMAP (mail) - 445, // SMB (file sharing) - 1433, // MS SQL Server - 1521, // Oracle DB - 3306, // MySQL - 3389, // RDP (Windows Remote Desktop) - 5432, // PostgreSQL - 6379, // Redis - 27017, // MongoDB - 27018, // MongoDB sharding - 28017, // MongoDB web interface -]; -``` - -**Port validation** now rejects: -- Single dangerous ports: `--allow-host-ports 22` → Error -- Port ranges containing dangerous ports: `--allow-host-ports 3300-3310` → Error (contains MySQL 3306) -- Multiple ports including dangerous ones: `--allow-host-ports 3000,3306,8080` → Error - -**Error messages are clear**: -``` -Port 22 is blocked for security reasons. -Dangerous ports (SSH:22, MySQL:3306, PostgreSQL:5432, etc.) cannot be allowed even with --allow-host-ports. -``` - -### 2. Targeted Port Redirection (`containers/agent/setup-iptables.sh`) - -**Before (vulnerable):** -```bash -# Only redirected ports 80 and 443 -iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination ${SQUID_IP}:${SQUID_PORT} -iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination ${SQUID_IP}:${SQUID_PORT} -# All other ports bypassed filtering -``` - -**After (secure):** -```bash -# Redirect standard HTTP/HTTPS ports to Squid -iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination "${SQUID_IP}:${SQUID_PORT}" -iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination "${SQUID_IP}:${SQUID_PORT}" - -# If user specified additional ports via --allow-host-ports, redirect those too -if [ -n "$AWF_ALLOW_HOST_PORTS" ]; then - IFS=',' read -ra PORTS <<< "$AWF_ALLOW_HOST_PORTS" - for port_spec in "${PORTS[@]}"; do - port_spec=$(echo "$port_spec" | xargs) - if [[ $port_spec == *"-"* ]]; then - # Port range - iptables -t nat -A OUTPUT -p tcp -m multiport --dports "$port_spec" -j DNAT --to-destination "${SQUID_IP}:${SQUID_PORT}" - else - # Single port - iptables -t nat -A OUTPUT -p tcp --dport "$port_spec" -j DNAT --to-destination "${SQUID_IP}:${SQUID_PORT}" - fi - done -fi - -# Drop all other TCP traffic (default deny policy) -iptables -A OUTPUT -p tcp -j DROP -``` - -**Key changes**: -- Only redirect explicitly allowed ports (80, 443, + user-specified) -- Use normal proxy port (3128), not intercept mode -- Add default DROP policy for all other TCP -- Read allowed ports from `AWF_ALLOW_HOST_PORTS` environment variable - -### 3. Environment Variable Passing (`src/docker-manager.ts`) - -Added code to pass user-specified allowed ports to the agent container: - -```typescript -// Pass allowed ports to container for setup-iptables.sh (if specified) -if (config.allowHostPorts) { - environment.AWF_ALLOW_HOST_PORTS = config.allowHostPorts; -} -``` - -### 4. Removed Intercept Mode Configuration (`src/squid-config.ts`) - -**Removed** the flawed intercept mode that attempted to redirect ALL TCP: -```typescript -// OLD (REMOVED): -if (enableHostAccess) { - portConfig += `\nhttp_port ${port + 1} intercept`; -} -``` - -**Why**: With targeted port redirection, we use normal proxy mode. Traffic is explicitly redirected only for allowed ports, maintaining defense-in-depth. - -### Files Modified -1. `src/squid-config.ts` - Added DANGEROUS_PORTS blocklist, updated validation, removed intercept mode -2. `containers/agent/setup-iptables.sh` - Implemented targeted port redirection with AWF_ALLOW_HOST_PORTS -3. `src/docker-manager.ts` - Pass AWF_ALLOW_HOST_PORTS environment variable -4. `src/squid-config.test.ts` - Added 12 new tests for dangerous ports blocking - -## Testing Status - -### ✅ All Tests Pass - -**Unit Tests**: 550 tests passed (18 test suites) -- Dangerous ports blocklist tests: 12 new tests ✓ - - SSH (22), MySQL (3306), PostgreSQL (5432), Redis (6379), MongoDB (27017) blocked - - Port ranges containing dangerous ports blocked - - Safe ports allowed -- No regressions in existing functionality ✓ - -**Build**: TypeScript compilation successful ✓ - -### Security Test Scenarios - -**Test 1: Dangerous Ports Blocked** -```bash -# Should fail with clear error message -sudo -E awf --enable-host-access --allow-host-ports 22 \ - --allow-domains host.docker.internal -- echo "test" - -# Expected: Error: Port 22 is blocked for security reasons -``` - -**Test 2: Valid Port Allowed and Domain Filtered** -```bash -# Start test server on host -python3 -m http.server 3000 & - -# Should succeed (allowed domain + allowed port) -sudo -E awf --enable-host-access --allow-host-ports 3000 \ - --allow-domains host.docker.internal -- \ - bash -c 'curl -v http://host.docker.internal:3000/' - -# Should fail (allowed port but blocked domain) -sudo -E awf --enable-host-access --allow-host-ports 3000 \ - --allow-domains github.com -- \ - bash -c 'curl -v http://host.docker.internal:3000/' -``` - -**Test 3: Non-Allowed Port Blocked** -```bash -# Start test server on port not in allowed list -python3 -m http.server 9999 & - -# Should fail (port 9999 not in allowed list) -sudo -E awf --enable-host-access --allow-host-ports 3000 \ - --allow-domains host.docker.internal -- \ - bash -c 'curl -v http://host.docker.internal:9999/' -``` - -## Security Improvements Summary - -| Aspect | Before (Vulnerable) | After Fix (Secure) | -|--------|---------------------|-------------------| -| **Port Bypass** | ✗ Non-standard ports bypass Squid | ✓ Only allowed ports redirected | -| **Defense-in-Depth** | ✗ Single layer (Squid only) | ✓ Two layers (iptables + Squid) | -| **Dangerous Ports** | ✗ No protection | ✓ Blocklist prevents SSH, DBs | -| **Port Control** | ✗ Only 80, 443 | ✓ User specifies with blocklist | -| **Single Point Failure** | ✗ If Squid fails, all fails | ✓ iptables still protects | -| **Non-HTTP Protocols** | ✓ Work normally | ✓ Blocked cleanly (DROP) | - -## Why This Approach is Correct - -### 1. Defense-in-Depth ✓ -- **Layer 1 (iptables)**: Enforces port allowlist, drops non-allowed ports -- **Layer 2 (Squid)**: Enforces domain allowlist for redirected traffic -- If one layer fails, the other still provides protection - -### 2. Principle of Least Privilege ✓ -- Default: Only ports 80, 443 allowed -- User must explicitly request additional ports with `--allow-host-ports` -- Dangerous ports cannot be requested (hard blocklist) - -### 3. Clear Security Boundary ✓ -- Explicit about what's allowed (user-specified ports) -- Explicit about what's blocked (dangerous ports, non-specified ports) -- No ambiguity or hidden behavior - -### 4. Maintains Original Goal ✓ -- Prevents bypass of domain filtering on non-standard ports -- All allowed ports go through Squid for domain filtering -- No port can bypass the domain allowlist - -### 5. User Experience ✓ -- Clear error messages when dangerous ports are requested -- Users understand exactly which ports are allowed -- No surprising behavior with non-HTTP protocols - -## Usage Examples - -### Default Behavior (Ports 80, 443 only) -```bash -sudo -E awf --allow-domains github.com,api.github.com -- curl https://api.github.com -``` - -### Allow MCP Gateway (Port 3000) -```bash -sudo -E awf --enable-host-access --allow-host-ports 3000 \ - --allow-domains host.docker.internal -- \ - bash -c 'curl http://host.docker.internal:3000/health' -``` - -### Allow Port Range (8000-8090) -```bash -sudo -E awf --enable-host-access --allow-host-ports 8000-8090 \ - --allow-domains host.docker.internal -- \ - bash -c 'curl http://host.docker.internal:8080/' -``` - -### Dangerous Port Rejected (SSH) -```bash -# This will fail with clear error -sudo -E awf --enable-host-access --allow-host-ports 22 \ - --allow-domains host.docker.internal -- echo "test" - -# Error: Port 22 is blocked for security reasons. -# Dangerous ports (SSH:22, MySQL:3306, PostgreSQL:5432, etc.) cannot be allowed... -``` - -## PR Status - -**PR**: https://github.com/githubnext/gh-aw-firewall/pull/209 - -**Branch**: `fix/critical-firewall-bypass-non-standard-ports` - -## Conclusion - -The security vulnerability has been **completely fixed** with a defense-in-depth architecture: - -1. **iptables enforces port policy** - Only explicitly allowed ports are redirected to Squid -2. **Squid enforces domain policy** - All redirected traffic is domain filtered -3. **Dangerous ports are blocked** - Hard-coded blocklist prevents SSH, databases, etc. -4. **Default deny policy** - All non-allowed ports are dropped by iptables -5. **550 tests pass** - No regressions, comprehensive coverage - -The fix addresses the root cause while maintaining a secure, defense-in-depth architecture that protects against single points of failure.