Persistent Reverse SSH Tunnel with SSH Keys
Have you ever wanted to access a machine behind NAT? Lets say you have a machine that blocks all incoming traffic (DESTINATION
), and you would like to be able to access it from your home machine (SOURCE
). Since the destination machine allows for outgoing connections, we can use Reverse SSH tunneling and a simple script to keep this connection up from the destination to source. As long as this connection stays up, we will be able to access the destination machine behind NAT from our home machine.
The steps described here will create an unprivileged user named tunnel
on each machine. That user will then be used to create the tunnel and run a script via cron to ensure that it remains up.
Create a tunnel
user on DESTINATION
machine (machine behind NAT):
useradd tunnel
passwd tunnel # Set a strong password
su - tunnel # Become the user 'tunnel'
Now create a public/private key pair:
[tunnel@DESTINATION ~]$ ssh-keygen
Leave all parameters to default:
The file in which the key will be saved /home/tunnel/.ssh/id\_rsa
The file in which the public key will be saved /home/tunnel/.ssh/id\_rsa.pub
The pass-phrase will be empty
Now create a tunnel
user on SOURCE
(home machine) and save the public key for [tunnel@DESTINATION]
in the authorized_keys
file:
useradd tunnel
passwd tunnel # Set a strong password
su - tunnel
mkdir .ssh
Copy the content of id_rsa.pub
file from the DESTINATION
which we created above into the ~/.ssh/authorized_keys
file of the SOURCE
.
[tunnel@SOURCE ~]# vi .ssh/authorized_keys
Now paste in the public key for tunnel@DESTINATION
At this point you should be able to ssh from tunnel@DESTINATION
to tunnel@SOURCE
without using a password.
Now we will create a script on the DESTINATION
(call it persistent_reverse_ssh.sh
) which will be testing the connection and port forwarding.
createTunnel()
{
/usr/bin/ssh -f -N -L8822:DESTINATION:22 -R7000:localhost:22 tunnel@SOURCE
if [[ $? -eq 0 ]]
then
echo "Tunnel to 192.168.248.201 created successfully"
else
echo "An error occurred creating a tunnel to 192.168.248.201 RC was $?"
fi
# Run the 'ls' command remotely. If it returns non-zero, then create a new connection
/usr/bin/ssh -p 8822 localhost ls
if [[ $? -ne 0 ]]
then
echo Creating new tunnel connection
createTunnel
fi
Save the script above and make it executable:
chmod 755 ~/persistent_reverse_ssh.sh
This script does two things. First whenever the user on DESTINATION
machine tries to ssh
to localhost
on port 8822, this script will route that connection to port 22 on SOURCE
machine. Second, every time the user on SOURCE
machine tries to ssh to port 7000 of itself, script will route that connection to port 22 of the DESTINATION
machine.
This script will attempt to ssh
to localhost
port 8822
and run the ls
command. If that fails, it will attempt to create a ssh
tunnel. The shh
command will create a tunnel for local port 8822
to port 22
on SOURCE
which the script uses to execute the ls
command remotely and test the connection.
Now to make this connection persistent, we need to make sure the connection is continiously checked. To do this we will add the persistent_reverse_ssh.sh
to the user tunnel
’s crontab on DESTINATION
machine.
Edit tunnel
’s crontab
:
[tunnel@DESTINATION ~]# crontab -e
Make the script run every minute by adding the line below to your crontab
:
1 * * * * /home/tunnel/persistent_reverse_ssh.sh
Save the file and exit.
For more information on how crontab works, click here.
Now the script will run every minute and check the connection, if the connection is broken, it will reconnect.