Skip to content

Latest commit

 

History

1,281 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

goshuttle

Transparent proxy over SSH — route some or all of your machine's TCP and DNS traffic through any host you can ssh to. No VPN server, no root on the remote side, no client configuration in your apps.

goshuttle is a Go rewrite of sshuttle, built for long-running, high-throughput tunnels:

  • Fast. Compiled Go with a zero-allocation mux hot path and per-channel flow control — designed to be significantly higher performance than the Python-based sshuttle.
  • Single static binary. No Python or other runtime needed on either end. The client cross-compiles the server half and uploads it over SSH automatically.
  • Correct TCP. Streams application bytes over SSH instead of nesting TCP packets inside TCP, so congestion control behaves the way it should.

Requirements

  • Client: macOS or Linux, with sudo access (firewall rules are managed via pf on macOS and iptables/nftables on Linux), and a Go toolchain if you build from source or use the auto-upload mode.
  • Server: any Linux, macOS, or BSD host you can SSH into. An unprivileged account is fine — the server side never needs root.

Install

git clone https://github.com/jdtx0/goshuttle
cd goshuttle
make build          # builds ./bin/goshuttle
make install-system # optional: installs to /usr/local/bin (sudo)

Quick start

Route a subnet through a bastion host:

goshuttle --remote user@bastion.example.com 10.0.0.0/8

Route everything, including DNS:

goshuttle --remote user@bastion.example.com --dns 0.0.0.0/0

That's it. On first run goshuttle detects the remote OS/arch, builds a matching server binary, and uploads it to ~/goshuttle on the remote host (it asks first; pass --yes to skip the prompt). Subsequent runs reuse the uploaded binary after a hash check.

Common options

Flag Description
--remote user@host SSH target (required)
--dns intercept DNS and resolve it remotely
--exclude SUBNET don't route this subnet; repeatable
--subnet-file FILE read subnets from a file
--remote-mode MODE homedir (default), go (ephemeral /tmp upload, self-deletes), or preinstalled
--remote-path PATH path to a preinstalled remote binary
--ssh-cmd CMD alternate ssh command

Subnets accept optional port ranges (10.0.0.0/8:443, 0/0:80-443) and - prefixes for exclusion. See docs-go/usage.md for the full flag reference and docs-go/platforms.md for platform details.

How it works

  1. Firewall rules (pf on macOS, iptables/nftables on Linux) redirect matching outbound TCP/DNS traffic into a local loopback listener.
  2. The client recovers each connection's original destination, then carries the byte stream through a framed multiplexer over SSH stdio.
  3. The server half — running as your normal SSH user — dials the real destination from the remote side and bridges the bytes back.

Because only application byte streams cross the SSH link (never raw packets), there is no TCP-over-TCP meltdown, and a slow connection backpressures its own sender without affecting other flows.

Platform support

Platform Client Server
macOS ✅ supported (pf) ✅
Linux ✅ supported (nat/nft/tproxy) ✅
FreeBSD / OpenBSD / NetBSD — ✅

The Linux client is covered by unit tests, privileged firewall integration tests, and the Docker E2E suite, but has not yet seen extended real-world use — please report issues.

Only one goshuttle instance can manage pf on a machine at a time.

Security notes

  • The local listeners bind to loopback only; the remote server runs over SSH stdio with no listening ports. SSH provides all authentication and encryption.

  • The remote server runs as your unprivileged SSH user and never invokes sudo. On shared bastions, consider restricting the key in authorized_keys:

    command="goshuttle server",no-pty,no-port-forwarding,no-X11-forwarding user-key...
    
  • Firewall redirection applies to all matching traffic on the client machine, regardless of which user generated it. See the usage docs if you need per-UID scoping.

  • Without --dns, hostname lookups still go to your normal resolver and are visible to your local network.

Development

make check        # vet + race-enabled unit tests
make bench        # mux throughput benchmark
make e2e          # full client → bastion → destination topology in Docker
make linux-fw-test  # privileged Linux firewall backend tests in Docker

The E2E suite blocks direct egress, tunnels a Linux client through a bastion container, and verifies concurrent SSH sessions, hash-checked 50 MiB transfers, and parallel iperf3 throughput.

License

LGPL-2.1, inherited from sshuttle, of which this project is a derivative.

About

Transparent proxy over SSH — a poor man's VPN in a single Go binary. Routes TCP and DNS through any host you can ssh to; no remote root or VPN server needed. macOS client (Linux experimental), Linux/macOS/BSD servers.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages