SSHUTTLE2(1)User StoriesSSHUTTLE2(1)

Why sshuttle2 exists

A hospital's Wi-Fi blocked everything except SSH. To get my work done, I first had to fix my internet connection. This is why I wrote sshuttle2, a Rust implementation of the idea behind sshuttle.

Allwin, October 2026

Source code: github.com/allwinpower/sshuttle2

Contents

  1. The network
  2. What got through
  3. sshuttle
  4. sshuttle2
  5. Results
  6. Links

1. The network

I was in a hospital for a while, with my Mac, my phone and work to do. The patient Wi-Fi runs through a Fortinet firewall that inspects HTTPS. The screenshots are from the phone (Brave on Android); the Mac got the same errors.

Brave on Android: claude.ai fails with 'An application is stopping Brave from safely connecting to this site', Fortinet, NET::ERR_CERT_AUTHORITY_INVALID.
Fig. 1: claude.ai [BLOCKED]
Brave on Android: chatgpt.com fails with the same Fortinet certificate error.
Fig. 2: chatgpt.com [BLOCKED]
Brave on Android: en.wikipedia.org fails with the same Fortinet certificate error.
Fig. 3: en.wikipedia.org [BLOCKED]

What the firewall blocked, on both devices:

The error is NET::ERR_CERT_AUTHORITY_INVALID. The firewall decrypts each HTTPS connection and signs it again with its own certificate, which my devices do not trust. Brave suggests disabling Fortinet or connecting to another network. A patient can do neither.

2. What got through

SSH on port 22 was not blocked, so I could log in to my own server from the Mac.

A network that allows SSH allows a tunnel. That is the idea behind sshuttle: a VPN over a plain SSH login, with nothing to install on the server.

3. sshuttle

sshuttle worked on the Mac, but slowly:

sshuttle relays every connection through one single-threaded Python process on the server, and all packets share one queue. It has served people well for years, but it was too slow for a working day.

4. sshuttle2

So I wrote sshuttle2 in Rust. Both ends are new code, and the idea is the same: the server needs nothing but an SSH login. On first run, the client uploads a small server binary (about 1.3 MB) over SSH and reuses it after that. It runs on macOS and Linux.

What is different:

Rust at both ends.
No Python in the data path.
One SSH session, one channel per connection.
Each channel has its own flow control, so a large download does not hold up other connections.
Self-sizing windows.
A channel's window grows while it limits throughput and shrinks when it only adds queueing delay.
TCP, DNS and UDP.
In tun mode, video calls, QUIC and games go through the tunnel too.
Clean exit.
Firewall rules and routes are removed on exit, also after a crash.

5. Results

Measured on the Mac, with the same Wi-Fi and the same server (2 vCPU, 4 GB) on the same day. Single samples. Repeated measurements on rented servers: benchmarks.

* No tunnel: blocked sites stay blocked. pf mode: TCP and DNS. tun mode: TCP, UDP and DNS. Download: one 100 MB file. Page wait: time until google.com/generate_204 sends its first byte. Busy: while that download runs.

I wrote sshuttle2 to get back to work. If you are behind a firewall that lets only SSH through, it may help you too. Bug reports and pull requests are welcome.