Why reverse ssh?
Well, cause you are screwed! The remote device that you want to access sits behind a firewall that you cannot control! But, no worries, reverse ssh comes to the rescue!
So, what reverse ssh does is the following:
- The remote device is, first, ssh-ing (yes that a verb!), to your local device:
ssh username@localdevice
assuming of course, your local device either does not have a firewall (well done!) OR allows ssh connections!
- and then, it is opening a magical “tunnel” back to itself:
-R 50000:localhost:22
That means, you, sitting at the local device, can now access that magical tunnel, and in turn access the remote device behind the firewall by ssh-ing (verb!) at the localhost at port 50000. This port can be anything, assuming it is not taken and it is > 1024 (reserved ones).
ssh remote_username@localhost -p 50000
Awesome right?? So, to sum up, all you need to do is the following two steps:
- At the remote device sitting behind a firewall:
ssh username@localdevice -R 50000:localhost:22
- Assuming the above succeeded, at your local device:
ssh remote_username@localhost -p 50000
That’s it! That’s the high-level idea!
What if multiple devices want to access the single remote device?
Well you have two options. You can go crazy and repeat the above process multiple times, using different ports, for each of the local device that want to access the remote device…
OR
you can just add an intermediate server, the “middle man“, forwarding each individual request to the remote device:
The high-level idea is the following:
- You remote device creates a tunnel to the “middle man”:
ssh user_mm@server_mm -R 50000:localhost:22
- Any other 3rd party device that wants to access the the remote device that is behind the firewall needs to
First ssh to the “middle man”:ssh user_mm@server_mm
And from the “middle man” the ssh into the remote device:ssh remote_username@localhost -p 50000
That’s it! This way the remote device doesn’t have to open tunnels witch each of the 3rd party devices that wants to access it!
What if the reverse ssh, disconnects, crashes, becomes unavailable for other reasons?
Normally you would be screwed again! But luckily AutoSSH is available for you!
AutoSSH is basically a tool to monitor and restart SSH sessions. It is checking the health of the connection and restarts SSH session if necessary. It is useful for non-reliable internet connections such as GSM or in cases when the connection parameters are changing from time to time (for example ISP is assigning new IP to the server from). We will be using it to maintain a reliable tunnel between the remote device and the “middle man” server.
Putting everything together
Here we will present all steps ton configure the reverse ssh, using a middle man and setting up autossh on the remote device.
Some definitions first:
- Remote device: The device sitting behind the firewall and the one we want to access.
- Local device: The local device that wants to access the remote one.
- “Middle Man” Server: The device that the remote device opens the tunnel to. Any 3rd party local device connects to the remote device via the “middle man”.
Prerequisites
- Remote device: Has ssh client and server. No open ports are needed.
- Local device: Has ssh client. No open ports are needed.
- “Middle Man” Server: Has ssh client and server, has SSH port open.
Step 1: Create a new user – “Middle Man” Server
This step is optional! You may use any existing user in the server.
Lets first create a new user in the “middle man” server. This will be a restricted user, only serving the needs of forwarding requests from the 3rd party local devices to the remote device.
In the “Middle Man” Server run:
# Create user sudo
useradd -m -s $(which bash) user_mm
# Add a password to the user
sudo passwd user_mm
Step 2: Add a AutoSSH user – Remote Device
For an extra security layer we will create a new user in the remote device that only runs the autossh
.
sudo useradd -m -s /bin/false autossh_user
Note as the user has a disabled shell and no password, which will make it impossible to use the user for access to the system.
Step 3: Setup SSH keys – Remote Device
Now we are going to setup SSH keys in the remote device, such that the device can ssh to the “middle man” server without the need of a password.
But, first, we have to impersonate the autossh_user
:
sudo su -s /bin/bash autossh_user
Now as a autossh_user
run:
ssh-keygen
Create the: ~/.ssh/id_rsa
without a passphrase.
Copy the SSH key to the “middle man” server:
ssh-copy-id -p 22 -i ~/.ssh/id_rsa user_mm@server_mm
This assumes the server is has its ssh-server on port 22. Change accordingly.
If you didn’t get any errors, then you can check if the installation of the key worked by ssh-ing to the server:
ssh -p 22 user_mm@server_mm
and you should be able to login in to the “middle man” server without password prompt.
Step 4: Configure AutoSSH – Remote Device
First install AutoSSH. Most distros just have this package in their repository. For debian based it is just:
sudo apt install autossh
Once you have installed the autossh, it is just the matter of using it. We can test quickly as following:
su -s /bin/sh autossh_user -c \
'autossh -M 0 -q -f -N -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 50000:localhost:22 user_mm@server_mm -p 22'
This is basically it! The arguments are explained bellow:
-M 0
: (autossh) – Turns the autossh monitoring function off, and autossh will only restart ssh upon ssh’s exit. We will use theServerAliveInterval
andServerAliveCountMax
to have the SSH client exit if it finds itself no longer connected to the server. In many ways this may be a better solution than the monitoring port.-q
: (ssh) – Quiet mode. Causes most warning and diagnostic messages to be suppressed.-f
: (autossh) – Causes autossh to drop to the background before running ssh.-N
: (ssh) – Do not execute a remote command. This is useful for just forwarding ports.ServerAliveInterval
: (ssh) – The number of seconds that the client will wait before sending a null packet to the server (to keep the connection alive)ServerAliveCountMax
: (ssh) – Sets a timeout interval in seconds after which if no data has been received from the server, ssh(1) will send a message through the encrypted channel to request a response from the server.-R
: (ssh) –local_socket:host:hostport
The reverse ssh argument
This is it! As long as autossh
runs the ssh is monitored!
To check if everything works, ssh to the “middle man” server and run from there:
ssh remote_device_username@localhost -p 50000
And you should be able to ssh to the remote device via the “middle man” server! The remote_device_username
can be any valid user present in the remote device.
Step 5: Start autossh during boot – Remote Device
We are going to configure a systemd
system such that autossh
starts automatically during boot.
sudo nano /etc/systemd/system/autossh-tunnel.service
and add the following:
[Unit]
Description=AutoSSH tunnel
After=network.target
[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/bin/su -s /bin/sh autossh_user -c 'autossh -M 0 -q -f -N -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 50000:localhost:22 user_mm@server_mm -p 22'
[Install]
WantedBy=multi-user.target
Once we have the service file created, we should let systemd
know that we changed something, we can start the service and we can enable it to run during the boot time:
sudo systemctl daemon-reload && \
sudo systemctl start autossh-tunnel.service && \
sudo systemctl enable autossh-tunnel.service
And you are done! The autossh
program will start automatically on every boot!
Step 6: Configure GatewayPorts – “Middle man” Server
By default, OpenSSH only allows connecting to remote forwarded ports from the server host. However, the GatewayPorts
option in the server configuration file sshd_config can be used to control this. Edit your sshd_config
file to allow forwarded ports from outside the server:
sudo nano /etc/ssh/sshd_config
and make sure the following holds:
GatewayPorts yes
Once edited restart the sshd
service:
sudo systemctl restart sshd
and you are done!
To test it, from any other 3rd party device, in order to access the remote device via the “middle man” server just run:
ssh remote_device_username@server_mm -p 50000
To break down the command:
remote_device_username
: Is some valid user in the remote device, the one behind the firewallserver_mm
: Is the IP or hostname of the “middle man” server.-p 50000
: Is the local port of the “middle man” server. This was defined in step 4.