- Spawn standalone LS as dedicated 'antigravity-ls' user via sudo - UID-scoped iptables redirect (port 443 → MITM proxy) via mitm-redirect.sh - Combined CA bundle (system CAs + MITM CA) for Go TLS trust - Transparent TLS interception with chunked response detection - Google SSE parser for streamGenerateContent usage extraction - Timeouts on all MITM operations (TLS handshake, upstream, idle) - Forward response data immediately (no buffering) - Per-model token usage capture (input, output, thinking) - Update docs and known issues to reflect resolved TLS blocker
145 lines
5.2 KiB
Markdown
145 lines
5.2 KiB
Markdown
# MITM Traffic Interception — Status
|
|
|
|
## Status: ✅ FULLY WORKING (Standalone Mode)
|
|
|
|
MITM interception is operational for the standalone LS. The proxy intercepts,
|
|
decrypts, and parses all LLM API traffic with per-model token usage capture.
|
|
|
|
## How It Works
|
|
|
|
```
|
|
Client → Proxy (8741) → Standalone LS (as antigravity-ls user)
|
|
↓ (port 443 traffic)
|
|
iptables REDIRECT (UID-scoped)
|
|
↓
|
|
MITM Proxy (8742)
|
|
↓ (TLS decrypt + parse SSE)
|
|
Google API (daily-cloudcode-pa.googleapis.com)
|
|
```
|
|
|
|
### Components
|
|
|
|
1. **UID-scoped iptables** (`scripts/mitm-redirect.sh`)
|
|
- Creates `antigravity-ls` system user
|
|
- iptables rule: redirect UID's port-443 → MITM port
|
|
- Only the standalone LS is affected — no side effects on other software
|
|
|
|
2. **Combined CA bundle** (`src/standalone.rs`)
|
|
- Go's `SSL_CERT_FILE` replaces (not appends) the system trust store
|
|
- Proxy concatenates system CAs + MITM CA → `/tmp/antigravity-mitm-combined-ca.pem`
|
|
- Set as `SSL_CERT_FILE` on the standalone LS process
|
|
|
|
3. **`sudo -u` spawning** (`src/standalone.rs`)
|
|
- If `antigravity-ls` user exists, LS is spawned via `sudo -n -u antigravity-ls`
|
|
- Env vars passed via `/usr/bin/env KEY=VALUE` args
|
|
- Falls back to current user if the dedicated user doesn't exist
|
|
|
|
4. **Google SSE parser** (`src/mitm/intercept.rs`)
|
|
- Parses `data: {"response": {"usageMetadata": {...}}}` events
|
|
- Extracts `promptTokenCount`, `candidatesTokenCount`, `thoughtsTokenCount`
|
|
- Handles both Google and Anthropic SSE formats
|
|
|
|
5. **Transparent proxy** (`src/mitm/proxy.rs`)
|
|
- Detects iptables-redirected connections via TLS ClientHello SNI
|
|
- Terminates TLS with dynamically generated certs
|
|
- Forwards HTTP/1.1 requests upstream with real DNS resolution (`dig @8.8.8.8`)
|
|
- Chunked response detection for fast completion
|
|
|
|
## What We Tried (Historical)
|
|
|
|
### 1. Extension Patch — `detectAndUseProxy` ✅ Still Active
|
|
|
|
Patches `detectAndUseProxy=1` in the extension JS. Makes auxiliary traffic
|
|
(Unleash, etc.) honor `HTTPS_PROXY`. Harmless, still applied.
|
|
|
|
### 2. MITM Wrapper (`mitm-wrapper.sh`) ⚠️ Superseded
|
|
|
|
Sets env vars on the main LS process. Works for routing but the main LS's
|
|
LLM client ignores `HTTPS_PROXY`. Superseded by standalone mode.
|
|
|
|
### 3. iptables REDIRECT (All Traffic) ❌ Abandoned
|
|
|
|
Redirected ALL port-443 traffic. Caused redirect loops, broke other HTTPS
|
|
traffic. Replaced by UID-scoped redirect.
|
|
|
|
### 4. DNS Redirect (`/etc/hosts`) ❌ Abandoned
|
|
|
|
Same TLS trust issue as #3. Unnecessary with UID-scoped iptables.
|
|
|
|
### 5. Standalone LS + UID-scoped iptables ✅ WORKING
|
|
|
|
Current solution. Full MITM interception with zero side effects.
|
|
|
|
## The Original Blocker (SOLVED)
|
|
|
|
> The LS's Go LLM HTTP client uses a custom `tls.Config` that does NOT read
|
|
> from `SSL_CERT_FILE` or the system CA store.
|
|
|
|
**This turned out to be wrong.** The Go client DOES honor `SSL_CERT_FILE` when:
|
|
|
|
- The env var is set BEFORE the process starts (not injected later)
|
|
- The value contains a combined bundle (system CAs + custom CA)
|
|
- `SSL_CERT_DIR` is set to `/dev/null` to force exclusive use of `SSL_CERT_FILE`
|
|
|
|
The standalone LS gives us full control over the process environment at spawn
|
|
time, which is why this approach works while the wrapper approach didn't.
|
|
|
|
## Technical Details
|
|
|
|
### API Endpoint
|
|
|
|
`POST https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse`
|
|
|
|
### SSE Response Format
|
|
|
|
```
|
|
data: {"response": {"candidates": [{"content": {"role": "model", "parts": [{"text": "..."}]}}],
|
|
"usageMetadata": {"promptTokenCount": 1514, "candidatesTokenCount": 25,
|
|
"totalTokenCount": 1539, "thoughtsTokenCount": 52},
|
|
"modelVersion": "gemini-3-flash"}, "traceId": "...", "metadata": {}}
|
|
```
|
|
|
|
Last event includes `"finishReason": "STOP"` in the candidate.
|
|
|
|
### Other Intercepted Endpoints
|
|
|
|
| Endpoint | Type | Content |
|
|
| --------------------------- | -------- | ---------------- |
|
|
| `fetchUserInfo` | Protobuf | User info |
|
|
| `loadCodeAssist` | Protobuf | Extension config |
|
|
| `fetchAvailableModels` | Protobuf | Model catalog |
|
|
| `webDocsOptions` | Protobuf | Docs config |
|
|
| `streamGenerateContent` | SSE/JSON | LLM responses ✅ |
|
|
| `recordCodeAssistMetrics` | Protobuf | Telemetry |
|
|
| `recordTrajectoryAnalytics` | Protobuf | Telemetry |
|
|
|
|
### 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 |
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# One-time setup (creates user + iptables rule)
|
|
sudo ./scripts/mitm-redirect.sh install
|
|
|
|
# Run proxy with standalone LS + MITM
|
|
RUST_LOG=info ./target/release/antigravity-proxy --standalone
|
|
|
|
# Check usage
|
|
curl -s http://localhost:8741/v1/usage | jq .
|
|
```
|
|
|
|
### Cleanup
|
|
|
|
```bash
|
|
# Remove iptables rule + user
|
|
sudo ./scripts/mitm-redirect.sh uninstall
|
|
```
|