Remote SSH tunelling on Debian/Ubuntu via dynamic IP

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:

  1. 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/
  2. Copy the key to your clipboard
  3. Connect to client (Receiver of connection). Use another unprivileged user with a workable home directory, I’ll use munin on this side, too.
    client# sudo -H -u munin /bin/bash
    client$ cd ~
    client$ mkdir .ssh
    client$ nano -w .ssh/authorized_keys
  4. 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"
  5. Leave a space and then paste the KEY from step 2. It should all be in one line.
  6. Save your file, log out of client
  7. 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


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:

  1. Become the unprivileged user:
    server# sudo -H -u munin /bin/bash
  2. 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
    #Usage: ./checktunnel "host name" "ip address" "port" ["ssh options"]

    #Looks like: ssh -NfL 5050:localhost:4949 -f -N munin@client
    if [ "`ps -eaf | grep "ssh -NfL $PORT" | grep -v grep`" ] ; then
    echo "tunnel to $HOST ($ADDRESS:$PORT) is up"
    echo "SSH tunnel ${HOST} NOT alive ... Restarting ..."
    logger -p daemon.notice "SSH tunnel ${HOST} NOT alive ... Restarting ..."
    echo "/usr/bin/ssh -NfL $PORT:localhost:4949 $4 munin@$HOST"
    /usr/bin/ssh -NfL $PORT:localhost:4949 $4 munin@$HOST
    sleep 1

  3. Add the appropriate cronjob entry:
    server$ crontab -e
    */5 * * * * ~/checktunnel client 5050

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.

Written by admin in: Uncategorized | Tags: , , , , , ,

1 Comment »

  • First article about SSH tunnelling with Munin that worked for me. Thx

    Comment by Oink — June 21, 2012

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress | Aeros Theme | WordPress Themes