Recently (I seem to start a lot of posts with “Recently”), I was on the road needed to access a server that was behind a firewall. There was no VPN and access was limited to a small set of IPs. I could however access another server in that set of IPs. That would let me bounce through for SSH access, but really I needed access from my laptop.

One simple thing to do in this situation is set up an SSH tunnel. I’ve covered inbound tunnels before, outbound ones are even simpler:

ssh -N user@gateway-server -L 8080:firewall-server:80

user@gateway-server is you SSH login on the server you can get to. -L says open a tunnel, 8080 being the local port your computer, firewall-server is the server behind the firewall, and 80 is the port on that server you want to connect to. -N says don’t start a shell, otherwise ssh would log you in to gateway-server.

The end results is that anything sent to port 8080 on you local computer is forwarded over SSH and then a connection is may out from gateway-server to port 80 on firewall-server.

Or more simply, once the tunnel is open, you open to http://localhost:8080, and boom, you find you self looking at the web server behind the firewall.

If you need to open multiple ports, you can use -L multiple times:

ssh -N user@gateway-server -L 2222:firewall-server:22 -L 8080:firewall-server:80 -L 4443:firewall-server:443

And if you aren’t running service locally on the official ports (i.e you don’t have SSH and a web server running), you can use those ports if you run ssh under sudo to get root privileges.

sudo -E ssh -N user@gateway-server -L 22:firewall-server:22 -L 80:firewall-server:80 -L 443:firewall-server:443

(sudo’s -E flag (covered here) will allow ssh to access SSH keys that you have loaded into ssh-agent).

With this setup you can just open http://localhost, https://localhost, and ssh user@localhost.

However, this still might not be enough, not all service behave nicely when proxied through random IPs and ports. This is especially true of web apps that hard code their cookie domain. The cookie won’t match “localhost”, and won’t be stored, preventing logins and other features that depend on cookies.

What we really want is something that can transparently use an SSH tunnel and send all traffic to an IP over it.

And such a thing exists! sshuttle creates a VPN over SSH. First install it:

brew install sshuttle

(Presuming you are on a Mac, Linux and Windows version exist as well).

Then all you need do is run:

sshuttle -r user@gateway-server firewall-ip/32

Where firewall-ip is the ip address of the server behind the firewall. The /32 routes only that one IP, but you can route whole networks:

sshuttle -r user@gateway-server firewall-network/24

One you start sshuttle you will be prompted for local sudo password, this is so local routing can be installed. Then the SSH connection is set up, prompting for a password if needed. Once this is in place, all traffic for the firewall IP (or network) will be transparently routed over the SSH link and out through gateway-server. SSH and web connections will just work and, since the DNS is valid, proxy issues will not arise.

Because this is a userland VPN running over SSH, it’s slow. But, slow is better than locked out. You win!