276 lines
9.7 KiB
Markdown
276 lines
9.7 KiB
Markdown
# 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/<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.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
|
|
→ See `docs/ls-binary-analysis.md` for comprehensive binary reverse engineering
|
|
|
|
## New Findings (from binary analysis)
|
|
|
|
### Alternative to Polling: `StreamCascadeReactiveUpdates`
|
|
|
|
The LS has a streaming gRPC method `StreamCascadeReactiveUpdates` that pushes
|
|
cascade state changes in real-time via server-sent streaming. The extension uses
|
|
this instead of polling `GetCascadeTrajectorySteps`.
|
|
|
|
**Potential improvement:** If we switch from polling to this streaming RPC, we'd
|
|
get lower latency and less backend traffic. However, our current polling approach
|
|
works reliably and doesn't require maintaining a long-lived gRPC stream.
|
|
|
|
### Quota Endpoint: `retrieveUserQuota`
|
|
|
|
The `PredictionService/RetrieveUserQuota` gRPC method and
|
|
`v1internal:retrieveUserQuota` REST endpoint provide quota/credit information.
|
|
This could be used to implement a proper `/v1/quota` endpoint instead of
|
|
scraping the LS's own quota tracking.
|
|
|
|
### `internalAtomicAgenticChat`
|
|
|
|
A REST endpoint that appears to handle the entire agentic chat loop atomically
|
|
(tool calls + responses in one request?). Investigation needed to understand
|
|
the request/response format.
|
|
|
|
### Credits System
|
|
|
|
The `google/internal/cloud/code/v1internal/credits` proto package exists with
|
|
`Credits_CreditType` enum. The `CASCADE_ENFORCE_QUOTA` config key controls
|
|
whether quotas are enforced. Related methods: `AddExtraFlexCreditsInternal`,
|
|
`GetTeamCreditEntries`, `GetPlanStatus`.
|