Just when you thought it all made sense.

I have often needed to create tunnels between machines. In this case I wanted to set up a munin server to monitor various nodes on the Net. The problem was that my server is on a dynamic IP (yes, I’m mazochistic like that). So my tunnels were problematic because I could not set “allow connection” permissions on the client nodes properly (no static IP).

Solution: SSH tunelling to an unprivileged user account via public-key authentication and a cronjob to keep the tunnels alive:

You need an unprivileged user on the server (originator of the connection), in this example I use munin. Make sure the user has a workable home directory and create a public-private key pair.server# sudo -H -u munin /bin/bash
server$ cd ~
server$ mkdir .ssh
server$ ssh-keygen -b 1024 -C munin@`hostname -f` -t rsa
server$ cat .ssh/id_rsa.pub

Set up access restrictions, allow only connections to the local port we need (I use 4949 in this case). So paste: command="/bin/false",no-pty,no-X11-forwarding,no-agent-forwarding,no-port-forwarding,permitopen="localhost:4949"

Leave a space and then paste the KEY from step 2. It should all be in one line.

Save your file, log out of client

On the server, initiate the tunnel. You must do this manually at least once to check it works and answer ‘yes’ to permanently accept the client’s public key. In this case I forward local port 5050 to remote port 4949:server$ ssh -L 5050:localhost:4949 -f -N clientuser@client

Done!

And now, on to the interesting bit, how to keep this tunnel alive. I needed some scripts to do this for me via a cron job. Ideally, you want the unprivileged user on the server to handle this job, since we can benefit from economies of scale: better to create a script that handles all connections to multiple clients on one machine than having to install a copy of the script onto each and every client. So:

Become the unprivileged user:server# sudo -H -u munin /bin/bash

Create script file you need (here I put it in the home directory of the user). Note the $4 parameter can be something like “-oPort=19222” for example, if you run ssh on a different port on the client.server$ nano -w checktunnel
#!/bin/bash
#Usage: ./checktunnel "host name" "ip address" "port" ["ssh options"]
#======================================================================
PORT=$3
ADDRESS=$2
HOST=$1

Now you have a tunnel that will be checked every 5 minutes and re-upped if disconnected. You should also make sure you can read mail from the unprivileged user on the server, just to see if something went wrong! Then you might want to comment out the “echo … is UP” line since it will cause mail for you every 5 minutes, even if everything works!

Obviously there is room from improvement here, for example, making 4949 another parameter and grepping for the $ADDRESS specifically, but these I leave as an exercise to you genius readers.