Files
zerogravity/docs/mitm-interception-status.md

8.3 KiB

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:

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/<PID>/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 <PID> (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.jsdetectAndUseProxy=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. Scriptsdns-redirect.sh, iptables-redirect.sh (both functional)

Cleanup Checklist

If things are broken, undo in this order:

# 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