2 minute read Updated

A SOCKS5 proxy over SSH to get around censorship

This setup is for when your device can only reach the local network, a machine on that network can reach a filtered internet, and you have a server somewhere outside the filter. One SSH tunnel chains them together, and your device ends up with a normal SOCKS5 proxy.

Client phone or PC, intranet only
Middle filtered internet
Server open internet

What you need

  • Client: the device you want to browse from. It only reaches the intranet.
  • Middle: a Linux machine the client can reach, with internet access that is filtered.
  • Server: a machine outside the filter that you can SSH into from the middle machine. A small VPS is enough.
  • SSH on the middle machine and the server.
  • Dante on the server, only if you use option B.
  • Proxifier, or any app that can use a SOCKS5 proxy, on the client.

Tip

Anywhere below you can use a domain name instead of an IP address, and any free port instead of 1080.

Option A: SSH alone

SSH can act as a SOCKS5 proxy by itself. Run this on the middle machine:

ssh -C -N -D 0.0.0.0:1080 user@server.example.com
  • -D 0.0.0.0:1080 opens a SOCKS5 proxy on port 1080 of the middle machine, on all its network interfaces, so the client can reach it.
  • -C compresses the traffic, and -N means “no shell, just the tunnel”.
  • Add -v while you test, to see each connection as it opens.

Every connection the client makes through the proxy now leaves from the server, outside the filter.

Option B: Dante on the server

If you’d rather run a real SOCKS server on the outside machine, for example to share it between several tunnels, install Dante there and keep it on localhost, so only the tunnel can reach it:

sudo apt install dante-server

A minimal /etc/danted.conf:

logoutput: syslog
internal: 127.0.0.1 port = 1080
external: eth0
socksmethod: none
clientmethod: none
user.privileged: root
user.unprivileged: nobody

client pass { from: 127.0.0.0/8 to: 0.0.0.0/0 }
socks pass { from: 127.0.0.0/8 to: 0.0.0.0/0 }

Replace eth0 with the server’s network interface (ip a lists them), then sudo systemctl restart danted.

Now forward a port from the middle machine to Dante:

ssh -N -L 0.0.0.0:1080:127.0.0.1:1080 user@server.example.com

The pattern is -L [listen IP]:[listen port]:[destination IP]:[destination port]. Here the middle machine listens on port 1080 and sends everything to port 1080 on the server’s own localhost, where Dante is waiting.

Point the client at the proxy

On the client, add a proxy in Proxifier:

  1. Profile → Proxy Servers → Add.
  2. Address: the middle machine’s intranet IP. Port: 1080. Protocol: SOCKS Version 5.
  3. Click Check to test it, then let Proxifier send all traffic through it.
  4. Under Profile → Name Resolution, resolve hostnames through the proxy. Otherwise DNS lookups still go through the filtered network.

On a phone, use any app or browser setting that supports SOCKS5 with the same address and port. In Firefox it’s Settings → Network Settings → Manual proxy, with Proxy DNS when using SOCKS v5 ticked.

Keep it running

  • Log in with an SSH key instead of a password, so the tunnel can start without you typing anything.
  • Add -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes so a dead connection is noticed quickly.
  • Wrap the command in autossh or a systemd service, so it comes back by itself after a drop.
  • Anyone who can reach the middle machine’s port 1080 can use your proxy. If the intranet isn’t yours, bind to one interface instead of 0.0.0.0, or block the port for everyone but your client with a firewall rule.