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 owntls.Configand transport, ignoringHTTPS_PROXYby 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:
- Renames real binary to
.real - Places a shell script wrapper at the original path
- 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:
- Redirect loop — proxy's own upstream connections got caught by iptables, creating infinite loops → fd exhaustion → crash
- Fixed loop with GID bypass — running proxy with
sg mitm-bypassand excluding GID in iptables. This fixed the loop. - 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.
- TLS trust failure — even with the MITM wrapper setting
SSL_CERT_FILE, the LS's Go LLM client likely uses a customtls.Configwith 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.8bypass 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.Configsetup in the binary - Modify
InsecureSkipVerifytotrue, 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.js—detectAndUseProxy=1(harmless, keeps working)- Everything else — clean/reverted
Code Changes Made (in the proxy)
- Transparent proxy mode (
src/mitm/proxy.rs) — supports iptables REDIRECT by detecting raw TLS ClientHello and extracting SNI - CryptoProvider init (
src/main.rs) — prevents rustls panic under load - PID detection fix (
src/backend.rs) — prefers.realbinary PID over wrapper shell script PID - SS fallback (
src/backend.rs) — discovers LS port viasswhen log file doesn't have it - DNS bypass (
src/mitm/proxy.rs) —connect_upstreamresolves viadig @8.8.8.8to bypass/etc/hosts - Scripts —
dns-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