Stable reverse port forwarding in SSH and stale sessions

Using VPS to forward ports behind NAT:

for((;;)) { ssh -R 2222:127.0.0.1:22 [email protected]; sleep 10; }

When connection is broken somehow and it is reconnecting.

Warning: remote port forwarding failed for listen port 2222
Linux vi-server.no-ip.org 2.6.18-92.1.13.el5.028stab059.3 #1 SMP Wed Oct 15 13:33:44 MSD 2008 i686

I type:

vi@vi-server:~$ killall sshd
Connection to vi-server.org closed by remote host.
Connection to vi-server.org closed.
Linux vi-server.no-ip.org 2.6.18-92.1.13.el5.028stab059.3 #1 SMP Wed Oct 15 13:33:44 MSD 2008 i686
vi@vi-server:~$ 

Now it's OK.

How it's simpler to make this automatic?


Solution 1:

I think you've taken the wrong side of it: In your case, sshd (server-side) is probably not failing nor having stale sessions, thus killing it should not help you besides the side effect of stopping roughly any connected ssh client connection.

It is the ssh client that does not quit connection upon failure of building the port forwarding mechansim. And this behavior is not a bug.

You need to look at the ExitOnForwardFailure option in the ssh manual..

Your script would be:

  for((;;)) { ssh -R 2222:127.0.0.1:22 [email protected] -o ExitOnForwardFailure=yes; sleep 10; }

Additionally, you might want to tighten ServerAliveInterval and ServerAliveCountMax for the client to detect sooner deconnections. (And you should ensure that TCPKeepAlive is on which is the default value). Note that autossh won't really help you more if you have set these options.

Solution 2:

I see there already is a good answer refering to an existing piece of software (autossh) that automatically maintains reverse port forwarding through ssh.

I still want to share my own little bash script doing the same thing and is trivial to set up.

#!/bin/bash

while true
do
  START=$(date +%s)
  ssh -NR rport:host:lport -o ServerAliveInterval=10 -o ExitOnForwardFailure=yes user@host
  END=$(date +%s)
  DIFF=$(( $END - $START ))
  if (( $DIFF < 3 ))
  then
    sleep 60
  fi
done

If forwarding fails repeatedly it should suffice to retry once a minute, if connection breaks after it was up for a while it will retry immediately.

I use it on archlinux with systemd (wrote a little .service-file) and it works like a charm.

Solution 3:

Looks like AutoSSH is the right thing for this.

Autossh is a program to start a copy of SSH and monitor it, restarting it as necessary should it die or stop passing traffic. The original idea and the mechanism were inspired by RSTunnel (Reliable SSH Tunnel).

With version 1.2 the method changed: autossh began to use SSH to construct a loop of SSH forwardings (one from the local machine to the remote, and one from the remote to the local), and then send test data that it expects to get back. (The idea was thanks to Terrence Martin.)

With version 1.3, a new method was added (thanks to Ron Yorston): a port may be specified for a remote echo service that will echo back the test data. This avoids the congestion and the aggravation of making sure all the port numbers on the remote machine do not collide. The loop-of-forwardings method remains available for situations where using an echo service may not be possible.

Features

  • autossh is a program to start a copy of ssh and monitor it, restarting it as necessary should it die or stop passing traffic. The idea is from rstunnel (Reliable SSH Tunnel), but implemented in C.
  • The author's view is that it is not as fiddly as rstunnel to get to work.
  • Connection monitoring using a loop of port forwardings or a remote echo service.
  • Backs off on rate of connection attempts when experiencing rapid failures such as connection refused.
  • Compiled and tested on OpenBSD, Linux, Solaris, Mac OS X, Cygwin, and AIX; should work on other BSDs.
  • Freeware.