Ssh – How to create SSH reverse tunnel with iptables forwarding

iptableslinuxsshssh-tunneling

I have a server in my home network (cannot port forward) and a VPS on the WAN. I need requests on a certain port, to, not from, the VPS to be forwarded to my home server. I have an SSH tunnel which works fine if I send the request to localhost on VPS. However I want requests forwarded to VPS's localhost from internet be sent to my home server through the tunnel. And it needs to be bi-directional.

I have seen this question, however it doesn't work for me. It is quite possible that I did something incorrectly.

My exact procedure:
On server a, my home server, I ran this command to set up the tunnel:

ssh -v -N -R 2222:localhost:22 root@server-b.com

I ran the following command on server b (VPS):

iptables -t nat -A PREROUTING -p tcp --dport 2223 -j DNAT --to-destination 127.0.0.1:2222

And tried to ssh from another machine:

ssh root@server-b.com -p 2223

I set GatewayPorts yes in sshd_config however I am still finding the same problem:
ssh: connect to host server-b.com port 2223: Connection refused.

Best Answer

Set GatewayPorts yes and AllowTcpForwarding yes in sshd_config on server b. With GatewayPorts clientspecified explicitly mention IP 0.0.0.0 or * on server a to create reverse tunnel. So that sshd on server b accepts connections from public too:

ssh -NTR *:2222:localhost:22 root@server-b.com

Otherwise sshd listens on loopback interface only as is the case with GatewayPorts no.

Now ssh from another machine to port 2222:

ssh root@server-b.com -p 2222

You'll be logged in to server a after authentication.
No need to set up iptables forwarding. Btw, avoid user root for remote logins, if possible.


If you don't want to set GatewayPorts option, then you need to forward traffic from some other port, say 2223, to localhost:2222.

  • This can be done with iptables on server b:

    iptables -t nat -I PREROUTING -p tcp -m tcp --dport 2223 -j DNAT --to-destination 127.0.0.1:2222
    

    * REDIRECT works only for same interface

    But routing to loopback interface isn't allowed unless:

    echo 1 | sudo tee /proc/sys/net/ipv4/conf/all/route_localnet
    

    Now ssh from another machine to port 2223:

    ssh root@server-b.com -p 2223
    

Another option is to setup some minimal local forwarding server from port 2223 to 2222 using tools like ssh, socat, netcat, *inetd etc.

  • On server b, with socat:

    socat TCP-LISTEN:2223,fork TCP:127.0.0.1:2222
    
  • Or with netcat:

    nc -l -p 2223 -c "nc 127.0.0.1 2222"
    
  • Or with ssh:

    ssh -4gfNTL 2223:localhost:2222 localhost
    

Any of the above can be combined with reverse tunnel to do a double forwarding from server a in a single step:

ssh -TR *:2222:localhost:22 root@server-b.com "ssh -4gfNTL 2223:localhost:2222 localhost"
Related Question