rusnel 0.6.0

Rusnel is a fast TCP/UDP tunnel, transported over and encrypted using QUIC protocol. Single executable including both client and server
Documentation

Rusnel

Crates.io

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

cargo install rusnel

or

Clone the repository and build the project:

git clone https://github.com/guyte149/Rusnel.git
cd rusnel
cargo build --release

Usage

$ rusnel --help
A fast tcp/udp tunnel

Usage: rusnel <COMMAND>

Commands:
  server  run Rusnel in server mode
  client  run Rusnel in client mode
  cert    generate certificates for use with --tls-* flags
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version
$ rusnel server --help
run Rusnel in server mode

Usage: rusnel server [OPTIONS]

Options:
      --host <HOST>          defines Rusnel listening host [default: 0.0.0.0]
  -p, --port <PORT>          defines Rusnel listening port [default: 8080]
      --allow-reverse        Allow clients to specify reverse port forwarding remotes
      --allow-socks          Allow clients to request reverse SOCKS5 dynamic tunnels
                             (R:socks). Default-deny; forward `socks` is not gated.
      --insecure             Disable all TLS authentication (testing only)
      --tls-self-signed      Persisted self-signed cert under --tls-state-dir
      --tls-state-dir <DIR>  Directory for persisted self-signed cert/key (default: ~/.rusnel)
      --tls-cert <PATH>      Server PEM cert (paired with --tls-key)
      --tls-key  <PATH>      Server PEM key  (paired with --tls-cert)
      --tls-ca   <PATH>      Enable mTLS: require client certs signed by this CA
      --congestion <CC>      QUIC congestion controller: cubic (default) or bbr.
                             cubic wins on loopback / clean LANs; bbr wins on
                             high-BDP / lossy WAN links (≳25ms RTT or any loss).
  -v, --verbose              enable verbose logging
      --debug                enable debug logging
  -h, --help                 Print help
$ rusnel client --help
run Rusnel in client mode

Usage: rusnel client [OPTIONS] <SERVER> <remote>...

Arguments:
  <SERVER>     defines the Rusnel server address (in form of host:port)
  <remote>...
               <remote>s are remote connections tunneled through the server, each which come in the form:

                   <local-host>:<local-port>:<remote-host>:<remote-port>/<protocol>

                    local-host defaults to 0.0.0.0 (all interfaces).
                    local-port defaults to remote-port.
                    remote-port is required*.
                    remote-host defaults to 0.0.0.0 (server localhost).
                    protocol defaults to tcp.

               which shares <remote-host>:<remote-port> from the server to the client as <local-host>:<local-port>, or:

                   R:<local-host>:<local-port>:<remote-host>:<remote-port>/<protocol>

               which does reverse port forwarding,
               sharing <remote-host>:<remote-port> from the client to the server\'s <local-host>:<local-port>.

                   example remotes

                       1337
                       example.com:1337
                       1337:google.com:80
                       192.168.1.14:5000:google.com:80
                       socks
                       5000:socks
                       R:2222:localhost:22
                       R:socks
                       R:5000:socks
                       1.1.1.1:53/udp
                       [::1]:80
                       [::1]:5000:[2001:db8::1]:80
                       R:[::1]:2222:[::1]:22

                   IPv6 literals must be wrapped in [brackets] so the parser
                   can disambiguate them from the colon-separated address
                   format (same convention as URLs and ssh -L).

                   When the Rusnel server has --allow-reverse enabled, remotes can be prefixed with R to denote that they are reversed.

                   Remotes can specify "socks" in place of remote-host and remote-port.
                   The default local host and port for a "socks" remote is 127.0.0.1:1080.


Options:
      --insecure                  Skip server cert verification (testing only)
      --tls-fingerprint <SHA256>  Pin server cert by SHA-256 fingerprint
      --tls-ca <PATH>             Verify server cert against this CA bundle
      --tls-cert <PATH>           Client PEM cert (mTLS; paired with --tls-key + --tls-ca)
      --tls-key  <PATH>           Client PEM key  (mTLS; paired with --tls-cert + --tls-ca)
      --tls-server-name <NAME>    Override SNI / verification name
      --congestion <CC>           QUIC congestion controller: cubic (default) or
                                  bbr. cubic wins on loopback / clean LANs; bbr
                                  wins on high-BDP / lossy WAN links.
      --max-retry-count <N>       Reconnect attempts after a disconnect or
                                  failed connect. -1 = retry forever (default);
                                  counter resets on every successful connect.
      --max-retry-interval <S>    Cap on the exponential reconnect backoff
                                  (default 300s; starts at 200 ms and doubles).
      --proxy <URL>               Route the QUIC connection through an outbound
                                  SOCKS5 proxy via UDP ASSOCIATE (RFC 1928 §4).
                                  Form: socks5://[user:pass@]host:port (alias:
                                  socks://). HTTP CONNECT is intentionally NOT
                                  supported — it cannot carry UDP/QUIC. The
                                  proxy must permit UDP ASSOCIATE; many
                                  corporate / hotel HTTP proxies do not.
  -v, --verbose                   enable verbose logging
      --debug                     enable debug logging
  -h, --help                      Print help

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:

rusnel server --tls-self-signed
# server cert fingerprint: sha256:abcd...
rusnel client --tls-fingerprint sha256:abcd... 1.2.3.4:8080 1337

For full mTLS, generate a CA + server + client cert (no openssl required):

scripts/gen-certs.sh ./pki 1.2.3.4
rusnel server --tls-ca   ./pki/ca.pem --tls-cert ./pki/server.pem --tls-key ./pki/server.key
rusnel client --tls-ca   ./pki/ca.pem --tls-cert ./pki/client.pem --tls-key ./pki/client.key \
              --tls-server-name 1.2.3.4 1.2.3.4:8080 1337

rusnel cert --help lists the underlying subcommands (ca, server, client, fingerprint) for finer control. Pre-configured binaries with credentials baked in at compile time are also supported via RUSNEL_EMBED_* env vars — see build.rs and the CHANGELOG for details.

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.

Throughput Latency

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

HTTP through tunnel

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:port routes 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 under Security & access control for the path that would unlock HTTP/SOCKS-CONNECT proxies.
  • RUST_LOG-style env filter for tracing-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 RemoteRequest server-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 client startup from ~3 RTT to ~1 RTT)
  • UDP hole-punching / NAT traversal mode: introduce a rusnel broker role 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 / --deny flags (and config-file equivalents) accepting wildcarded RemoteRequest patterns, 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-issuer daemon: client runs rusnel 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|kick subcommand 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.

Testing & CI

  • run ./benchmark/run.sh on a self-hosted runner per release tag and commit the result PNGs back, so perf regressions surface in PRs