Today we will look at port forwarding through SSH 🔐: how to use local and reverse tunnels to organize access to local and remote services.
🖐️Hey!
Subscribe to our Telegram channel @r4ven_me📱, so you don’t miss new posts on the website 😉. If you have questions or just want to chat about the topic, feel free to join the Raven chat at @r4ven_me_chat🧐.

Local Port Forwarding
For example, we need to connect from a local machine to a database💽 that is available only on the localhost interface of a remote server🖥.
You can open an SSH tunnel with this command:
ssh -q -f -N -L 127.0.0.1:5432:localhost:5432 ivan@test.r4ven.meWhere:
-q- quiet mode (output only errors);-f- start SSH in the background;-N- do not execute commands (tunnel only);-L- local port forwarding.
First comes address:port of the local host, then address:port of the remote one.
This command will open the tunnel in the background and keep it running permanently.
Now you can connect to the database at 127 0.0.1:5432
If this is your goal, the task is solved👌.
But what if you need to open the tunnel only for the duration of some operations? For example, when configuring automation using shell scripts📝. Obviously, in this case, after the work is finished, the tunnel must be closed🔒.
Unfortunately, SSH has no built-in functionality for providing the PID of the background process😢.
Therefore, you can close the tunnel in a crude way, through pkill 🎯, passing the full tunnel opening command as an argument😒.
Example:
pkill -f "ssh -q -f -N -L 127.0.0.1:5432:localhost:5432 ivan@test.r4ven.me"
# or
pgrep -f "ssh -q -f -N -L 127.0.0.1:5432:localhost:5432 ivan@test.r4ven.me" | xargs killThis method works, but it is not exactly elegant. And it is unsafe: if you make a mistake in the argument, you can accidentally kill another running process🤷♂
The search for an alternative solution took quite some time⏳
And here is an example:
ssh -q -f -L 127.0.0.1:5432:localhost:5432 test.r4ven.me sleep 60The point is that we open the tunnel for 60 seconds. During this time, you need to start the command you need🐧.
Even if your application command takes more than a minute to run, the connection will not be interrupted UNTIL it completes😌. In other words, the tunnel will close correctly and automatically👍.
Below is the command variant I used in one of my scripts:
ssh -q -f -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ExitOnForwardFailure=yes -L 127.0.0.1:5432:localhost:5432 test.r4ven.me -p 2222 -l ivan -i ~/.ssh/id_ed25519_test sleep 60Additional options:
StrictHostKeyChecking=no- disables host key checking (⚠️automation at the expense of security);UserKnownHostsFile=/dev/null- do not save the host key to~/.ssh/known_hosts;ExitOnForwardFailure=yes- exit if port forwarding fails.
Maybe it will be useful to someone for their shell 🐚 scripts🙃.
Reverse Port Forwarding
Now let’s talk about reverse tunnels🌐.
For example, you need to direct data from port 4443 of a remote machine to port 5001 of a local one:
ssh -q -f -N -R 127.0.0.1:4443:localhost:5001 ivan@test.r4ven.meLet me remind you:
-q- quiet mode (only errors);-f- start SSH in the background;-N- do not execute commands (tunnel only);-R- reverse forwarding (from a remote port to a local one).
First comes address:port of the remote host, then address:port of the local one.
If needed, you can close the connection with this crude command:
pkill -ef 'ssh -q -f -N -R 127.0.0.1:4443:localhost:5001 ivan@test.r4ven.me'As you noticed, we forwarded a port on the remote machine that waits for connections on the local interface. To forward a non-localhost interface, specify it explicitly. Or forward all interfaces:
ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.meMost likely, if you run the following on the remote machine:
ss -tln | grep 4443You will see:
Local Address:Port
127.0.0.1:4443The remote port still listens on the local interface🤔

The reason is secure shell, which would not be secure if it did not have secure default parameters😎.
For security reasons, reverse port forwarding on a non-local interface is disabled. The GatewayPorts parameter in the SSH config is responsible for this🔒.
To allow reverse forwarding, open the sshd config:
nvim /etc/ssh/sshd_configAnd set the GatewayPorts parameter to yes.
❗️ Caution
⚠️ Remember that this is a potential hole in your system!
After that, restart the sshd daemon:
sshd -t
systemctl restart sshd💡 Tip
☝️If you use a firewall, do not forget to add an allow rule. Example for ufw:
ufw allow 4443Reopen the tunnel:
pkill -ef 'ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.me'
ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.meAnd check the port on the remote machine again:
ss -tln | grep 4443Everything is as it should be:
Local Address:Port
0.0.0.0:4443Now, when connecting from any client, you will be forwarded to your local system:
nc -zv test.r4ven.me 4443
curl test.r4ven.me:4443⚠️ Warning
❗️Do not forget to close the connection after all operations on the local machine:
pkill -ef 'ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.me'Have a good day!👋
👨💻And…
Don’t forget about our Telegram channel 📱 and chat
Or maybe you want to become a co-author? Then click here🔗
💬 All the best ✌️
That should be it. If not, check the logs 🙂


