When establishing SSH reverse tunnels (ssh -R
), many developers encounter this behavior where the remote port only binds to localhost. Here's why this happens and how to properly configure it.
The default SSH server configuration restricts reverse forwarded ports to localhost only. This is a security measure. When you run:
ssh -R 2222:localhost:22 target.example.com
The target host will only listen on 127.0.0.1:2222 by default.
To make the remote host accept connections from all interfaces, you need to modify the SSH server configuration on the TARGET machine:
# Edit /etc/ssh/sshd_config on TARGET
GatewayPorts clientspecified
# Or for all ports:
GatewayPorts yes
Then restart the SSH service:
sudo systemctl restart sshd
With GatewayPorts clientspecified
in the server config, you can control binding on a per-connection basis:
# Bind to all interfaces:
ssh -R *:2222:localhost:22 target.example.com
# Bind to specific IP:
ssh -R 192.168.1.100:2222:localhost:22 target.example.com
After making changes, verify the listening ports:
netstat -tulnp | grep 2222
# Should show 0.0.0.0:2222 or [::]:2222 instead of localhost
Remember to check your firewall rules if connections still fail:
sudo ufw allow 2222
# Or for specific IP ranges:
sudo ufw allow from 192.168.1.0/24 to any port 2222
For production use, consider using autossh to maintain the connection:
autossh -M 0 -N -R *:2222:localhost:22 target.example.com
ssh -R 2222:localhost:22 user@target_host -N
When you create a reverse tunnel using ssh -R
, by default the remote host only listens on localhost (127.0.0.1). This explains why:
netstat -tuln | grep 2222
tcp 0 0 127.0.0.1:2222 0.0.0.0:* LISTEN
To make the remote host accept connections from any interface, you need to modify the SSH server configuration on the TARGET machine:
# Edit /etc/ssh/sshd_config
GatewayPorts clientspecified
Then restart the SSH service:
sudo systemctl restart sshd
Now when creating the reverse tunnel, specify 0.0.0.0
as the bind address:
ssh -R 0.0.0.0:2222:localhost:22 user@target_host -N
After establishing the tunnel, verify it's listening on all interfaces:
netstat -tuln | grep 2222
tcp 0 0 0.0.0.0:2222 0.0.0.0:* LISTEN
tcp6 0 0 :::2222 :::* LISTEN
For persistent tunnels, consider using autossh:
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-R 0.0.0.0:2222:localhost:22 user@target_host -N
When binding to all interfaces, consider these security measures:
- Use firewall rules to restrict access
- Implement SSH key authentication only
- Consider using non-standard ports
sudo iptables -A INPUT -p tcp --dport 2222 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 2222 -j DROP