SSH reverse tunnel

Written by on .

This article demonstrates how to make a local port available on a remote server. Ideal for dynamic IP-addresses or devices not accessible from outside a local network.

Quick reverse tunnel

Further below is a more standardized method using a dedicated tunnel-user. But this following command should already work out of the box (if no root access to the remote server):

ssh -o 'ExitOnForwardFailure yes' -N -R 0.0.0.0:[remote-port]:127.0.0.1:[local-port] [remote-user]@[remote-host] -p [remote-ssh-listen-port]

Remote server configuration

It is good to stick to the following standardized setup. First add a new dummy-user that will only be used for authentication purposes (will have no home-directory).

useradd --no-create-home -s /bin/false tunnel

Add the following to /etc/ssh/sshd_config:

Port 22000 Match User tunnel PasswordAuthentication no AuthorizedKeysFile /etc/ssh/tunnel_keys GatewayPorts clientspecified AllowTcpForwarding yes PermitOpen 0.0.0.0:22001 0.0.0.0:22002 0.0.0.0:22003 0.0.0.0:22004 0.0.0.0:22005 0.0.0.0:22006 0.0.0.0:22007 0.0.0.0:22008 0.0.0.0:22009 127.0.0.1:22001 127.0.0.1:22002 127.0.0.1:22003 127.0.0.1:22004 127.0.0.1:22005 127.0.0.1:22006 127.0.0.1:22007 127.0.0.1:22008 127.0.0.1:22009 X11Forwarding no AllowAgentForwarding no ForceCommand /bin/false PermitTTY no

Ensure a file exists (which only root may write to), with on each line the public key of the user who is allowed to create a tunnel. That is, to allow a user, add the contents of the user's ~/.ssh/id_rsa.pub file to /etc/ssh/tunnel_keys:

ssh-rsa ... user@device-name

Open the tunnel-ports in the firewall:

firewall-cmd --add-port=22000-22009/tcp --permanent firewall-cmd --reload firewall-cmd --list-all

Note: Don't forget to forward these ports to the remote machine if it's behind a proxy (in TCP-mode).

Creating the reverse SSH-tunnel (actual usage)

ssh -o 'ExitOnForwardFailure yes' -N -R 127.0.0.1:22001:127.0.0.1:[local-port] tunnel@[remote-host] -p 22000

The above command will keep running until interrupted, an error occurred, or it is killed. To stop the command gracefully, use <CTRL+C>. This command does not terminate if a connection between the remote and local port was disconnected or stopped listening. That is, it will keep the tunnel intact, and survives a restart of a program listening on the local-port.

You may make the tunnel available on another machine, using the following command on that other machine:

ssh -o 'ExitOnForwardFailure yes' -N -L 127.0.0.1:[local-port]:127.0.0.1:22001 tunnel@[remote-host] -p 22000

These tunnels are only (privately) available for the given tunnel user and all in the allocated port range as per the configuration. But by default, anyone with shell-access to the server typically has access to the tunnels.

If you wish to make the tunnel public, use 0.0.0.0:22001 instead of 127.0.0.1:22001 in the remote address. This means that anyone who can access the remote machine port, has access to the endpoint of that tunnel.