SSH remote port forwarding without server support

You read about how to create an SSH tunnel and saw that you can do the same in reverse, forwarding a port from the server to your local machine. You tried it out on a server you have access to and discovered that the port you chose isn’t opening on the outside, even though the port is allowed through the firewall. What gives?

A visual representation of remote port forwarding

SSH Remote Port Forwarding flow diagram

You probably missed one small but important detail: it must be enabled in the SSH config. It isn’t enabled by default, probably for security reasons. To enable it, edit /etc/ssh/sshd_config and add this line:

GatewayPorts yes

After that, restart the SSH daemon.

More likely than not, though, you’re on a shared server and you don’t have permissions to edit the SSH config, much less restart the server. If that’s the case, you won’t be able to open a port to the outside directly using SSH like that. You’ll need something else. That something else has to be able to open a port to the outside and stream received data to your local computer.

socat can be that little something. The idea is to use socat to listen on a port to the outside and forward the data through your SSH connection. How do we get the data through the SSH connection?

It turns out that remote port forwarding through SSH actually does mostly work without enabling it in the SSH config; it just doesn’t expose the port to the outside. In other words, you can connect to the port from localhost (127.0.0.1). This is key, because I can think of no other simple way to achieve the goal without it.

The procedure is this: set up SSH remote port forwarding as usual, except with a different target port, then use socat to listen on the desired target port and have it forward the data to the remote SSH port you specified earlier.

To give a concrete example, let’s take my original use case for this and suppose you’re playing mahjong using Julian Bradfield’s excellent xmj program. The program consists of a server process and four client processes. In the relevant case, the server listens on a port (5000 by default) and the clients processes connect to that port to send and receive data. You want to play with friends over the Internet, but you’re behind a NAT router with no access to set up port forwarding on it. You do, however, have SSH access to a remote server, example.com, that allows incoming connections between ports 28000 and 28500. You can use the remote server as an SSH tunnel to perform remote port forwarding to your local computer.

xmj interface

xmj, the program in the example.

The first step is finding two ports on the remote server: one in the open range, and one other that does not have to be in the open range (although for courtesy, pick a port outside of the open range). Suppose you determine that ports 28005 and 6001 are open, where port 28005 is in the open range and is the desired target port.

Suppose that you have the mahjong server running on your local computer listening on port 5000. To set up your SSH tunnel, you would run this command:

ssh -R 5000:localhost:6001 example.com

This will forward any data sent to port 6001 locally on the remote server to your local port 5000. It will also give you a shell.

The mahjong programs use TCP connections, so in shell, use socat to set up a TCP socket listening on port 28005 and have socat forward the data to port 6001:

socat TCP-LISTEN:28005,fork TCP:127.0.0.1:6001

It will appear to hang, but rest assured that it’s doing what it’s supposed to. Your friends should now be able to connect to example.com:28005 and play.

You can actually shorten this to a single command because SSH accepts a command as the last parameter:

ssh -R 6001:localhost:5000 example.com 'socat TCP-LISTEN:28005,fork TCP:127.0.0.1:6001'

Again, it will appear to hang because of socat, but it should work as expected.

There you have it. Even without server support, you can set up remote port forwarding using SSH and socat.


For the record, I solved my original xmj problem simply by running the server process on the remote server instead; everything is much simpler that way.

Also, that hand ended with West winning on the 5 of characters discarded by South. I was waiting on a 3 of bamboo that evidently wouldn’t have been discarded.

West won with a 5C discard

So close to winning!

$ cat your_comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.