SSH Connection through a Reverse (Remote) SSH Tunnel

Solution 1:

What you're referring is "SSH REMOTE FORWARDING", and is properly explained in the "man ssh", regarding the "-R" option.

> man ssh
[...]
 -R [bind_address:]port:host:hostport
    Specifies that the given port on the remote (server) host is to 
    be forwarded to the given host and port on the local side.
    This works by allocating a socket to listen to port on the remote 
    side, and whenever a connection is made to this port, the
    connection is forwarded over the secure channel, and a connection is 
    made to host port hostport from the local machine.
    [...]

In your context, where:

  • a Linux box A (LINUX_BOX_A) inside a LAN behind a firewall.
  • a Linux server B (SERVER_B) with a fixed IP that is accessible from the internet

SSH remote forwarding can be used to reach LINUX_BOX_A from SERVER_B. The only condition is: LINUX_BOX_A MUST be able to connect via SSH to SERVER_B.

To achieve this goal you need:

  1. on LINUX_BOX_A:

LINUX_BOX_A:~ $ ssh -R 2222:localhost:22 user@SERVER_B

this will open an ssh connection from LINUX_BOX_A to SERVER_B that will be used for the remote, incoming, connection.

After above ssh connection is established, you can:

  1. on SERVER_B:

SERVER_B:~ $ ssh -p 2222 user@localhost

such ssh-connection, launched on SERVER_B, will be directed to the 2222 port listening on localhost that... is binded to the previous ssh connection. So this will be an "ssh connection within another ssh connection".

Some additional notes:

  • consider that if the first ssh-connection will timeout and/or fall down for whatever reason (including: killed by local firewall, due to inactivity), you'll be unable to remote-forward/remotely_connect;

  • as it's important to leave the first ssh connection active for really long time, you might find useful to launch such ssh within a "screen" session

A final note: Obviously, all of the above has some (potentially serious) security implications that are out of scope of this answer.

Solution 2:

I have drawn some sketches

The machine, where the ssh tunnel command is typed is called »your host«.

ssh tunnel starting from local


ssh tunnel starting from remote

Introduction

  1. local: -L Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side.

    ssh -L sourcePort:forwardToHost:onPort connectToHost means: connect with ssh to connectToHost, and forward all connection attempts to the local sourcePort to port onPort on the machine called forwardToHost, which can be reached from the connectToHost machine.

  2. remote: -R Specifies that the given port on the remote (server) host is to be forwarded to the given host and port on the local side.

    ssh -R sourcePort:forwardToHost:onPort connectToHost means: connect with ssh to connectToHost, and forward all connection attempts to the remote sourcePort to port onPort on the machine called forwardToHost, which can be reached from your local machine.

Additional options

  • -f tells ssh to background itself after it authenticates, so you don't have to sit around running something on the remote server for the tunnel to remain alive.
  • -N says that you want an SSH connection, but you don't actually want to run any remote commands. If all you're creating is a tunnel, then including this option saves resources.
  • -T disables pseudo-tty allocation, which is appropriate because you're not trying to create an interactive shell.

Your example

The third image represents your case. The computer behind the firewall, you called it A, is your host, because you have to execute the ssh command on this computer, to open the tunnel to the localhost of your host (see third image).

The remotehost (in the image) is your serverB (which is accessible from everywhere).

ssh -R 2022:localhost:22 user@serverB

All connection attempts to the green port 2022 are forwarded through the ssh tunnel to the pink port 22 on the your host’s localhost (the firewalled computer).

Now you can ssh from anywhere in the internet to the remotehost (you called serverB) on port 2022 (with the command: ssh -p 2022 user@serverB from any machine in the world) and will be connected to your firewalled computer. That means, you can control this computer.

Automatic reconnection

You can write a script startTunnelToHidden that keeps the tunnel alive and re-establishes the connection, every time it gets lost. It is a good idea to start this script inside a screen session and even better to start it with ssh-agent to be able to connect with a public key (because, otherwise how would you enter the password? It is possible, but better to use a public key).

To open the tunnel on the firewalled computer, enter:

screen ssh-agent bash ssh-add startTunnelToHidden 2022:localhost:22 user@serverB

Or put this in /etc/rc.local (so that the tunnel gets opened on boot of the firewalled computer):

(sleep 2m; su - userOnA -c 'screen -S tunnelToHidden -d -m startTunnelToHidden 2022:localhost:22 serverB') &

The content of the bash script startTunnelToHidden:

#!/bin/bash
# Opens and restarts a remote ssh tunnel (in case it gets disconnected).

MinReconnectTime=$((1*60))
MaxReconnectTime=$((30*60))
RTime="$MinReconnectTime"
TimeUnit="s"

count=0
while true; do
  ((count++))
  echo "Open tunnel (try number $count)"
  date

  ConnTime=$(date +%s)
  # Example: Open tunnel to remotehost, which forwards connections from the
  # remote’s localhost:10002 to the local port 22. »-N« means do not execute
  # any command (no command prompt), »-T« means no tty, and
  # »-o ServerAliveInterval=60« to keep the connection alive:
  #   ssh -N -T -o ServerAliveInterval=60 -R 10002:localhost:22 remotehost

  ssh -N -T -o ServerAliveInterval=60 -R $@
  DisconnTime=$(date +%s)

  SSHTime=$(($DisconnTime-$ConnTime))
  echo "SSHTime=${SSHTime}${TimeUnit}"
  echo "Excited Tunnel!"

  # it takes 63s for ssh to timeout, minreconntime is 60s, therefore
  # 2*minrectime
  if [ $SSHTime -gt $(($MinReconnectTime*2)) ]; then
    RTime=$MinReconnectTime
  else
    ((RTime*=2))
    if [ $RTime -gt $MaxReconnectTime ]; then
      RTime=$MaxReconnectTime
    fi
  fi                                                                                                                                                                      
  echo "Waiting $RTime$TimeUnit"
  sleep $RTime
done

Reattach the screen session with your tunnel with screen -r tunnelToHidden