When setting up SSH remote port forwarding (-R
) across multiple backend machines through a hub server with multiple IP addresses, a common issue arises where the forwarded ports bind to all interfaces (0.0.0.0) despite specifying a particular bind_address. Let's examine the core problem and solutions.
Given your configuration:
# From machine-one
ssh -NR 100.200.130.121:22:localhost:22 root@hub-server.tld
# On hub-server.tld
netstat -tan | grep LISTEN
tcp 0 0 100.200.130.121:2222 0.0.0.0:* LISTEN
tcp 0 0 :::22 :::* LISTEN
The key observation here is that even when explicitly specifying 100.200.130.121
as the bind_address, SSH still listens on all interfaces (0.0.0.0). This prevents you from simultaneously running separate SSH forwarding on other IPs of the same server.
First verify your SSH version:
ssh -V
OpenSSH_8.2p1, OpenSSL 1.1.1f 31 Mar 2020
Some older versions (pre-7.0) had limitations in interface-specific binding. Modern versions should support this functionality when properly configured.
Your current sshd_config contains:
GatewayPorts yes
This is correct, but we need additional parameters. Try these modifications:
# In /etc/ssh/sshd_config on hub-server.tld
GatewayPorts clientspecified
AllowTcpForwarding remote
Then restart SSH:
systemctl restart sshd
Use this enhanced syntax:
ssh -NR \*:100.200.130.121:2222:localhost:22 root@hub-server.tld
The backslash before the asterisk is crucial to prevent shell expansion. This tells SSH to bind specifically to the given IP.
If the SSH method still doesn't work as expected, implement iptables rules:
# For machine-one (100.200.130.121)
iptables -t nat -A PREROUTING -d 100.200.130.121 -p tcp --dport 22 -j DNAT --to-destination machine-one:22
iptables -A FORWARD -p tcp -d machine-one --dport 22 -j ACCEPT
# Repeat for other machines with their respective IPs
iptables -t nat -A PREROUTING -d 100.200.130.122 -p tcp --dport 22 -j DNAT --to-destination machine-two:22
iptables -A FORWARD -p tcp -d machine-two --dport 22 -j ACCEPT
After implementation, verify with:
ss -tulpn | grep 22
# Should show specific IP bindings
# Test connectivity
telnet 100.200.130.121 22
telnet 100.200.130.122 22
For permanent solutions:
# For SSH method: Add to ~/.ssh/config
Host hub-forward-1
HostName hub-server.tld
RemoteForward \*:100.200.130.121:2222 localhost:22
# For iptables method
iptables-save > /etc/iptables.rules
# Add to /etc/rc.local
iptables-restore < /etc/iptables.rules
When setting up reverse SSH tunnels (-R) on a server with multiple IP addresses, you'll notice SSH automatically binds to all interfaces (0.0.0.0) regardless of specifying a bind_address. This behavior persists even with GatewayPorts enabled in sshd_config.
# Problematic behavior example
ssh -NR 100.200.130.121:22:localhost:22 user@hub-server.tld
# Still listens on all interfaces:
netstat -tuln | grep 22
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
The SSH protocol implements remote port forwarding (-R) with these characteristics:
- bind_address parameter primarily controls who can connect (security layer)
- Actual socket binding occurs at the kernel level before SSH process starts
- Legacy behavior maintains compatibility with older implementations
Solution 1: Combining SSH with iptables
Create targeted firewall rules to restrict access:
# On hub-server.tld:
iptables -A INPUT -p tcp --dport 22 -d 100.200.130.121 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
# Verify with:
iptables -L INPUT -n -v
Solution 2: Port Multiplexing with socat
Use socat as a smart proxy:
# On hub-server.tld:
socat TCP-LISTEN:22,bind=100.200.130.121,fork,reuseaddr TCP:localhost:2222 &
# SSH command becomes:
ssh -NR 2222:localhost:22 user@hub-server.tld
Solution 3: Systemd Socket Activation
For systemd-based systems:
# Create /etc/systemd/system/ssh-port121.socket
[Socket]
ListenStream=100.200.130.121:22
BindIPv6Only=both
Accept=yes
[Install]
WantedBy=sockets.target
Here's a complete setup for three machines:
# On hub-server.tld's sshd_config:
Match Host machine-one
AllowTcpForwarding yes
PermitOpen 100.200.130.121:22
# machine-one's connection command:
autossh -M 0 -NR 12122:localhost:22 -p 2222 user@hub-server.tld
# Corresponding iptables rule:
iptables -t nat -A PREROUTING -d 100.200.130.121 -p tcp --dport 22 \
-j DNAT --to-destination :12122
When implementing these solutions:
- Monitor connection latency with 'ss -ti'
- Consider using PersistentKeepalive in SSH config
- For high-traffic services, test kernel TCP stack settings
Always verify:
- Firewall rules persist after reboots
- SSH GatewayPorts aren't exposed to public interfaces
- Regularly audit port binding behavior