3.0 KiB
Stealth Mode Setup
Stealth mode makes VPN traffic look like normal HTTPS to deep packet inspection systems. The client opens a real TLS 1.3 connection, performs a WebSocket upgrade, and then carries the konduit protocol inside WebSocket binary frames — identical in appearance to any browser-based web app.
Client ──TLS 1.3──▶ HAProxy :443 (TCP passthrough) ──▶ Konduit :8443
HTTP/1.1 GET /ws → 101 Switching Protocols
WebSocket binary frames (konduit protocol inside)
Server Requirements
- A domain with a valid TLS certificate (Let's Encrypt / certbot)
- HAProxy 2.4+
- Konduit server on a non-public port (e.g.
8443)
HAProxy Configuration
HAProxy acts as a pure TCP proxy — it passes TLS through to the konduit server unchanged.
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 prevents HAProxy from dropping idle connections.
Reload after changes:
haproxy -c -f /etc/haproxy/haproxy.cfg # validate first
systemctl reload haproxy
Konduit Server (server.toml)
[server]
listen_addr = "0.0.0.0"
listen_port = 8443
public_addr = "your-domain.com"
public_port = 443
tls_cert = "/etc/letsencrypt/live/your-domain.com/fullchain.pem"
tls_key = "/etc/letsencrypt/live/your-domain.com/privkey.pem"
websocket = true
Client Configuration (client.toml)
[client]
server_endpoint = "your-domain.com:443"
peer_id = "..."
identity_key = "..."
server_public_key = "..."
[stealth]
enabled = true
That's it — stealth = true activates TLS and WebSocket camouflage automatically.
Flutter / Mobile App
Enable Stealth Mode in the app settings. The toggle maps to [stealth] enabled = true
in 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.
Verify
# TLS cert must be valid — browser must reach your site
curl -s https://your-domain.com/ | head -5
# Connect the VPN client
sudo ./konduit --config client.toml
# Watch HAProxy — you should see the tunnel established
journalctl -u haproxy -f
Common Mistakes
| Mistake | Effect | Fix |
|---|---|---|
HAProxy terminating TLS (bind *:443 ssl crt ...) |
konduit receives plain TCP, WebSocket handshake fails | Use bind *:443 (no ssl) — TCP passthrough |
Missing timeout tunnel 0 |
Connection drops after 30 s of silence | Add timeout tunnel 0 to backend |
| Port 8443 exposed to internet | Bypass HAProxy entirely | Firewall port 8443 to localhost only |
| Expired/self-signed cert | TLS error on client | Use Let's Encrypt; renew via certbot |
stealth = false on client |
Plain TCP connection, DPI detects VPN | Set [stealth] enabled = true |