Skip to content

Repository files navigation

TunnelVision

Move any traffic through a single WebSocket.

TunnelVision is a high-performance tunneling engine written in Go. It hides arbitrary TCP, UDP, SOCKS5, and HTTP-proxy traffic inside ordinary WebSocket or HTTP/2 streams — the kind of traffic every firewall, corporate proxy, and DPI box already lets through. If a network permits HTTPS, it permits TunnelVision.

It ships as a single static binary, runs as either end of a tunnel, and drops in as a Go library when you want to build tunneling into your own software.


Why TunnelVision

  • Goes where plain sockets can't. Restrictive gateways see nothing but a long-lived WebSocket or an HTTP/2 POST. Your SSH session, database connection, game traffic, or WireGuard handshake rides inside it.
  • Speaks a lot of protocols. TCP, UDP, SOCKS5, HTTP CONNECT, Unix sockets, stdio, and Linux transparent proxying — forward or reverse.
  • Built for scale. Every tunnel is a handful of goroutines. Thousands of concurrent connections stay cheap.
  • Secure by construction. TLS and mTLS, ECH, JWT-authenticated tunnels, and server-side rules that decide exactly what a client is allowed to reach.
  • Operator-friendly. Structured slog logging, hot-reloadable certificates, self-signed bootstrap, systemd and Windows service tooling, and a Caddy module.
  • Embeddable. Clean package layout so pkg/client, pkg/server, and pkg/protocol compose into your own binaries.

Quickstart

Grab a binary from Releases, or build it yourself:

git clone https://github.com/DsThakurRawat/TunnelVision.git
cd TunnelVision
make build          # -> ./bin/tunnelvision
# or: go build -o tunnelvision ./cmd/tunnelvision   (needs Go 1.25+)

Stand up a server, then reach a service through it:

# On the far side of the firewall:
tunnelvision server --tls wss://0.0.0.0:8443

# On your machine — expose the remote's Postgres on a local port:
tunnelvision client -L tcp://5432:db.internal:5432 wss://gateway.example.com:8443

psql -h 127.0.0.1 -p 5432 now talks to db.internal:5432, tunneled over TLS-wrapped WebSocket.


What you can tunnel

A tunnel is written as scheme://[listen:]host:port[?options].

Forward tunnels (-L) — listen locally, exit at the server

# Local SOCKS5 proxy, everything routed out through the server
tunnelvision client -L socks5://127.0.0.1:1080 wss://gateway.example.com

# Local HTTP CONNECT proxy
tunnelvision client -L http://127.0.0.1:3128 wss://gateway.example.com

# Point a single local port at one remote destination
tunnelvision client -L tcp://8080:example.com:443 wss://gateway.example.com

# UDP, e.g. DNS — reclaim idle sessions after 15s
tunnelvision client -L "udp://5353:1.1.1.1:53?timeout_sec=15" wss://gateway.example.com

# Hand the real client IP to the backend with a PROXY v2 header
tunnelvision client -L "tcp://8080:backend.internal:80?proxy_protocol" wss://gateway.example.com

# Unix socket -> remote unix socket; and stdio (great as an SSH ProxyCommand)
tunnelvision client -L unix:///tmp/local.sock:/var/run/app.sock wss://gateway.example.com
tunnelvision client -L stdio://remote-host:22 wss://gateway.example.com

Schemes: tcp, udp, socks5, http, unix, stdio, tproxy+tcp, tproxy+udp.

Query options:

Option Effect
?proxy_protocol Prepend a HAProxy PROXY v2 header to the target (TCP forward tunnels), so backends log the originating client.
?timeout_sec=N Idle timeout for UDP sessions, seconds. Default 30; 0 disables.
?login=U&password=P Credentials required by a socks5 / http proxy listener.

Reverse tunnels (-R) — listen on the server, exit at the client

# Expose the client's local web server on the server's port 8080
tunnelvision client -R tcp://8080:127.0.0.1:80 wss://gateway.example.com

# Turn the client into an exit node: a SOCKS5 proxy on the server,
# dynamic destinations dialed from the client's network
tunnelvision client -R socks5://0.0.0.0:1080 wss://gateway.example.com

# Same idea over HTTP CONNECT
tunnelvision client -R http://0.0.0.0:3128 wss://gateway.example.com

Reverse schemes: tcp, udp, unix, socks5, http.

Heads-up on dynamic reverse tunnels. Reverse udp, socks5, and http carry the per-connection target in an in-band frame between TunnelVision's own client and server. They work Go-to-Go only — the same way --mode ws does — and are not wire-compatible with other implementations. Static reverse tcp/unix are fully interoperable.

Transparent proxy (Linux)

tproxy+tcp and tproxy+udp intercept traffic without the application knowing. They need Linux, CAP_NET_ADMIN (or root), and an iptables rule pointing at the listener:

tunnelvision client -L tproxy+tcp://0.0.0.0:1234 wss://gateway.example.com
# True TPROXY (mark-based) on port 1234:
iptables -t mangle -A PREROUTING -p tcp --dport 80 \
  -j TPROXY --on-port 1234 --tproxy-mark 0x1/0x1
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

-j REDIRECT (DNAT) setups work too — TunnelVision reads the original destination from the socket and falls back to SO_ORIGINAL_DST when needed. UDP recovers each datagram's original destination via IP_RECVORIGDSTADDR.


TLS and security

# Bootstrap TLS instantly with an in-memory self-signed certificate
tunnelvision server --tls wss://0.0.0.0:8443

# Bring your own certificate and require client certs (mTLS)
tunnelvision server \
  --tls-certificate cert.pem --tls-private-key key.pem \
  --tls-client-ca-certs ca.pem \
  --restrict-config rules.yaml wss://0.0.0.0:8443

# Rotate certificates without downtime — replace the files, then:
kill -HUP "$(pidof tunnelvision)"     # also picks up changes automatically within a few seconds
  • TLS / mTLS — full verification, client certificates, both ends.
  • Self-signed bootstrap--tls with no cert/key generates an ephemeral certificate in memory; clients trust it or disable verification.
  • Hot-reload — server and client certificates reload on file change (polling) or on SIGHUP. A bad reload is ignored and the last-good certificate stays live.
  • ECH & SNI control — Encrypted Client Hello, plus SNI override or suppression.
  • JWT-authenticated tunnels — every tunnel request is a signed token describing exactly what it may open.
  • Server-side rules — a YAML policy restricts which destinations, ports, and path prefixes a client can use.

Transports

TunnelVision negotiates one of three carriers:

Transport Select with Notes
WebSocket (default) ws:// / wss:// Battle-tested framing with deliberate deviations for broad compatibility.
Strict RFC 6455 --mode ws Standards-clean WebSocket; interoperates with generic Go WebSocket clients.
HTTP/2 http:// / https:// Full-duplex streaming over an HTTP/2 POST.

Deployment

systemd — template units manage client and server from config files:

# /etc/tunnelvision/client-myserver.yaml
sudo systemctl enable --now tunnelvision-client@myserver
# /etc/tunnelvision/server-main.yaml
sudo systemctl enable --now tunnelvision-server@main

Windows — register a background task with the bundled PowerShell scripts:

.\packaging\windows\install.ps1 -ConfigPath "C:\path\client.yaml" -BinaryPath "C:\path\tunnelvision.exe"
.\packaging\windows\control.ps1 -Action start

Caddy — build TunnelVision into Caddy and let it terminate TLS (including mTLS):

xcaddy build --with github.com/DsThakurRawat/TunnelVision/pkg/caddy
{
    order tunnelvision before reverse_proxy
}
example.com {
    route /tunnelvision/* {
        tunnelvision {
            prefix /tunnelvision
            mode ws
            # restrict_config /etc/tunnelvision/rules.yaml
        }
    }
}

Packages.deb, .rpm, and .apk are attached to each release. Docker images are on the roadmap.


Configuration

Configure by flags, environment, or a YAML file (--config).

Global flags
  • --config — path to a YAML config file.
  • --log-lvlTRACEERROR / OFF (default INFO).
  • --no-color — plain log output.
  • --nb-worker-threads — accepted for compatibility (TOKIO_WORKER_THREADS); a no-op in Go.
Client flags
  • -L, --local-to-remote, -R, --remote-to-local — define tunnels.
  • --mode — default or ws (strict RFC 6455).
  • --http-upgrade-path-prefix — upgrade path prefix (default v1).
  • --jwt-secret — secret used to sign tunnel JWTs.
  • --http-upgrade-credentials, -H, --header, --http-headers-file — customize the upgrade request.
  • --tls-verify-certificate, --tls-certificate, --tls-private-key — verification and client mTLS (hot-reloaded).
  • --tls-sni-override, --tls-sni-disable, --tls-ech-enable — SNI and ECH control.
  • --http-proxy, --http-proxy-login, --http-proxy-password — dial through an HTTP proxy.
  • --connection-min-idle, --connection-retry-max-backoff, --reverse-tunnel-connection-retry-max-backoff — pooling and retry.
  • --socket-so-mark — (Linux) SO_MARK on outgoing sockets.
  • --dns-resolver, --dns-resolver-prefer-ipv4 — resolver control.
  • --websocket-ping-frequency, --websocket-mask-frame — keep-alive and frame masking.
Server flags
  • --mode — default or ws.
  • --restrict-to, -r, --restrict-http-upgrade-path-prefix, --restrict-config — access policy.
  • --jwt-secret — verifies tunnel JWT signatures under --mode ws. In default mode, tokens are parsed without cryptographic verification.
  • --insecure-no-jwt-validation — accept unverified HS256 tokens even in --mode ws.
  • --tls — serve TLS; generate an ephemeral self-signed cert if none is supplied.
  • --tls-certificate, --tls-private-key — server cert/key (hot-reloaded on change or SIGHUP).
  • --tls-client-ca-certs — enable mTLS.
  • --socket-so-mark, --dns-resolver, --dns-resolver-prefer-ipv4 — networking.
  • --websocket-ping-frequency, --websocket-mask-frame — WebSocket behavior.
  • --http-proxy, --http-proxy-login, --http-proxy-password — route server-side dials through a proxy.
  • --remote-to-local-server-idle-timeout — reap idle reverse-tunnel listeners.
# config.yaml  ->  tunnelvision --config config.yaml
mode: client            # or: server
log_lvl: INFO
client:
  remote_addr: wss://gateway.example.com
  local_to_remote:
    - "tcp://8080:example.com:443"
    - "socks5://127.0.0.1:1080"
server:
  listen_addr: ws://0.0.0.0:8080
  restrict_config: /etc/tunnelvision/rules.yaml

As a library

import (
    "github.com/DsThakurRawat/TunnelVision/pkg/client"
    "github.com/DsThakurRawat/TunnelVision/pkg/protocol"
)

func main() {
    c := client.NewClient(client.Config{
        ServerURL:  "wss://gateway.example.com",
        PathPrefix: "v1",
    })

    ltr, _ := client.ParseTunnelArg("tcp://8080:example.com:443", false)
    go c.StartTunnel(ltr)

    select {}
}

Compatibility and status

TunnelVision implements a flexible wire protocol for forward tunnels and static reverse (TCP/Unix) tunnels. Dynamic reverse tunnels are a Go-native extension and stay within the TunnelVision client/server pair.

Capability Status Cross-implementation
TCP forward / reverse
UDP forward
UDP reverse ⚠️ TunnelVision ↔ TunnelVision
SOCKS5 forward
SOCKS5 reverse ⚠️ TunnelVision ↔ TunnelVision
HTTP CONNECT forward
HTTP CONNECT reverse ⚠️ TunnelVision ↔ TunnelVision
Unix sockets / stdio
Transparent proxy (Linux TCP/UDP)
PROXY protocol injection (?proxy_protocol)
UDP idle timeout (?timeout_sec)
mTLS
Ephemeral self-signed TLS (--tls) N/A
Certificate hot-reload (poll + SIGHUP) N/A
ECH (Encrypted Client Hello)
HTTP/2 transport
JWT authentication
YAML restriction rules

Performance. Expect throughput on par with native connections and sub-millisecond added latency; idle memory sits around ~20 MB (Go runtime and goroutine stacks). Numbers are workload- and environment-dependent — measure on your own path.


Contributing

  1. make fmt — format.
  2. make lint && make vet — static analysis.
  3. make test — unit and integration tests.
  4. make test-interop — run this whenever you touch protocol code.

Pull requests welcome.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages