SSH Port Forwarding For Remote Access
An ssh port forwarding connection allows us to access machines that are not on the internet publicly (without a static public IP address) through another machine that is. If you've ever used Ngrok to connect to your local machine from the internet, this serves a similar purpose. I found this solution as an alternative to Ngrok.
Using the -R
flag in ssh, we can accomplish this by forwarding all the traffic on a particular port on the remote machine to our current machine.
The Basic Command
ssh -R <Remote Port>:localhost:<Local port> -N <remote username>@<remote ip address>
NOTE: For the above command to work, you must change a setting in your sshd_config
file to allow GatewayPorts
. We can do this by opening the file /etc/ssh/sshd_config
and changing the line that contains GatewayPorts
to say: GatewayPorts yes
.
Here's an example of that command that I run:
ssh -R 7247:localhost:22 -N suchi@suchicodes.com
The above command forwards all the traffic on port 7427
on my remote machine (my VPS) to port 22
on my current machine. Once this has started we can run something like:
ssh suchi@suchicodes.com -p 7427
This command will now connect to the remote machine (VPS), which will forward that request to the local machine you ran the command on.
The -R
flag specifies a remote port forward. The -N
specifies to not execute a remote command (useful for forwarding ports.)
Setting Up A Script
Everything is better when it's a script! We can set up a script that sets up the ssh connection, and if it happens to die for some reason, then it launches it again.
Here's the script:
#!/bin/sh
# Author: Suchith Sridhar
# Website: https://suchicodes.com
# Date: 20th Dec 2022
# Note: to run the script, you'll have to add your ssh key to
# the list of authorized hosts, else you'll have to type the
# password every time the ssh command is run.
# seconds to wait before checking again
sleep_interval=10
# log file to log ssh command output
log_file=/home/suchi/ssh-suchicodes.log
# can be IP or domain name
remote_ip=0.0.0.0
# remote username for login
remote_user=suchi
# remote port to be used for ssh forwarding
# Note that you will have to allow tcp/ip traffic on this port
remote_port=7327
# local port to be forwarded to
local_port=22
printed=0
while true
do
exist=`ps aux | grep $remote_user@$remote_ip | grep $local_port`
if test -n "$exist"; then
if test $printed -eq 0; then
echo "SSH connection active since: $(date)"
fi
printed=1
else
printed=0
echo "SSH connection not active $(date)"
echo "SCRIPT: Starting new connection on $(date)" >> $log_file
ssh -R $remote_port:localhost:$local_port -N $remote_user@$remote_ip -v >> $log_file 2>&1
fi
sleep $sleep_interval
done
This script manages the ssh connection and ensures it's always active. You need to update the variables present before the loop, and each has a comment describing its purpose.
A Script For The Script?!
We want to launch the above script in the background so it can do its thing. So, I created a script that does exactly that.
#!/bin/bash
# Author: Suchith Sridhar
# Website: https://suchicodes.com
# Date: 20th Dec 2022
# This is the path of the previous script
# I recommend putting in ~/.local/bin
# I recommend a name like: open-from-<domain_name>.sh
PATH_OF_SCRIPT=/home/suchi/.local/bin/open-from-suchicodes.sh
# kill all existing script
pkill -f `echo $PATH_OF_SCRIPT | rev | cut -d "/" | rev`
echo "Launching script, log file: <name of current logfile> and <name of ssh logfile>"
bash $PATH_OF_SCRIPT >> <logfile> 2>&1 &
echo "Launch complete."
Assuming we called this script reverse-ssh-script.sh
, we can just run this single script once, and it should handle everything else.
I've also added this line into my crontab file using crontab -e
: @reboot /home/suchi/.local/bin/reverse-ssh-script.sh
so that this script is run as soon as the system starts up.