Reverse SSH tunnels turn the usual SSH story around: instead of you reaching a server, the server reaches back in through the tunnel you created. This solves the classic “my device is behind NAT or a strict firewall” problem without punching random holes in routers.
What problem does a reverse tunnel solve?
Remote port forwarding (ssh -R) lets you expose a local service (a dev site on localhost:3000, a Raspberry Pi on your LAN, a maintenance SSH port) through a server that is already reachable on the public Internet. The client behind NAT initiates the only outbound SSH connection it is allowed to make, and the remote server opens a socket that forwards traffic back through the encrypted channel.
How the ssh -R flag works
ssh -R <remote-port>:<target-host>:<target-port> user@remote-server
- <remote-port>: The port that will be opened on the reachable server.
- <target-host>:<target-port>: Where that traffic should go once it travels through the tunnel (often
localhost:PORTon the hidden machine). - user@remote-server: The jump host you control on the public Internet.
Hands-on practice lab
Test it from anywhere that can reach the public server:
curl http://your.remote.server:8080/
You should see the output of the local HTTP server. You effectively made your laptop’s port 3000 reachable without touching your firewall rules.
Create the reverse tunnel from the hidden machine:
ssh -fN -R 8080:localhost:3000 deploy@your.remote.server
-R exposes server port 8080 and forwards it to localhost:3000 on the hidden machine. -N tells SSH not to start a shell, and -f runs it in the background.
Run something locally (the machine behind NAT):
python3 -m http.server 3000
Prep the reachable server (Ubuntu example):
sudo apt install openssh-server
sudoedit /etc/ssh/sshd_config
# ensure these lines exist
AllowTcpForwarding yes
GatewayPorts clientspecified
sudo systemctl restart ssh
Keeping the tunnel alive
- autossh will re-establish the tunnel if the connection drops:
autossh -M 0 -fN -R 8080:localhost:3000 deploy@your.remote.server
- Systemd user service for persistence:
[Unit]
Description=Reverse SSH tunnel
After=network-online.target
[Service]
ExecStart=/usr/bin/ssh -N -R 8080:localhost:3000 deploy@your.remote.server
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
Enable it with systemctl --user enable --now reverse-tunnel.service.
Security checklist
- Create a dedicated unprivileged account on the remote server for each tunnel.
- Use SSH keys plus
ssh-agentor hardware keys instead of passwords. - Limit the tunnelled ports with
PermitOpeninsshd_config. - Monitor logs (
/var/log/auth.log) or wire autossh’s-Mmonitor port into your metrics.
Member discussion: