Rusnel
Description
Rusnel is a fast TCP/UDP tunnel, transported over and encrypted using QUIC protocol. Single executable including both client and server. Written in Rust.
Features
- Easy to use
- Single executable including both client and server.
- Uses QUIC protocol for fast and multiplexed communication.
- Encrypted connections using the QUIC protocol (TLS 1.3).
- Static forward tunneling (TCP, UDP)
- Static reverse tunneling (TCP, UDP)
- Dynamic tunneling (socks5, including UDP ASSOCIATE)
- Dynamic reverse tunneling (reverse socks5, including UDP ASSOCIATE)
- Layered peer authentication: insecure, fingerprint pinning, or full mTLS (see Authentication).
Install
or
Clone the repository and build the project:
Usage
)
)
)
)
)
)
;
)
<SERVER> defines )
<remote>...
<remote>s
<local-host>:<local-port>:<remote-host>:<remote-port>/<protocol>
)
)
)
)
; )
; )
)
;
);
(; )
The client survives both transient network drops and full server restarts
out of the box: on disconnect it logs the close reason (e.g.
closed by peer: server received ^C (code 0)), backs off, and tries every
resolved address in parallel using RFC 8305 Happy Eyeballs so v4-only
servers reachable via a v6-preferring resolver still connect within
~250 ms. Server-side resources (including reverse-tunnel listeners) are
released the moment the QUIC connection drops.
Authentication
Both the server and the client require an explicit TLS-mode flag — there is no silent insecure default. Three modes:
| Mode | Server | Client |
|---|---|---|
| Insecure | --insecure |
--insecure |
| Fingerprint pin | --tls-self-signed (or --tls-cert/--tls-key) |
--tls-fingerprint sha256:... |
| Full mTLS | --tls-cert ... --tls-key ... --tls-ca ... |
--tls-ca ... --tls-cert ... --tls-key ... [--tls-server-name ...] |
Quickest path for a private/single-user setup — the server logs its fingerprint at startup, the client pins it:
# server cert fingerprint: sha256:abcd...
For full mTLS, generate a CA + server + client cert (no openssl required):
rusnel cert --help lists the underlying subcommands (ca, server,
client, fingerprint) for finer control.
Embedded credentials (drop-and-run binaries)
For Sliver-style "drop the binary onto a host and have it just work" deployments,
Rusnel can bake credentials and a default server address into the binary at
build time via RUSNEL_EMBED_* env vars. The resulting binary runs in the
appropriate TLS mode with no flags required (CLI flags still override embedded
values when both are present).
# Pre-configured client: connects to 1.2.3.4:8080, fingerprint-pinned.
RUSNEL_EMBED_SERVER_ADDR=1.2.3.4:8080 \
RUSNEL_EMBED_FINGERPRINT=sha256:abcd... \
# Pre-configured mTLS pair (CA + server cert/key on one binary, CA + client
# cert/key on the other). Both ends run in mTLS mode with no flags.
RUSNEL_EMBED_CA=./pki/ca.pem \
RUSNEL_EMBED_SERVER_CERT=./pki/server.pem \
RUSNEL_EMBED_SERVER_KEY=./pki/server.key \
RUSNEL_EMBED_CA=./pki/ca.pem \
RUSNEL_EMBED_CLIENT_CERT=./pki/client.pem \
RUSNEL_EMBED_CLIENT_KEY=./pki/client.key \
RUSNEL_EMBED_SERVER_NAME=1.2.3.4 \
Recognised vars: RUSNEL_EMBED_SERVER_ADDR, RUSNEL_EMBED_CA,
RUSNEL_EMBED_FINGERPRINT, RUSNEL_EMBED_SERVER_NAME,
RUSNEL_EMBED_SERVER_CERT, RUSNEL_EMBED_SERVER_KEY,
RUSNEL_EMBED_CLIENT_CERT, RUSNEL_EMBED_CLIENT_KEY. Path-style vars are
resolved at build time via include_bytes! (the file no longer needs to exist
on the deployment host); string-style vars are baked in as &'static str.
See build.rs for the full mapping.
Performance
Rusnel (QUIC) vs Chisel (SSH-over-WebSocket) on loopback. Throughput is iperf3 over a tunneled TCP forward (100 MB × 5 runs + warmup, median); latency is the round-trip time of a 64 B echo across the tunnel.

End-to-end HTTP request times through the tunnel across payload sizes (median of 5 runs, error bars show min/max):

The benchmark harness also includes a wan profile that applies
tc qdisc netem delay 25ms to the loopback interface to approximate a
50 ms-RTT WAN. Reproduce everything with ./benchmark/run.sh
(requires Docker; needs --cap-add=NET_ADMIN for netem profiles, which
the script adds for you). See benchmark/ for tunables.
TODO
Reliability & UX
- client reconnect with exponential backoff (configurable via
--max-retry-count/--max-retry-interval) - proxy support for client:
--proxy socks5://[user:pass@]host:portroutes the QUIC connection through a SOCKS5 proxy via UDP ASSOCIATE (RFC 1928 §4). HTTP CONNECT is intentionally not supported in this release because it cannot carry UDP — see the WebSocket-fallback transport item underSecurity & access controlfor the path that would unlock HTTP/SOCKS-CONNECT proxies. -
RUST_LOG-style env filter fortracing-subscriber(per-module log levels)
Protocol features
- add fake-backend http/3 feature to server (real HTTP/3 facade for active probes that open streams)
- skip the 1-RTT control handshake on static forwards (cache the parsed
RemoteRequestserver-side; saves ~1 RTT per accepted TCP connection on WAN) - enable QUIC 0-RTT connection resumption (session-ticket cache; cuts the first request after
rusnel clientstartup from ~3 RTT to ~1 RTT) - UDP hole-punching / NAT traversal mode: introduce a
rusnel brokerrole that observes each peer's reflexive address (optionally cross-checked against public STUN servers to detect symmetric NAT) and brokers a direct QUIC connection between two NATed peers à la libp2p DCUtR / Tailscale DERP, with relay fallback when punching fails. Lets two devices behind NAT talk without anyone running a publicly-reachable data-plane server.
Security & access control
- server-side remote ACLs:
--allow/--denyflags (and config-file equivalents) accepting wildcardedRemoteRequestpatterns, e.g.--allow socks,--allow R:2222:localhost:22,--deny tcp:*:*:169.254.169.254:*. Default-deny dangerous targets like cloud instance-metadata endpoints. With mTLS, bind ACLs to the client cert subject / fingerprint so a contractor cert can be scoped to one tunnel. - SSO / OIDC client auth via a
rusnel-issuerdaemon: client runsrusnel client --sso https://issuer.corp.example, completes a device-code flow against the org's IdP (Okta/Google/Auth0), and the issuer mints a short-lived (~8h) mTLS client cert with the user's email/groups in the SAN. Rusnel server only needs to trust the issuer's CA — no IdP knowledge in the data path. ACLs from the bullet above match on cert subject/groups.
Operability
- server admin API + CLI + web UI:
- typed
ServerState(DashMap of clients/tunnels with bytes-in/out counters) populated from the existing per-connection / per-tunnel handlers. - HTTP admin API on a unix socket (filesystem-perm gated) or TCP+mTLS:
GET /clients,GET /clients/:id/tunnels,GET /tunnels,DELETE /clients/:id(kick),GET /metrics(Prometheus). rusnel ctl clients|tunnels|kicksubcommand consuming that API.- tiny embedded web UI (single
include_str!'d HTML file, no JS framework) with a client/tunnel dashboard and bandwidth sparklines off/metrics.
- typed
Testing & CI
- run
./benchmark/run.shon a self-hosted runner per release tag and commit the result PNGs back, so perf regressions surface in PRs