Setting Up AutoSSH

The Goal: Always Connected Clients

The goal is to have random clients connected to random networks using DHCP getting random addresses behind NAT connected firewalls. People try all kinds of things to achieve this. They use DynDNS services. They open and forward ports through their firewalls. Everyone tries accomplishing this in different ways. This document tells how I am doing it. Is it better? Is it better for me but not the ultimate. Is it perfect? No. But it is quite good. I find it more robust than even OpenVPN and depending upon what I am doing it is better than OpenVPN. Does it replace OpenVPN? No. I also use OpenVPN in other environments for a full service solution. How does it compare to sshuttle? I love sshuttle but that has a different use goal in mind.

With that introduction let's get on with the documentation. This is what I do for connectivity to remote clients. I have the client connect to my server machine. If the client is online it will connect to my server and use ssh to create a tunnel back to itself. I can then always follow through the tunnel to the remote device. I have many of these remote clients set up in commercial environments at mountain airports and other remote places.

Is this document perfect? No. Far from it. In fact I am rather embarrased by how jumbled this appears to me. I have held off posting this for some time because I wanted to make it more cohesive. In the end I have given up and am just going to let it escape as it is now. Some of you will think this is overly complicated and want to run screaming back to OpenVPN. That's fine. OpenVPN is awesome. I use it too. But I also feel very confident setting up AutoSSH on an already running remote host with no danger of locking myself out of it. With OpenVPN I need to modify routing tables and have locked myself out at times. I have never done that with AutoSSH. In fact I usually set up two connections for redundancy and rewire them one at a time when needed. If this document helps you then great! If not then you got what you paid for it. I am hoping it is useful regardless. Read through the entire document before doing any part of it. Then after you think you have a grip on how things go together then try it on a test instance. I think you will find it a reliable connection method.

What is AutoSSH and why do you want it?

AutoSSH is a program that starts ssh and monitors it, restarting it as necessary to keep it runing continuously. This ssh is then used for port forwarding creating a simple VPN service connection between two Internet connected hosts. A typical use of this ssh port forwarding VPN service is for remote access to a host on a dynamic ip address. The remote host can change addresses and will always "call back home" to create the VPN to enable you to log into it. I also use this for secure email transport from a client to a mail server. There are many possible uses. I am using this on everything from a Raspberry Pi to a large rack mounted server.

In order to make use of this for logging into dynamic clients one must have a static IP address somewhere. I have a server with a static address that I use. If you have a server somewhere, anywhere, then that is what you will use. If you don't have a static IP address anywhere then this will not be useful to you. But if you do then you can have your dynamic remote system open up a listening port on your server that you can use to connect to ssh.

I place a Banana Pi at the remote airport hangar. It has a network connection but only at a dynamic IP address behind a firewall. I want to be able to log into it from my home. Or copy files back and forth from it.

rsync -av somefile hangarpi:bin/
ssh hangarpi

It is really this simple to use. Let's talk about how to set it up. Setup is a little involved. But each step is simple and following this recipe guide should help you out. You can read the upstream AutoSSH documentation at http://www.harding.motd.ca/autossh/ for details, and there are many other fine articles, but this is my tutorial on how I do it.

Configuring the server

Conceptually I would like to start on either the client or the server and walk through the setup there. Then move to the other and walk through the setup on the other side. That would be great! But it can't be done. We must do a little here and a little there and go back and forth between the server and the client just a little bit.

Setting Up the Echo Port on the Server

AutoSSH has two different strategies that can be used to monitor the connection. It doesn't just monitor that the ssh program is alive. Instead it monitors that the ssh connection from end to end is alive and passing data. That is what we really want and much more useful. In order to do this autossh configures its own port forward across the tunnel from the client to the server and back again. It is the full loop that is important. From system A to system B and then back again from B to A.

By default autossh will use two connections. One connection from here to there. Another connection from there to here. This works great. However it means that as soon as you have two autossh systems running the second one collides using the server side port! You the reader haven't even started using it yet and already I am telling you about a problem that needs to be avoided. Sorry. But to avoid that problem I use the alternate strategy of using the echo service on the server. Using the echo service on port 7 only one connection is needed. That is the way to go! It allows using the same configuration on multiple clients.

AutoSSH will start on a local port. It will connect to the remote echo service port. The echo service port will echo back anything sent to it on the same connection. This allows autossh to monitor that the connection is alive and passing data from one end to the other end.

This requires that the server support an echo port service. The easiest way to do this is to install an inetd. I don't use the inetd for anything else these days. Therefore I like to install the BSD version of the daemon.

Some people will read this and go, "I am using xinetd now. It's all about the Red Hat xinetd, baby." For those xinetd people I say, "Knock yourself out! Different drummer, same beat." All that matters is that there is an echo service available on the server.

Install the inetd and the Debian inetd update script.

apt-get install openbsd-inetd update-inetd

On Debian use the one line command to set up the echo port. (As a small tidbit the --comment-chars '#' option is needed to let the command know this is a human editing the file and not a package postinst script. It is designed for package postinst scripts to use and there it uses a special comment.) This command sets up the /etc/inetd.conf file for the echo service.

update-inetd --comment-chars '#' --group INTERNAL --add "echo  stream tcp nowait root internal"

That command will do all of the work of editing the file and signaling the running inetd to read it after the update. Spiffy!

Installing AutoSSH on the client

Install the autossh program on the client.

apt-get install autossh

Configuring AutoSSH on the Client

UPDATE: Instead of using /etc/init.d/foo scripts and mostly due to the incoming of systemd (me throws salt over my left shoulder) I no longer put these files in /etc/init.d anymore. Instead I put them into /usr/local/sbin/foo and modify the Monit setup accordingly. I hope to get around to updating the following soon but...

Create the init.d startup files, two required, to have autossh start automatically when the system is rebooted. This is the majority of the needed tinkering to get this going.

I like to name the startup script after the connecting system. For example on my home system joseki automatically connecting to my server system havoc I name the script /etc/init.d/autossh_havoc to give it the name of the remote system. This allows me to have several connections and the file names won't collide. I will name it myserver from here on.

Download this sample init.d file (see as text in browser) into a working directory so that you can edit it. Change all occurances of the example hostname example.com to the name of your remote host. Feel free to edit it using a text editor. I will show the command line sed command here just for ease of documentation.

mv autossh_example.com.init autossh_myserver
sed --in-place 's/example.com/myserver/g' autossh_myserver
chmod a+x autossh_myserver

Place it into your /etc/init.d directory named using the /etc/init.d/autossh_myserver name.

mv autossh_myserver /etc/init.d/
chown root:root /etc/init.d/autossh_myserver

Download this sample default file (see as text in browser) into a working directory so that you can edit it. And the same action of changing all occurances of the example hostname example.com to the name of your remote host is needed again. This does NOT need to be executable. Don't chmod it.

mv autossh_example.com.default autossh_myserver

The default file is where all of the configuration needs to take place. This is where you will need to edit the configuration and make various changes. All of the capitalized AUTOSSH_* variables are used by autossh and need to be set in the environment. I use the lower case args variable to set this up in a way that makes editing it easier.

args="-N"
args="$args -oBatchmode=yes"
args="$args -oUsePrivilegedPort=no"
args="$args -oExitOnForwardFailure=yes"
args="$args -oServerAliveInterval=200"
args="$args -oSetupTimeOut=120"
args="$args -oUserKnownHostsFile=/dev/null"
args="$args -oCheckHostIP=no"
args="$args -oStrictHostKeyChecking=no"
args="$args -R 2201:127.0.0.1:22"
args="$args clientA@93.184.216.34"  # clientA at example.com

DAEMON_ARGS="$args"
unset args

AUTOSSH_POLL=200
AUTOSSH_PORT=21021:7
AUTOSSH_GATETIME=0
AUTOSSH_LOGFILE=/var/log/autossh_example.com.log
AUTOSSH_DEBUG=yes
export AUTOSSH_POLL AUTOSSH_PORT AUTOSSH_GATETIME AUTOSSH_LOGFILE AUTOSSH_DEBUG

Change all instances of example.com to the name of your server.

s/example.com/myserver/

Change the IP address 93.184.216.34 to the IP address of your server. (The 93.184.216.34 is the IP address of example.com.)

Pick a port on your server to forward to the ssh port on the autossh client. I like to start mine at 22something so that when I grep through a listening list the port 22something ports are all grouped together. Therefore I start at port 2201 for the first dynamic client and then 2202 for the next and 2203 for the next and so forth. But the choice is arbitrary. I keep a file with a listing of used ports so that I can allocate the next one for the next one.

This is also the place to forward other ports in either direction. Can server an http web page this way. Can send mail this way. To have the dynamic client be able to tunnel mail to the server use this addition. This sets up port 2501 locally tunneled over the VPN to the remote end. More configuration of the MTA (Mail Transfer Agent) is needed to actually make it transport mail however. Ask if you need this.

args="$args -L 2501:127.0.0.1:25"

Obviously the -R and -L options configure ports on the Remote or the Local end from the perspective of the dynamic client running the autossh command. From that dynamic client perspective it will either listen locally and forward it or it will listen remotely and forward it.

Move this created file into the /etc/default/autossh_myserver location.

chmod a+r,u+w,go-w autossh_myserver
chown root:root autossh_myserver
mv autossh_myserver /etc/default/

Create an SSH RSA Key on the client

Create an SSH RSA key for root on the dynamic client. Press Enter and take the defaults for all entries. Do not set a passphrase. Root's key does not need a passphrase and will use file system permissions to protect it. Also the dynamic client usually has no other logins.

ssh-keygen -t rsa

Note the /root/.ssh/id_rsa.pub file. This will be used in a moment to set up the login on the server.

cat /root/.ssh/id_rsa.pub

Using a Unique Login Everywhere

It is a good idea to compartmentalize security into layers. Therefore I always use a unique login for every dynamic client. This way if a remote client is compromised I can disable just that one client individually without needing to reconfigure any other dynamic clients that I have connected. I have several.

As a convention I name the user after the dynamic client system name. Conceptually it is the system that is going to log in using this account. I will use clientA for this example. Pick the name of your dynamic client for this user and not clientA from my example.

On the server:

adduser --disabled-password --gecos "clientA System" clientA

Set up the idrsa.pub key from the client into the authorizedkeys file for this user on the server.

cat /home/clientA/.ssh/authorized_keys

I do the copy by cut-n-paste. Be careful not to make any mistakes. End of line errors can be problematic. Edit carefully.

Testing the Dynamic Client Login to the Server

There are many things that might go wrong. It is time to test. Login from the client to the server. If it works then it will work for autossh. If it fails then debug why and fix it. This must work before autossh can function.

ssh -q -oStrictHostKeyChecking=no -oCheckHostIP=no -oUserKnownHostsFile=/dev/null clientA@93.184.216.34

If that works you will get a prompt on the remote machine. If it fails then remove the -q and start debugging. If it works then try it with all of the port forwarding inplace.

ssh -q -oStrictHostKeyChecking=no -oCheckHostIP=no -oUserKnownHostsFile=/dev/null -R 2201:127.0.0.1:22 clientA@93.184.216.34

If that is all working then you are almost done! Just need to set up autossh to start automatically and add a checker to ensure it stays running automatically forever.

Starting AutoSSH Automatically

Start the service.

service autossh_example.com start

Using Monit to Ensure AutoSSH Stays Running Forever

At this point in the configuration everything is working and the tunnel is up and running. However autossh itself sometimes fails and exits under rare circumstances. Since this is my remote access to these remote systems it is useful to set up Monit to monitor the process and restart it if it fails. Therefore we need a watcher to watch the watcher. Monit is perfect for this.

This is a belt and suspenders approach if you have both. AutoSSH will restart ssh tunnels. Monit will restart AutoSSH. A rock solid combination.

apt-get install monit

Then download this monit setup file (see as text in browser) and download this autossh monitor file and copy them to the monit config /etc/monit/conf.d/ directory.

check process autossh_example.com with pidfile /var/run/autossh_example.com.pid
  start program = "/usr/sbin/service autossh_example.com start"
  stop program = "/usr/sbin/service autossh_example.com stop"

The autossh script needs to be edited for the same example.com change as all of the other examples.

sed --in-place 's/example.com/myserver/g' monitconf.autossh

mv monitconf.Monit autossh /etc/monit/conf.d/Monit
mv monitconf.autossh /etc/monit/conf.d/autossh

chown root:root /etc/monit/conf.d/Monit
chown root:root /etc/monit/conf.d/autossh

Then restart monit.

service monit restart

Check that monit knows about the services.

monit status

Monit is pretty nice. Monit will send email when it detects a problem.

This following will check that autossh is running and restart it if for whatever reason it is not running.

If you wish to temporarily suspect monit from this action for manual maintenance then tell monit to stop monitoring. You can check the status. You can start monitoring back up again. You can unmonitor only a specific service.

monit status
monit unmonitor all
monit status
monit monitor all
monit unmonitor autossh_havoc

And of course you can always stop monit entirely.

service monit stop

Verification and Testing

Verify that everything is working. Things are tricky enough that frankly the odds are good that there will be a simple mistake somewhere and it won't be working. Therefore take it one step at a time and see if it is working. If not go back and verify each step to make sure that it is correct and work forward again.

The easiest way to verify that ssh is tunneled on the new port is to connect to it. The typical tool is netcat. Feel free. It has some problems. A better tool is socat.

apt-get install socat

Verify that you can connect to the port using socat. If you can see the ssh banner then you know the connection is operating. Use Control-C to break out of the connection.

socat - TCP4:localhost:2201
  SSH-2.0-OpenSSH_6.7p1 Debian-5+deb8u3
Control-C

With that working verify that you can log into your dynamic client from the server using the VPN.

Set up your ~/.ssh/config file for this system. This can all be done in one long command line command.

ssh -oProxyCommand='ssh -W 127.0.0.1:2201 myserver.example.com' root@myclient

That can be done on the command line but it is much more convenient to put it in the ssh config file. Here is an example from my config for logging into the remote system through myserver.

Host myclient
  ProxyCommand ssh -W 127.0.0.1:2201 myserver
  HostKeyAlias myclient

This avoids the need to open any ports in the firewall. Simply hop through the server to get to the dynamic client. These examples are now working trivially.

rsync -av somefile myclient:bin/
rsync -av myclient:bin/somefile .
ssh myclient

This works best when using an ssh-agent. Otherwise you will be prompted for your ssh rsa key passphrase twice. (You do have a passphrase on your ssh rsa key, right?) Once for the server and once for the client. Using ssh-agent allows convenient secure access.

What is not covered

Not covered in this guide is how to set up the Postfix MTA to email the mail sent by monit through to your server. Since monit sends email whenever anything happens this is pretty much a necessity or email will build up on the dynamic client.

Later!

Hope this quick guide helps!

License

Copyright (C) 2015, 2016, 2017, 2021 Bob Proulx

Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty.