A tiny proxy server for the home network

Tired of always having to type that ssh -D ... command and change your browser proxy settings to let your connection exit from somewhere else in the world?

Or, even better, do you want several devices on your LAN to be able to just send their stuff over the tunnel and beat some stupid geo-fencing even if they can’t ssh themselves?

If you have a spare Raspberry Pi (or whatever other linux box), here is a simple way to setup a tiny proxy server for your LAN :)

the aim

As boring as it may sound, I want to be able to watch RaiPlay, the free streaming service of the italian state TV (RAI). They cover a lot of interesting sport events but sadly they make them unavailable to connections coming from outside of Italy.

This is where having ssh access to a machine in Italy is really handy.

the plan

The plan is easy:

  1. Set up a permanent ssh tunnel using autossh, so that it reconnects upon failure.
  2. Set up a lighttpd web server to publish a proxy.pac file with some proxy rules to use with mobile devices (e.g. route all traffic to *.it domains through the tunnel).

dependencies

First let’s install what we need:

sudo apt update
sudo apt install autossh lighttpd

Then I assume you have an ~/.ssh/config entry for your server myserver, something that looks like

Host myserver
	HostName stocazzo.it
	User myuser

I also assume that you have already setup an ssh key for passwordless acces with

ssh-copy-id -i ~/.ssh/id_ed25519.pub myserver

Now let’s go!

autossh

Let’s setup a systemd service for our permanent tunnel, in this way autossh will be automatically started at boot.

First we create the service file

sudo vim.tiny /etc/systemd/system/autossh-myserver-tunnel.service

and fill it with

[Unit]
Description=AutoSSH tunnel service to myserver
After=network.target

[Service]
User=XYZ
Group=XYZ
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -g -D 6666 -T -N myserver

[Install]
WantedBy=multi-user.target

making sure to replace XYZ with your user name on your RPI and myserver with whatever your server is named in your ~/.ssh/config.

Of course you can always man ssh to know what all those options do, but a little explanation doesn’t hurt:

  • -D 6666 This is where the magic happens: any incoming connection on port 6666 is forwarded to the other side of the tunnel.
  • -T -N These two prevent the opening of a terminal and the execution of any remote command, since we just want to use ssh for port forwarding.
  • -g This allows remote connections coming from, e.g., other hosts in your LAN to use the tunnel.

Now you can start the service and check its state with the following

sudo systemctl start autossh-myserver-tunnel
sudo systemctl status autossh-myserver-tunnel

If there is any issue you can just stop it and try again. If everything is fine, you can enable the service to be run at startup with enable:

sudo systemctl enable autossh-myserver-tunnel

Now you can test your new proxy from your browser: just go in the proxy settings (or use something like FoxyProxy on Firefox) and set your home server ip, port 6666 and SOCKS5 as proxy type. Cheers :)

proxy.pac

The proxy.pac file can be used by devices that support Proxy Auto-Configuration. It contains a javascript function that is inquired with every browser request and it determines if and what proxy should be used for it based on the url. More info here.

This file should be made available to devices on the LAN using an http server, such as lighttpd.

The lighttpd server should be already enabled and running as a systemd service. Let’s just check it with

sudo systemctl status lighttpd

Now let’s assume your home server where this setup is taking place is reachable on your lan under the address raspberry.local (or whatever ip address). Then let’s create and open the proxy.pac file

sudo vim.tiny /var/www/html/proxy.pac

and let’s fill it with something like the following self-explanatory javascript code

function FindProxyForURL(url, host)
{ 
     if (dnsDomainIs(host, ".it")) return "SOCKS raspberry.local:6666; DIRECT";
     else return "DIRECT";
}

This will forward all requests with *.it address through our tunnel at raspberry.local:6666, unless it is somehow unavailable, in which case it will default to a DIRECT connection. All other addresses will also use a DIRECT connection.

More info on all the available functions and their syntax here.

proxy  ssh  rpi