Julius Kaiser
Leipzig, Germany
jkdata@mailbox.org


Title: Reverse SSH tunnels...
Status: Informational

Published: 30 Nov 2023
Updated: 08 Jan 2024

Reverse SSH tunnels for remote port forwarding

Here’s how to leverage reverse SSH tunnels to access hosts behind a firewall and/ or (CG)NAT over a public network.

   external host  |          internal network
                  |
  example.com     |	        	 192.0.2.50
  ------------    |   ------------       --------------
  |ssh-server|    |   |ssh-client|       |winrdp:3389 |
  |(ext. IP) |<--ssh--|(int. IP) |--lan--|(int. IP)   |
  ------------    |   ------------       --------------
       A          |         B                   C
    "Broker"            "Jumphost"           "Target"

Configure SSH Servers

To allow (remote) port forwarding, set these two options in /etc/ssh/sshd_config both on Broker (A) and Jumphost (B):

GatewayPorts yes # Allow remote hosts to connect to forwarded ports
AllowTcpForwarding yes # Allow TCP forwarding

Gateway ports can be omitted on Jumphost (B) if all you need is to reach services that are running directly on it.

Establish Tunnel

The ssh-client command is executed on host B (“Jumphost”):

jumphost$ ssh user@example.com -N -R 5000:192.0.2.50:3389
^^^^^^^^           ^^^^^^^^^^^            ^^^^^^^^^^
    B                   A                      C
  • ssh - Invoke ssh-client
  • user@example.com - Connect to Broker (A)
  • -N - Do not start a shell
  • -R - Provide a reverse tunnel
  • 5000:192.0.2.50:3389 forward all traffic that arrives on Broker (A) port 5000 to Target (C) port 3389.

Access Published Service

Use any independent host that is able to reach Broker (A) and access a service (Windows RDP in this example) running on Target (C):

mstsc /v example.com:5000

If a service (e.g. a local web server) that you want to make reachable is running directly on Jumphost (B), just use localhost instead of Target’s (C) IP address:

jumphost$ ssh -N -R 7000:localhost:80 user@example.com

Run tunnels in the background

The example above runs the SSH command in the foreground of your TTY, blocking your session on the jumphost. To continue working on the jumphost, e.g. to spawn more tunnels, consider using the ControlMaster & ControlPath functionality of SSH:

  • -f - Requests ssh to go to background
  • -M - Places the ssh client into ‘‘master’’ mode for connection sharing
  • -S - Specifies the location of a control socket for connection sharing

See man ssh_config paragraphs ControlMaster & ControlPath for more information.

Example:

jumphost$ ssh user@example.com -f -M -S ~/session1 -N -R 5000:192.0.2.51:3389
jumphost$ ssh user@example.com -f -M -S ~/session2 -N -R 5000:192.0.2.52:3389

To close the tunnels:

 jumphost$ ssh -S ~/session1 -O exit example.com
 jumphost$ ssh -S ~/session2 -O exit example.com

Instead of using session1 etc. as filenames for the sockets, I like to use the hostnames of my targets.

Use With Caution

Internal services are usually not ment to be publicly reachable.

Firewalling Broker (A) to only allow connections to forwarded ports (:5000) from trusted sources substantially minimizes risks here.

Also may consider to use e.g. a shell script that removes tunnels nightly or something like this..