Skip to content

Commit 9b8a057

Browse files
committed
docs(plans): add SNI-deferred MITM cert fix plan
1 parent 352bff4 commit 9b8a057

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Fix MITM cert generation for SNI-deferred connections
2+
3+
## Overview
4+
5+
When a SOCKS5 CONNECT arrives with a raw IP (no hostname) and the connection is SNI-deferred, the MITM proxy generates a TLS certificate for the raw IP address. This cert lacks IP SANs, causing `x509: cannot validate certificate for <IP> because it doesn't contain any IP SANs` errors on the upstream connection.
6+
7+
The `sniAwareTLSConfig` callback in goproxy already handles this for normal connections by reading SNI from the TLS ClientHello and regenerating the cert with the hostname. But for SNI-deferred connections, the custom connect handler peeks the ClientHello bytes before they reach goproxy, and the MITM layer may not see the SNI.
8+
9+
Observed in production: OpenClaw's Telegram client falls back to direct IP connections (`149.154.167.220`) when DNS-resolved IPs are unreachable. These IP-only connections get SNI-deferred, pass through the MITM, but the upstream TLS handshake fails because the MITM cert is for the IP.
10+
11+
## Context
12+
13+
- SNI deferral: `internal/proxy/server.go` (handleConnect, sniPolicyCheck)
14+
- SNI extraction: `internal/proxy/sni.go` (extractSNI, peekSNI)
15+
- MITM cert generation: `internal/proxy/inject.go` (sniAwareTLSConfig, GetConfigForClient)
16+
- DNS reverse cache: `internal/proxy/dns_reverse.go`
17+
18+
## Development Approach
19+
20+
- **Testing approach**: Regular (code first, then tests)
21+
- CRITICAL: every task MUST include new/updated tests
22+
- CRITICAL: all tests must pass before starting next task
23+
24+
## Testing Strategy
25+
26+
- Unit tests: `internal/proxy/sni_test.go` for SNI extraction
27+
- Integration tests: `internal/proxy/server_test.go` for SNI-deferred MITM connections
28+
- Verify on knuth with OpenClaw's Telegram fallback IP behavior
29+
30+
## Implementation Steps
31+
32+
### Task 1: Investigate SNI flow through custom connect handler to MITM
33+
34+
**Files:**
35+
- Read: `internal/proxy/server.go` (handleConnect, sniPolicyCheck)
36+
- Read: `internal/proxy/inject.go` (sniAwareTLSConfig)
37+
38+
- [ ] Trace the data flow: when sniPolicyCheck peeks ClientHello bytes and returns `io.MultiReader(buf, reader)`, verify that goproxy's TLS layer still receives the ClientHello (including SNI)
39+
- [ ] Check if `sniAwareTLSConfig.GetConfigForClient` is called and receives the SNI hostname
40+
- [ ] Identify whether the peeked bytes are consumed before goproxy reads them
41+
- [ ] Document the exact failure point
42+
- [ ] Run tests
43+
44+
### Task 2: Fix cert generation for SNI-deferred IP connections
45+
46+
**Files:**
47+
- Modify: `internal/proxy/server.go`
48+
- Modify: `internal/proxy/inject.go`
49+
- Test: `internal/proxy/server_test.go`
50+
51+
- [ ] After SNI extraction in sniPolicyCheck, pass the recovered hostname to the MITM layer
52+
- [ ] Option A: update the CONNECT target in the goproxy context so the MITM uses hostname instead of IP
53+
- [ ] Option B: store the SNI hostname in the connection context and use it in sniAwareTLSConfig
54+
- [ ] Option C: replace the IP in the dial target with the SNI hostname before routing to the injector
55+
- [ ] Ensure the DNS reverse cache is populated (already done in sniPolicyCheck)
56+
- [ ] Write test: SNI-deferred IP connection produces cert with hostname SAN
57+
- [ ] Write test: SNI-deferred IP connection successfully MITMs upstream TLS
58+
- [ ] Run tests
59+
60+
### Task 3: Verify acceptance criteria
61+
62+
- [ ] Deploy to knuth, restart stack
63+
- [ ] Verify OpenClaw's Telegram fallback IP connections work through MITM
64+
- [ ] Verify no `IP SANs` errors in sluice logs
65+
- [ ] Verify credential injection works for IP-only Telegram connections
66+
- [ ] Run full test suite: `go test ./... -v -timeout 30s`
67+
68+
### Task 4: [Final] Update documentation
69+
70+
- [ ] Update CLAUDE.md SNI deferral section to note the cert generation fix
71+
- [ ] Move this plan to `docs/plans/completed/`
72+
73+
## Post-Completion
74+
75+
**Manual verification:**
76+
- Force OpenClaw to use IP-only Telegram connection (block DNS, let fallback kick in)
77+
- Verify MITM cert has correct hostname SAN
78+
- Check sluice logs for clean SNI -> ALLOW flow without cert errors

0 commit comments

Comments
 (0)