Routing It Your Way Part 1: ssh + socat

Every now and then I get questions that make me think in new directions and the results are often useful far beyond the original intent. Here’s an example: For many years I’ve been using ssh reverse tcp port forwarding to make servers available on the Internet that are behind a NAT, for example in a home network without a public IP address. Here’s my original post from back in 2014 and here’s a post about a talk I gave on the topic in 2019. But what if one can’t initiate an ssh tunnel from the server behind the NAT? Well, I have a solution for that as well now: Combining ssh and socat!

Socat can forward Unix socket traffic, which includes UDP and TCP from one place to another, either locally, or over the network. The ‘network’ is the important part here, as it allows to forward traffic from one server to another server. The picture below shows how that comes in handy:

To get to the server behind the NAT, my ‘traditional’ approach is to establish an ssh connection from the server without a public IP to that gateway server, and use tcp reverse port forwarding to make its services available via that gateway. Typically, that will be port 80 and 443. The DNS entry for the server then points to the public IP address of the gateway server on the Internet. Done.

In case it’s not possible to use ssh reverse port forwarding on the server, another intermediary in the NATed network is necessary. In my case, that’s a Raspberry Pi that establishes a ssh tcp reverse port forwarding tunnel to the gateway server on the Internet in one direction. To get to the real server, socat port forwarding can then be used on the Raspberry Pi in the other direction.

On the Raspberry Pi, ssh and socat are started as follows:

# Commands to be run on the Raspberry Pi
# Note 1: The ssh public key needs to be known to the 
# gateway server.
# Note 2: On the gateway server, ssh runs on port 2222

# ssh tcp forwarding from the gateway server
sudo nohup autossh -M 0 -f \
     -o ConnectTimeout=10 \
     -o ServerAliveInterval=60 \
     -o ServerAliveCountMax=2 \
     -N \
     -o ExitOnForwardFailure=yes \
     -R 443:localhost:443 \
     -R 80:localhost:80 \ 
     -p 2222 \
     root@$ &

# And here's the two socat commands to forward traffic of 
# port 80+443 to/from the local server (

sudo nohup socat TCP-LISTEN:443,fork TCP: &
sudo nohup socat TCP-LISTEN:80,fork TCP: &

Note: Since ‘privileged’ ports are used, the commands need to be executed as root.

Admittedly, having two gateway servers been the client and the server is far from ideal. But if you are restricted by a NAT without a public IP address, and a server on which you can’t use ssh, this turns into an elegant solution, at least in my book. And on top, this exercise sparked two other ideas that lead to very interesting solutions for two other problems. More about that in follow up posts.