Easy SSH tunnelling for the Mac

Yes, I know I fit the cliche here, but I like to work outside my office in a coffee shop or another public location. It’s a good feeling to have some people around instead of seeing the same faces all day long – or even no faces when you’re a lonesome freelancer like me ;) However, there are two drawbacks I was never happy with: First almost all public WLAN hotspots are unencrypted, which is quite a security problem, and second some networks don’t allow the use of certain services (e.g. the network at my university doesn’t allow me to send emails via secure SMTP). Luckily the solution to this problem is quite simple: We need a local SOCKS5 proxy that routes all internet traffic through a secure SSH tunnel.

All you need is a working Mac OS X, my WiFiTunnel script (Installer or ZIP archive) and a SSH Server outside of your local network. No idea where to get such a server? Well, there are some public SSH servers out there, but almost all virtual server packages offer SSH, so just try to connect to a server that hosts one of your websites. All necessary  details are explained during the script installation, but you should read on if you want to get the whole story.

How to use WiFiTunnel?

Installing and using WiFiTunnel is pretty easy:

  1. Download the WiFiTunnel Installer Package
  2. Open the package to install WiFiTunnel
  3. Open a terminal (/Applications/Utilities/Terminal)
  4. Enter wifitunnel --on user@hostname to open the tunnel
  5. Enter wifitunnel --off to close the tunnel
  6. Read the man page (man wifitunnel) for more details.

If don’t want to use an installer you can also download the ZIP archive and follow the instructions in the readme.

What does WiFiTunnel?

OpenSSL comes with OSX, so there is no need to install anything to secure your connection. WiFiTunnel is nothing more but a bash script for convenience, which does the following tasks:

  1. Establishing a SSH tunnel to a server outside of the local network
  2. Configuring SSH to act as a SOCKS proxy
  3. Configuring the active system networks to use this proxy
  4. Sending keep alive requests while connected
  5. Disable the SOCKS proxy in the system network settings
  6. Shutting down the SSH tunnel

Steps 1 to 3 are executed after entering wifitunnel --on user@hostname in the terminal, step 4 runs as long as the connection is active and the steps 5 and 6 are executed after a wifitunnel --off.

What is done in what step?

Step 1 and 2 are done with one command:

ssh -f -N -D 127.0.0.1:$PORT $HOST

This establishes a SSH tunnel to $HOST (user@hostname) where -N prevents the execution of any commands through this connection. The -D switch tells SSH to act as a SOCKS5 proxy server on port $PORT (default is 1080) of the local host (which IP address is 127.0.0.1). Finally -f forces SSH into the background after the connection has been established.

Next we edit the network settings in step 3:

networksetup -setsocksfirewallproxy $SERVICE 127.0.0.1 $PORT off
networksetup -setsocksfirewallproxystate $SERVICE on

$Service is the name of the network interface we’re using, usually Wi-Fi or Ethernet. The first command sets the SOCKS proxy parameters for the interface and the second enables the proxy. It’s basically the same as configuring the network through the System Preferences.

Step 4 keeps the connection alive:

curl -s http://www.google.com > /dev/null

Some public wireless hotspots close the connection if there is not traffic on port 80, so I added a keep-alive option to WiFiTunnel that requests the Google startpage every 3 minutes. We don’t need the result so we send everything to /dev/null.

Disabling the SOCKS proxy  – step 5:

networksetup -setsocksfirewallproxystate $SERVICE off

And finally step 6:

kill $PID

This terminates the proxy server. $PID is the process id of the SSH call we made in step 1, you can see it when you enter ps ax | grep ssh in the terminal.

Final notes

This is my first bash script ever so I’m pretty sure there are some bugs in it, but I hope you find it useful anyway. I’m always happy about feedback, especially bug reports (and fixes ^^).