diff --git a/docs/stealth-setup.md b/docs/stealth-setup.md index 925f5c3..3645596 100644 --- a/docs/stealth-setup.md +++ b/docs/stealth-setup.md @@ -17,9 +17,12 @@ connection to a legitimate domain — not a VPN. 3. **Konduit** receives a plain TCP connection and runs its normal handshake. Stealth mode must be **disabled** on the server side — HAProxy already handled TLS. -The `stealth = true` and `tls = true` config flags are equivalent. Both activate TLS -wrapping on the client. Existing configs with `stealth = true` continue to work without -modification. +There are two client-side flags: + +- **`tls = true`** — activates real TLS wrapping only (for the HAProxy TLS-termination model above) +- **`[stealth] enabled = true`** — activates TLS **and** WebSocket camouflage (see [WebSocket mode](#websocket-camouflage-tspu--russia-bypass) below) + +Use `tls = true` when HAProxy terminates TLS for you. Use `stealth = true` when the server handles TLS directly and you need WebSocket framing to bypass DPI. ## Server Requirements @@ -99,18 +102,26 @@ echo "your-mantra" | ./konduit-ctl bootstrap \ ## Client Configuration (`client.toml`) +For the HAProxy TLS-termination model (HAProxy decrypts TLS, konduit receives plain TCP): + ```toml [client] -server_endpoint = "your-domain.com:443" -tls = true -peer_id = "..." -identity_key = "..." +server_endpoint = "your-domain.com:443" +tls = true +peer_id = "..." +identity_key = "..." server_public_key = "..." ``` -The legacy flag is identical: +For WebSocket camouflage mode (konduit handles TLS and WebSocket end-to-end): ```toml +[client] +server_endpoint = "your-domain.com:443" +peer_id = "..." +identity_key = "..." +server_public_key = "..." + [stealth] enabled = true ``` @@ -118,8 +129,8 @@ enabled = true ## Flutter / Mobile App Enable **Stealth Mode** in the app settings. The toggle maps to `stealth = true` in -the connection config and activates TLS wrapping on all platforms (Linux, Android, -Windows). +the connection config and activates TLS + WebSocket camouflage on all platforms +(Linux, Android, Windows). For QR-code-based provisioning, the `t: true` field in the QR payload enables stealth. @@ -136,11 +147,77 @@ curl -s https://your-domain.com/ | head -5 journalctl -u haproxy -f ``` +## WebSocket Camouflage (TSPU / Russia Bypass) {#websocket-camouflage-tspu--russia-bypass} + +Some ISPs (notably Russia's TSPU system) allow real TLS but still drop connections whose +payload doesn't look like HTTP. WebSocket camouflage solves this: konduit protocol runs +inside WebSocket binary frames over HTTPS, which TSPU whitelists (Telegram Web, online +games, and countless other services use the same pattern). + +``` +Client ──TLS 1.3──▶ HAProxy :443 (TCP passthrough) ──▶ Konduit :8443 + HTTP/1.1 GET /ws + 101 Switching Protocols + WS binary frames (konduit protocol inside) +``` + +HAProxy acts as a pure TCP proxy — it does **not** terminate TLS in this mode. + +### Server (`server.toml`) + +```toml +[server] +listen_addr = "0.0.0.0" +listen_port = 8443 +public_addr = "your-domain.com" +public_port = 443 +tls = true +tls_cert = "/etc/letsencrypt/live/your-domain.com/fullchain.pem" +tls_key = "/etc/letsencrypt/live/your-domain.com/privkey.pem" +websocket = true +``` + +### HAProxy (`haproxy.cfg`) + +```haproxy +frontend https-ingress + bind *:443 + mode tcp + default_backend konduit-vpn + +backend konduit-vpn + mode tcp + timeout tunnel 0 + server konduit 127.0.0.1:8443 +``` + +`timeout tunnel 0` disables HAProxy's idle-connection timeout for established tunnels. +Without it, connections are killed after 30 seconds of silence. + +### Client (`client.toml`) + +```toml +[client] +server_endpoint = "your-domain.com:443" +peer_id = "..." +identity_key = "..." +server_public_key = "..." + +[stealth] +enabled = true +``` + +`stealth = true` tells the client to use TLS and WebSocket framing. No other flags needed. + +--- + ## Common Mistakes | Mistake | Effect | Fix | |---------|--------|-----| | `WAIT_END` in inspect rule | 5-second stall on every connect | Use `req.payload(0,1) -m found` | -| Stealth enabled on server | Handshake mismatch after HAProxy strips TLS | Set `[stealth] enabled = false` | +| Stealth enabled on server (HAProxy-TLS model) | Handshake mismatch after HAProxy strips TLS | Set `[stealth] enabled = false` on server | +| `tls = true` instead of `stealth = true` on client | Connects but drops — no WebSocket framing | Use `[stealth] enabled = true` for TSPU bypass | +| Missing `timeout tunnel 0` in HAProxy | Connection drops after 30 s of silence | Add `timeout tunnel 0` to backend | | Port 8443 exposed to internet | Bypass HAProxy, no DPI camouflage | Firewall port 8443 to localhost only | | Expired/self-signed cert | TLS error on client | Use Let's Encrypt; renew via certbot |