wstunnel
A high-performance WebSocket tunnel library for Go that implements the net.Conn interface over WebSocket connections. Enables TCP and UDP traffic tunneling through WebSocket connections with TLS support, optional end-to-end encryption and browser fingerprinting.
Features
net.Conn Interface: Seamless integration with existing Go network code
- TCP & UDP Support: Tunnel both TCP and UDP traffic through WebSocket
- TLS Encryption: Built-in TLS support for secure connections
- End-to-End Encryption: Optional ChaCha20-Poly1305 encryption with steganographic key exchange
- Browser Fingerprinting: Mimic real browser traffic using uTLS (Chrome, Firefox, Safari, Edge)
- Custom Headers: Support for custom HTTP headers in WebSocket handshake
- High Performance: Optimized for throughput with zero-copy optimizations
- Middleware Support: HTTP middleware for authentication, logging, etc.
- Zero-value Configs: Idiomatic Go API with sensible defaults
Installation
go get -u github.com/n0madic/go-wstunnel
Quick Start
Client Example
package main
import (
"fmt"
"io"
"time"
"github.com/n0madic/go-wstunnel"
)
func main() {
client := wstunnel.NewClient(wstunnel.ClientConfig{
BrowserFingerprint: wstunnel.BrowserChrome,
Timeout: 30 * time.Second,
InsecureSkipVerify: true,
})
conn, err := client.Connect("example.com:8443")
if err != nil {
panic(err)
}
defer conn.Close()
conn.Write([]byte("Hello, tunnel!"))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
panic(err)
}
fmt.Printf("Received: %s\n", buf[:n])
}
Server Example
package main
import (
"crypto/tls"
"io"
"net"
"github.com/n0madic/go-wstunnel"
)
func main() {
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
panic(err)
}
server := wstunnel.NewServer(wstunnel.ServerConfig{
Handler: handleConnection,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
},
})
panic(server.ListenAndServe(":8443"))
}
func handleConnection(tunnelConn *wstunnel.Conn) {
io.Copy(tunnelConn, tunnelConn)
}
End-to-End Encryption
Encrypted Client Example
client := wstunnel.NewClient(wstunnel.ClientConfig{
Encrypted: true,
BrowserFingerprint: wstunnel.BrowserChrome,
InsecureSkipVerify: true,
})
conn, err := client.Connect("server.example.com:8443")
if err != nil {
panic(err)
}
defer conn.Close()
conn.Write([]byte("Secret message"))
Server with Auto-Detection
server := wstunnel.NewServer(wstunnel.ServerConfig{
Handler: func(conn *wstunnel.Conn) {
io.Copy(conn, conn)
},
Encrypted: false,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
},
})
Server with Mandatory Encryption
server := wstunnel.NewServer(wstunnel.ServerConfig{
Handler: func(conn *wstunnel.Conn) {
io.Copy(conn, conn)
},
Encrypted: true,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
},
})
Security Features
- ChaCha20-Poly1305 AEAD: Authenticated encryption with 256-bit keys
- X25519 Key Exchange: Elliptic curve Diffie-Hellman with perfect forward secrecy
- Steganographic Headers: Key exchange hidden in innocent HTTP cookies
- Automatic Nonce Management: No nonce reuse with deterministic overflow protection
- Zero Configuration: Server automatically detects encrypted clients
- Mandatory Encryption Modes: Both client and server can enforce encryption
- Perfect Forward Secrecy: Each connection uses unique ephemeral keys
Advanced Usage
headers := make(http.Header)
headers.Set("Authorization", "Bearer token123")
headers.Set("X-Custom-Header", "value")
client := wstunnel.NewClient(wstunnel.ClientConfig{
Headers: headers,
})
Server with Custom Routes and WebSocket Path
server := wstunnel.NewServer(wstunnel.ServerConfig{
WSPath: "/tunnel",
Handler: myHandler,
Routes: map[string]http.HandlerFunc{
"/healthz": func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
},
"/api/status": func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"running"}`))
},
},
})
server.Use(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
token := r.Header.Get("Authorization")
if !isValidToken(token) {
http.Error(w, "Unauthorized", 401)
return
}
next(w, r)
})
server.AddRoute("/metrics", metricsHandler)
Client with Custom WebSocket Path
client := wstunnel.NewClient(wstunnel.ClientConfig{
WSPath: "/tunnel",
BrowserFingerprint: wstunnel.BrowserChrome,
})
Port Forwarding Server
func forwardToTarget(tunnelConn *wstunnel.Conn) {
buf := make([]byte, 256)
n, err := tunnelConn.Read(buf)
if err != nil {
return
}
target := string(buf[:n])
parts := strings.SplitN(target, ":", 3)
if len(parts) != 3 {
return
}
targetConn, err := net.Dial(parts[0], parts[1]+":"+parts[2])
if err != nil {
return
}
defer targetConn.Close()
tunnelConn.Write([]byte("OK"))
go io.Copy(targetConn, tunnelConn)
io.Copy(tunnelConn, targetConn)
}
Configuration
ClientConfig
type ClientConfig struct {
Timeout time.Duration
InsecureSkipVerify bool
TLSConfig *tls.Config
Encrypted bool
BrowserFingerprint BrowserType
UserAgent string
Headers http.Header
WSPath string
ReadBufferSize int
WriteBufferSize int
EnableCompression bool
}
ServerConfig
type ServerConfig struct {
TLSConfig *tls.Config
ReadHeaderTimeout time.Duration
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
WSPath string
CheckOrigin func(*http.Request) bool
Subprotocols []string
ReadBufferSize int
WriteBufferSize int
EnableCompression bool
Encrypted bool
Handler ConnectionHandler
Routes map[string]http.HandlerFunc
}
Browser Types
const (
BrowserChrome BrowserType = "chrome"
BrowserFirefox BrowserType = "firefox"
BrowserSafari BrowserType = "safari"
BrowserEdge BrowserType = "edge"
BrowserRandom BrowserType = "random"
)
Performance
The library is optimized for high throughput with realistic end-to-end benchmarks that include all WebSocket + TLS + framing overhead:
Benchmark Results
Tested on Apple M4 Max (14 cores) - Average of 3 runs
| TCP Echo (Round-trip) | | | | | |
| Quick Test | 10 MB | 0.14s | 147.1 MB/s | 2,560 | Fast validation (unencrypted) |
| Quick Test Encrypted | 10 MB | 0.15s | 134.1 MB/s | 2,560 | Fast validation (encrypted) |
| 1KB Blocks | 100 MB | 3.70s | 54.1 MB/s | 102,400 | Small packet efficiency |
| 64KB Blocks | 200 MB | 0.52s | 770.3 MB/s | 3,200 | Large packet optimization |
| TCP One-way (Maximum Speed) | | | | | |
| 32KB Blocks | 500 MB | 0.50s | 1009.0 MB/s | 16,000 | Unidirectional peak performance |
| UDP Echo (Round-trip) | | | | | |
| 1KB Blocks | 50 MB | 2.02s | 49.5 MB/s | 51,200 | UDP with round-trip latency |
| Concurrent Connections | | | | | |
| 4 Parallel | 200 MB total | 0.19s | 1031.2 MB/s | 12,800 | Multiple connections |
Performance Characteristics
- Round-trip (Echo): Lower throughput due to network latency and echo server processing
- One-way: Maximum throughput when data flows in single direction
- Large Blocks: Better efficiency with 64KB+ packet sizes
- Concurrent: Best performance with multiple parallel connections
- UDP: Consistent performance around 50 MB/s for small packets
- Encryption Overhead: ~9% performance impact with ChaCha20-Poly1305 end-to-end encryption
All benchmarks include the complete tunnel chain: Client → WebSocket → TLS → Tunnel Server → Target Server → back, providing realistic real-world performance expectations.
Testing
go test
go test -v
go test -bench=.
go test -bench=BenchmarkTCP_Echo_Quick -v
Dependencies
github.com/gorilla/websocket - WebSocket implementation
github.com/refraction-networking/utls - Browser fingerprinting
golang.org/x/crypto/chacha20poly1305 - ChaCha20-Poly1305 AEAD encryption
golang.org/x/crypto/curve25519 - X25519 elliptic curve key exchange
golang.org/x/crypto/hkdf - HMAC-based Key Derivation Function
License
MIT