SSH -R Remote Port Forwarding Only Binds to 127.0.0.1: Troubleshooting Non-Localhost Loopback Bindings


2 views

When executing commands like:

ssh -N -f -R127.0.2.3:23000:127.1.2.3:23000 user@remote

Many developers expect the tunnel to bind to the specified loopback address (127.0.2.3) on the remote server. However, SSH defaults to binding only to 127.0.0.1 regardless of what you specify in the -R parameter.

This behavior stems from OpenSSH's security design:

  • Remote port forwarding (-R) defaults to localhost binding only
  • The GatewayPorts setting in sshd_config controls this behavior
  • Modern SSH versions restrict bind addresses for security reasons

To achieve the desired binding behavior:

Server-side Configuration

Edit /etc/ssh/sshd_config on the remote server:

# Enable binding to non-localhost addresses
GatewayPorts clientspecified
# Then restart sshd
sudo systemctl restart sshd

Alternative Workarounds

If you can't modify server configuration:

# Use socat to redirect after establishing the tunnel
ssh -N -f -R23000:127.1.2.3:23000 user@remote
# Then on remote server:
socat TCP-LISTEN:23000,bind=127.0.2.3,fork TCP:127.0.0.1:23000

Verify the binding with these commands:

# On remote server:
ss -tulnp | grep 23000
# Or alternatively:
netstat -tulnp | grep 23000

For multiple services on the same port:

# First tunnel
ssh -N -f -R127.0.2.1:443:service1.local:443 user@remote
# Second tunnel 
ssh -N -f -R127.0.2.2:443:service2.local:443 user@remote
# Requires GatewayPorts clientspecified on server

When executing SSH remote port forwarding with specific bind addresses like:

ssh -N -f -R127.0.2.3:23000:127.1.2.3:23000 user@remote

Many developers expect the remote end to bind to the specified IP (127.0.2.3 in this case), but it defaults to 127.0.0.1 regardless of the configuration.

SSH servers typically restrict remote forwarding to localhost (127.0.0.1) by default for security reasons. This is controlled by the GatewayPorts option in sshd_config.

To enable binding to specific loopback addresses:

# On the remote server's sshd_config
GatewayPorts clientspecified
# Then restart sshd
sudo systemctl restart sshd

Alternative approach using socat as a workaround:

# On remote server after standard SSH tunnel is established
socat TCP-LISTEN:23000,bind=127.0.2.3,reuseaddr,fork TCP:127.0.0.1:23000

For multiple services needing the same port:

# Tunnel 1
ssh -N -f -R23000:service1:23000 user@remote

# Tunnel 2 (using different IP)
ssh -N -f -R23001:service2:23000 user@remote

# Then on remote:
socat TCP-LISTEN:23000,bind=127.0.0.2,reuseaddr,fork TCP:127.0.0.1:23000
socat TCP-LISTEN:23000,bind=127.0.0.3,reuseaddr,fork TCP:127.0.0.1:23001

When modifying GatewayPorts, be aware that:

  • It potentially exposes services to other network interfaces
  • Always combine with firewall rules
  • Consider using SSH certificates for authentication

For temporary solutions, you can specify at connection time:

ssh -o GatewayPorts=yes -R127.0.2.3:23000:127.1.2.3:23000 user@remote

Note this requires server-side configuration to be permissive.

To check where your tunnels are actually bound:

# On remote server
ss -tulnp | grep 23000
# Or for BSD systems:
sockstat -l | grep 23000