diff --git a/docs/mitm-interception-status.md b/docs/mitm-interception-status.md new file mode 100644 index 0000000..ce68b07 --- /dev/null +++ b/docs/mitm-interception-status.md @@ -0,0 +1,242 @@ +# MITM Traffic Interception — Research & Status + +## Goal + +Capture the LS's LLM API traffic (requests + responses, including system prompts +and token usage) by routing it through our MITM proxy. + +## Key Discovery: How the LS Makes LLM API Calls + +The LS does **NOT** use gRPC for LLM API calls. It uses: + +- **Protocol**: Standard HTTPS POST with Server-Sent Events (SSE) +- **Endpoint**: `https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse` +- **HTTP client**: `ApiServerClientV2` — a Go HTTP client that creates its own `tls.Config` + and transport, **ignoring `HTTPS_PROXY` by default** + +The Go HTTP client for LLM API calls is separate from the one used for Unleash +(feature flags) and other auxiliary traffic. The Unleash client respects proxy +settings, but the LLM client does not. + +## What We Tried + +### 1. Extension Patch — `detectAndUseProxy` ✅ Partial + +**Status**: Applied and still active. Harmless. + +The extension sends a protobuf field `detect_and_use_proxy` (field 34) to the LS +during initialization. By default, it's set to `UNSPECIFIED` (0), meaning the LS +ignores proxy env vars. + +**Patch applied:** + +```bash +sudo sed -i 's/detectAndUseProxy=pe.UNSPECIFIED/detectAndUseProxy=1/' \ + /usr/share/antigravity/resources/app/extensions/antigravity/dist/extension.js +``` + +**Enum values:** + +- 0 = `DETECT_AND_USE_PROXY_UNSPECIFIED` (default, ignore proxy) +- 1 = `DETECT_AND_USE_PROXY_ENABLED` +- 2 = `DETECT_AND_USE_PROXY_DISABLED` + +**Result:** Unleash/aux traffic now routes through `HTTPS_PROXY`. But the LLM API +client (`ApiServerClientV2`) has its own transport that ignores this flag. LLM +calls still go direct to Google. + +**Verify:** `grep -o 'detectAndUseProxy=[^;]*' /usr/share/antigravity/resources/app/extensions/antigravity/dist/extension.js` +→ should show `detectAndUseProxy=1` + +**Re-apply after updates:** Yes, must re-apply after every Antigravity update. + +### 2. MITM Wrapper (`mitm-wrapper.sh`) ✅ Works for Env Vars + +Sets `HTTPS_PROXY` and `SSL_CERT_FILE` on the LS process by wrapping the binary. + +**How it works:** + +1. Renames real binary to `.real` +2. Places a shell script wrapper at the original path +3. Wrapper sets env vars and execs the real binary with all original args + +**Result:** The wrapper correctly sets env vars on the LS process (verified via +`/proc//environ`). Combined with the extension patch, Unleash traffic routes +through the proxy. But LLM API calls still bypass — the `ApiServerClientV2` Go +HTTP client doesn't honor `HTTPS_PROXY`. + +### 3. iptables REDIRECT — ALL Port 443 ❌ Failed + +Redirected all outbound port 443 traffic from the user's UID to the MITM proxy. + +**Problems encountered:** + +1. **Redirect loop** — proxy's own upstream connections got caught by iptables, + creating infinite loops → fd exhaustion → crash +2. **Fixed loop with GID bypass** — running proxy with `sg mitm-bypass` and + excluding GID in iptables. This fixed the loop. +3. **Broke Antigravity** — ALL HTTPS traffic (telegram, discord, microsoft + telemetry, extension marketplace, etc.) went through the proxy. The TLS + passthrough worked technically but was too disruptive. +4. **TLS trust failure** — even with the MITM wrapper setting `SSL_CERT_FILE`, + the LS's Go LLM client likely uses a custom `tls.Config` with its own root + CAs, not the system pool. So it rejected our MITM CA cert. + +**Abandoned.** Too disruptive, and the fundamental TLS trust issue remained. + +### 4. DNS Redirect (`/etc/hosts`) ❌ Failed + +Redirected only `daily-cloudcode-pa.googleapis.com` to 127.0.0.1 via `/etc/hosts`, +then used a targeted iptables rule for `127.0.0.1:443` only. + +**Problems:** + +- Same TLS trust issue — the Go LLM client rejected our MITM CA +- Needed `dig @8.8.8.8` bypass for upstream resolution (implemented but untested) + +**Abandoned.** TLS trust is the blocker. + +## The Core Blocker + +**The LS's Go LLM HTTP client (`ApiServerClientV2`) uses a custom `tls.Config` +that does NOT read from `SSL_CERT_FILE` or the system CA store.** It likely has +its own hardcoded/embedded root CAs. + +This means: + +- Even if we redirect traffic to our MITM proxy ✅ +- Even if the MITM generates valid certs for the domain ✅ +- The LS rejects the cert because it doesn't trust our CA ❌ + +## Potential Solutions (Untried) + +### A. Binary Patching + +Patch the Go binary to accept our CA or disable cert verification. + +- Find the `tls.Config` setup in the binary +- Modify `InsecureSkipVerify` to `true`, or inject our CA cert DER bytes +- Very fragile, breaks on updates + +### B. LD_PRELOAD Hook + +Hook `connect()` syscall to redirect traffic. + +- **Won't work** for Go — Go uses raw syscalls, not libc wrappers + +### C. Network Namespace + +Run the LS in an isolated network namespace with custom routing. + +- Complex setup, but clean isolation +- The standalone LS work would feed into this + +### D. Standalone LS with Full Control + +Get standalone LS cascades working (see `docs/standalone-ls-todo.md`), then +have full control over the process environment, including: + +- Custom CA trust +- Custom DNS resolution +- Custom proxy settings +- Network namespace isolation + **This is probably the best long-term approach.** + +### E. Kernel-level TLS Interception (eBPF) + +Use eBPF to intercept TLS records pre-encryption. + +- Very powerful, can read plaintext before encryption +- Complex, requires kernel support (>= 4.18) +- Tools: `bpftrace`, custom eBPF programs, `ecapture` + +### F. `SSLKEYLOGFILE` + Passive Capture + +- Go doesn't support `SSLKEYLOGFILE` (confirmed by testing) +- Could patch the binary to enable it, but same fragility as option A + +### G. ptrace-based Interception + +Use `ptrace` to intercept `write()`/`sendmsg()` syscalls on TLS sockets. + +- Can read plaintext data being written to TLS connections +- Tools: `strace -e trace=write -p ` (but output is messy) +- Better: custom ptrace tool that filters for TLS socket FDs + +## Technical Details + +### Model IDs + +| Placeholder | Model | +| ------------------------- | ------------------- | +| `MODEL_PLACEHOLDER_M18` | Gemini 3 Flash | +| `MODEL_PLACEHOLDER_M8` | Gemini 3 Pro (High) | +| `MODEL_PLACEHOLDER_M7` | Gemini 3 Pro (Low) | +| `MODEL_PLACEHOLDER_M26` | Claude Opus 4.6 | +| `MODEL_PLACEHOLDER_M12` | Claude Opus 4.5 | +| `MODEL_CLAUDE_4_5_SONNET` | Claude Sonnet 4.5 | + +### LS Binary Location + +`/usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64` + +### API Endpoint + +`https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse` + +### Protobuf Field 34 — `detect_and_use_proxy` + +- Part of the init metadata sent from extension to LS via stdin +- Enum: `DetectAndUseProxy` (0=UNSPECIFIED, 1=ENABLED, 2=DISABLED) +- Controls whether auxiliary HTTP clients honor `HTTPS_PROXY` +- Does NOT control the LLM API client + +### Unleash Feature Flags + +- Authorization: `*:production.e44558998bfc35ea9584dc65858e4485fdaa5d7ef46903e0c67712d1` +- Endpoint: `antigravity-unleash.goog` +- App name: `codeium-language-server` + +### Files Modified (Current State) + +- `extension.js` — `detectAndUseProxy=1` (harmless, keeps working) +- Everything else — clean/reverted + +## Code Changes Made (in the proxy) + +1. **Transparent proxy mode** (`src/mitm/proxy.rs`) — supports iptables REDIRECT + by detecting raw TLS ClientHello and extracting SNI +2. **CryptoProvider init** (`src/main.rs`) — prevents rustls panic under load +3. **PID detection fix** (`src/backend.rs`) — prefers `.real` binary PID over + wrapper shell script PID +4. **SS fallback** (`src/backend.rs`) — discovers LS port via `ss` when log file + doesn't have it +5. **DNS bypass** (`src/mitm/proxy.rs`) — `connect_upstream` resolves via + `dig @8.8.8.8` to bypass `/etc/hosts` +6. **Scripts** — `dns-redirect.sh`, `iptables-redirect.sh` (both functional) + +## Cleanup Checklist + +If things are broken, undo in this order: + +```bash +# 1. Remove iptables rules +sudo ./scripts/iptables-redirect.sh uninstall +sudo ./scripts/dns-redirect.sh uninstall + +# 2. Remove /etc/hosts entries (verify manually) +sudo grep -v "antigravity-mitm" /etc/hosts | sudo tee /etc/hosts.tmp && sudo mv /etc/hosts.tmp /etc/hosts + +# 3. Uninstall wrapper +sudo ./scripts/mitm-wrapper.sh uninstall + +# 4. Remove system CA +sudo rm -f /usr/local/share/ca-certificates/antigravity-mitm.crt +sudo update-ca-certificates + +# 5. Restart Antigravity +``` + +## Next Steps + +→ See `docs/standalone-ls-todo.md` for standalone LS isolation work diff --git a/docs/standalone-ls-todo.md b/docs/standalone-ls-todo.md new file mode 100644 index 0000000..a3b55d4 --- /dev/null +++ b/docs/standalone-ls-todo.md @@ -0,0 +1,74 @@ +# Standalone LS for Proxy Isolation + +## Goal + +Route ALL proxy traffic through a standalone LS instance instead of the real one, +so development/testing/proxying never interferes with active coding sessions. + +## Current State + +The proxy currently talks to the **real** LS spawned by Antigravity. +This is risky — a bad cascade or proxy bug can disrupt the coding conversation. + +## What Works + +- Standalone LS starts fine with custom init metadata via stdin protobuf +- Connects to the main extension server (`-extension_server_port`) +- Accepts cascade requests (returns cascadeId) +- With `detect_and_use_proxy = ENABLED` (field 34 = 2), honors `HTTPS_PROXY` + +## What Doesn't Work + +- **Cascades silently fail** — the LS accepts the request but never processes it + - No planner invocation, no upstream API call, no logs beyond startup + - 9 lines of log after 40s wait + - Main LS logs show zero trace of the standalone's cascade + +## Suspected Blockers (investigate in order) + +1. **Auth context** — standalone may not receive OAuth token from extension server + - Check: does the standalone's `GetUserStatus` return valid auth? + - The extension server might only share tokens with the "primary" LS + +2. **Unleash feature flags** — cascade processing gated by flags the standalone doesn't fetch + - The standalone connects to Unleash via the proxy, but might not get the right flags + - Check: compare Unleash responses between main and standalone + +3. **Workspace indexing** — planner might require indexed workspace state + - The standalone's workspace (`/tmp/antigravity-standalone`) is empty + - Try: point it at a real workspace with actual files + +4. **Extension server coupling** — cascade might need the extension to "drive" it + - The chat panel in the extension might send additional RPCs to progress the cascade + - Check: trace what RPCs the extension sends after StartCascade + +## Investigation Plan + +```bash +# 1. Launch with max verbosity +echo "$METADATA" | base64 -d | \ + timeout 90 "$LS_BIN" \ + -v 5 \ + -server_port 42200 \ + ... > /tmp/standalone-verbose.log 2>&1 & + +# 2. Check auth status +curl -sk "https://127.0.0.1:42200/exa.language_server_pb.LanguageServerService/GetUserStatus" \ + -H "Content-Type: application/json" \ + -H "x-codeium-csrf-token: $CSRF" \ + -d '{}' + +# 3. Send cascade and watch logs in real-time +tail -f /tmp/standalone-verbose.log & +curl -sk "https://127.0.0.1:42200/.../StartCascade" ... + +# 4. Compare Unleash flags +# Main LS unleash vs standalone unleash +``` + +## Key Technical Details + +- Init metadata protobuf field 34 = `detect_and_use_proxy` (enum: 0=UNSPECIFIED, 1=ENABLED, 2=DISABLED) +- Model IDs: M18=Flash, M8=Pro-High, M7=Pro-Low, M26=Opus4.6, M12=Opus4.5 +- LS binary: `/usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64` +- API endpoint: `daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse` diff --git a/scripts/dns-redirect.sh b/scripts/dns-redirect.sh new file mode 100755 index 0000000..214dce2 --- /dev/null +++ b/scripts/dns-redirect.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +# ╔═══════════════════════════════════════════════════════════════════════════╗ +# ║ Antigravity MITM — DNS-based redirect for targeted interception ║ +# ║ ║ +# ║ Instead of redirecting ALL port 443 traffic (which breaks everything), ║ +# ║ this uses /etc/hosts to redirect ONLY the LLM API domain to localhost, ║ +# ║ then iptables redirects only localhost:443 → MITM port. ║ +# ║ ║ +# ║ Also adds the MITM CA to the system trust store so Go trusts it. ║ +# ╚═══════════════════════════════════════════════════════════════════════════╝ +set -euo pipefail + +MITM_PORT="${ANTIGRAVITY_MITM_PORT:-8742}" +MITM_CA="${HOME}/.config/antigravity-proxy/mitm-ca.pem" +# If run with sudo, use SUDO_USER's home +if [[ -n "${SUDO_USER:-}" ]]; then + MITM_CA="$(eval echo "~${SUDO_USER}")/.config/antigravity-proxy/mitm-ca.pem" +fi + +HOSTS_MARKER="# antigravity-mitm" +API_DOMAINS=( + "daily-cloudcode-pa.googleapis.com" +) + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +cmd_install() { + echo -e "${BOLD}${CYAN}Antigravity MITM DNS Redirect Setup${NC}" + echo -e "────────────────────────────────────" + echo "" + + # 1. Add MITM CA to system trust store + if [[ ! -f "$MITM_CA" ]]; then + echo -e " ${RED}✗${NC} MITM CA not found: ${MITM_CA}" + echo -e " Start the proxy once first to generate it." + exit 1 + fi + + local sys_cert="/usr/local/share/ca-certificates/antigravity-mitm.crt" + cp "$MITM_CA" "$sys_cert" + update-ca-certificates >/dev/null 2>&1 + echo -e " ${GREEN}✓${NC} MITM CA added to system trust store" + + # 2. Add /etc/hosts entries for API domains → 127.0.0.1 + # First, cache the real IPs for the MITM to use later + local real_ips_file="/tmp/antigravity-mitm-real-ips" + > "$real_ips_file" + + for domain in "${API_DOMAINS[@]}"; do + # Remove old entries + sed -i "/${domain}.*${HOSTS_MARKER}/d" /etc/hosts + + # Resolve and cache the real IPs BEFORE redirecting + local real_ip + real_ip=$(dig +short "$domain" 2>/dev/null | grep -E '^[0-9]+\.' | head -1) + if [[ -n "$real_ip" ]]; then + echo "${domain}=${real_ip}" >> "$real_ips_file" + fi + + # Add the /etc/hosts redirect + echo "127.0.0.1 ${domain} ${HOSTS_MARKER}" >> /etc/hosts + echo -e " ${GREEN}✓${NC} /etc/hosts: ${domain} → 127.0.0.1 (real: ${real_ip:-unknown})" + done + + # 3. iptables: redirect ONLY 127.0.0.1:443 → MITM port + # This catches only the /etc/hosts redirected domains, nothing else! + iptables -t nat -D OUTPUT -d 127.0.0.1 -p tcp --dport 443 \ + -j REDIRECT --to-port "$MITM_PORT" 2>/dev/null || true + iptables -t nat -A OUTPUT -d 127.0.0.1 -p tcp --dport 443 \ + -j REDIRECT --to-port "$MITM_PORT" + echo -e " ${GREEN}✓${NC} iptables: 127.0.0.1:443 → localhost:${MITM_PORT}" + + echo "" + echo -e " ${GREEN}Done!${NC}" + echo "" + echo -e " ${BOLD}How it works:${NC}" + echo -e " 1. LS resolves ${API_DOMAINS[0]} → 127.0.0.1 (via /etc/hosts)" + echo -e " 2. LS connects to 127.0.0.1:443" + echo -e " 3. iptables redirects to MITM proxy on :${MITM_PORT}" + echo -e " 4. MITM intercepts, decrypts (CA is trusted), proxies to real Google" + echo "" + echo -e " Real upstream IPs cached in: ${real_ips_file}" + echo -e " Restart Antigravity for changes to take effect." + echo -e " Undo: sudo $0 uninstall" + echo "" +} + +cmd_uninstall() { + echo -e "${BOLD}${CYAN}Removing MITM DNS Redirect${NC}" + echo "" + + # Remove /etc/hosts entries + sed -i "/${HOSTS_MARKER}/d" /etc/hosts + echo -e " ${GREEN}✓${NC} Removed /etc/hosts entries" + + # Remove iptables rule + iptables -t nat -D OUTPUT -d 127.0.0.1 -p tcp --dport 443 \ + -j REDIRECT --to-port "$MITM_PORT" 2>/dev/null || true + echo -e " ${GREEN}✓${NC} Removed iptables rule" + + # Remove system CA (optional) + rm -f /usr/local/share/ca-certificates/antigravity-mitm.crt + update-ca-certificates >/dev/null 2>&1 + echo -e " ${GREEN}✓${NC} Removed MITM CA from system trust store" + + echo "" +} + +cmd_status() { + echo -e "${BOLD}${CYAN}MITM DNS Redirect Status${NC}" + echo "" + + # Check /etc/hosts + local hosts_count + hosts_count=$(grep -c "$HOSTS_MARKER" /etc/hosts 2>/dev/null || echo 0) + if [[ "$hosts_count" -gt 0 ]]; then + echo -e " ${GREEN}✓${NC} /etc/hosts: ${hosts_count} domain(s) redirected" + grep "$HOSTS_MARKER" /etc/hosts | sed 's/^/ /' + else + echo -e " ${YELLOW}○${NC} /etc/hosts: no redirects" + fi + + echo "" + # Check iptables + if iptables -t nat -L OUTPUT -n 2>/dev/null | grep -q "127.0.0.1.*REDIRECT.*${MITM_PORT}"; then + echo -e " ${GREEN}✓${NC} iptables: 127.0.0.1:443 → :${MITM_PORT}" + else + echo -e " ${YELLOW}○${NC} iptables: no redirect" + fi + + echo "" + # Check system CA + if [[ -f /usr/local/share/ca-certificates/antigravity-mitm.crt ]]; then + echo -e " ${GREEN}✓${NC} System CA: installed" + else + echo -e " ${YELLOW}○${NC} System CA: not installed" + fi + echo "" +} + +case "${1:-}" in + install) + cmd_install + ;; + uninstall) + cmd_uninstall + ;; + status) + cmd_status + ;; + *) + echo "Usage: sudo $0 {install|uninstall|status}" + echo "" + echo "Redirects LLM API domain to localhost via /etc/hosts + iptables." + echo "Only intercepts API traffic, everything else is untouched." + exit 1 + ;; +esac diff --git a/scripts/iptables-redirect.sh b/scripts/iptables-redirect.sh new file mode 100755 index 0000000..382461b --- /dev/null +++ b/scripts/iptables-redirect.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# ╔═══════════════════════════════════════════════════════════════════════════╗ +# ║ Antigravity MITM — iptables redirect for transparent interception ║ +# ║ ║ +# ║ Redirects outbound port 443 traffic to the MITM proxy. ║ +# ║ Uses a dedicated GID to exclude the proxy's own upstream traffic, ║ +# ║ preventing redirect loops. ║ +# ║ ║ +# ║ Usage: sudo ./iptables-redirect.sh install ║ +# ║ sudo ./iptables-redirect.sh uninstall ║ +# ║ sudo ./iptables-redirect.sh status ║ +# ╚═══════════════════════════════════════════════════════════════════════════╝ +set -euo pipefail + +MITM_PORT="${ANTIGRAVITY_MITM_PORT:-8742}" +CHAIN="ANTIGRAVITY_MITM" +BYPASS_GROUP="mitm-bypass" + +# Resolve target user (the one whose traffic we redirect) +TARGET_USER="${SUDO_USER:-$(whoami)}" +TARGET_UID=$(id -u "$TARGET_USER" 2>/dev/null || echo "") + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' + +cmd_install() { + echo -e "${BOLD}${CYAN}Antigravity MITM iptables Setup${NC}" + echo -e "────────────────────────────────" + echo "" + + if [[ -z "$TARGET_UID" ]]; then + echo -e " ${RED}✗${NC} Cannot resolve UID for user '${TARGET_USER}'" + exit 1 + fi + + # Create bypass group (proxy runs with this GID to avoid redirect loop) + if ! getent group "$BYPASS_GROUP" >/dev/null 2>&1; then + groupadd "$BYPASS_GROUP" + echo -e " ${GREEN}✓${NC} Created group: ${BYPASS_GROUP}" + else + echo -e " ${GREEN}✓${NC} Group exists: ${BYPASS_GROUP}" + fi + + # Add user to bypass group (so they can use 'sg' to run proxy) + if ! id -nG "$TARGET_USER" 2>/dev/null | grep -qw "$BYPASS_GROUP"; then + usermod -aG "$BYPASS_GROUP" "$TARGET_USER" + echo -e " ${GREEN}✓${NC} Added ${TARGET_USER} to ${BYPASS_GROUP}" + fi + + local bypass_gid + bypass_gid=$(getent group "$BYPASS_GROUP" | cut -d: -f3) + + # Check MITM proxy is running + if ! ss -tlnp 2>/dev/null | grep -q ":${MITM_PORT}"; then + echo -e " ${YELLOW}!${NC} MITM proxy not running on :${MITM_PORT} (will work once started)" + else + echo -e " ${GREEN}✓${NC} MITM proxy listening on :${MITM_PORT}" + fi + + # Create our chain + iptables -t nat -N "$CHAIN" 2>/dev/null || true + iptables -t nat -F "$CHAIN" + + # THE KEY RULE: redirect port 443 traffic UNLESS it's from the bypass group. + # This prevents redirect loops — the proxy runs with GID=mitm-bypass, + # so its upstream connections to Google are NOT redirected back to itself. + iptables -t nat -A "$CHAIN" \ + -m owner ! --gid-owner "$bypass_gid" \ + -p tcp --dport 443 \ + -j REDIRECT --to-port "$MITM_PORT" + + echo -e " ${GREEN}✓${NC} Redirect rule: tcp/443 → :${MITM_PORT} (skip GID ${bypass_gid})" + + # Hook into OUTPUT for target user only + iptables -t nat -D OUTPUT -m owner --uid-owner "$TARGET_UID" \ + -p tcp --dport 443 -j "$CHAIN" 2>/dev/null || true + iptables -t nat -A OUTPUT -m owner --uid-owner "$TARGET_UID" \ + -p tcp --dport 443 -j "$CHAIN" + + echo -e " ${GREEN}✓${NC} OUTPUT hook: UID ${TARGET_UID} (${TARGET_USER})" + + echo "" + echo -e " ${GREEN}Done!${NC}" + echo "" + echo -e " ${BOLD}IMPORTANT:${NC} Run the proxy with the bypass group to avoid loops:" + echo -e " ${CYAN}sg ${BYPASS_GROUP} -c 'RUST_LOG=info ./target/release/antigravity-proxy'${NC}" + echo "" + echo -e " Then restart Antigravity to re-establish connections." + echo -e " Undo: sudo $0 uninstall" + echo "" +} + +cmd_uninstall() { + echo -e "${BOLD}${CYAN}Removing iptables MITM redirect${NC}" + echo "" + + local target_uid + target_uid=$(id -u "$TARGET_USER" 2>/dev/null || echo "1000") + + # Remove jump from OUTPUT + iptables -t nat -D OUTPUT -m owner --uid-owner "$target_uid" \ + -p tcp --dport 443 -j "$CHAIN" 2>/dev/null || true + echo -e " ${GREEN}✓${NC} Removed OUTPUT jump" + + # Flush and delete our chain + iptables -t nat -F "$CHAIN" 2>/dev/null || true + iptables -t nat -X "$CHAIN" 2>/dev/null || true + echo -e " ${GREEN}✓${NC} Removed ${CHAIN} chain" + + echo "" + echo -e " ${YELLOW}Note:${NC} Group '${BYPASS_GROUP}' left intact (harmless)." + echo -e " Remove with: sudo groupdel ${BYPASS_GROUP}" + echo "" +} + +cmd_status() { + echo -e "${BOLD}${CYAN}iptables MITM Status${NC}" + echo "" + + if iptables -t nat -L "$CHAIN" -n 2>/dev/null | grep -q "REDIRECT"; then + echo -e " ${GREEN}✓${NC} Chain ${CHAIN}: active" + iptables -t nat -L "$CHAIN" -nv --line-numbers 2>/dev/null | \ + sed 's/^/ /' + else + echo -e " ${YELLOW}○${NC} Chain ${CHAIN}: not installed" + fi + + echo "" + if iptables -t nat -L OUTPUT -n 2>/dev/null | grep -q "$CHAIN"; then + echo -e " ${GREEN}✓${NC} OUTPUT hook: installed" + iptables -t nat -L OUTPUT -n 2>/dev/null | grep "$CHAIN" | sed 's/^/ /' + else + echo -e " ${YELLOW}○${NC} OUTPUT hook: not installed" + fi + + echo "" + if getent group "$BYPASS_GROUP" >/dev/null 2>&1; then + local gid + gid=$(getent group "$BYPASS_GROUP" | cut -d: -f3) + echo -e " ${GREEN}✓${NC} Bypass group: ${BYPASS_GROUP} (GID ${gid})" + else + echo -e " ${YELLOW}○${NC} Bypass group: not created" + fi + echo "" +} + +case "${1:-}" in + install) + cmd_install + ;; + uninstall) + cmd_uninstall + ;; + status) + cmd_status + ;; + *) + echo "Usage: sudo $0 {install|uninstall|status}" + echo "" + echo "Redirects outbound port 443 traffic to the MITM proxy." + echo "The proxy must be run with 'sg mitm-bypass' to avoid redirect loops." + exit 1 + ;; +esac