NetworkManager Dispatcher script to manage SSH tunnel

Introduction

NetworkManager is the tool most Linux users are familiar with to manage their network connections and is shipped by default in Debian, Ubuntu, RedHat, Fedora, Linux Mint, and more. NetworkManager consists of two components:

  • the NetworkManager daemon, which runs in the background and is interacted with via the terminal
  • the NetworkManager GUI, which provides a user interface to interact with the daemon (usually via the NetworkManager applet in a toolbar)

While the GUI is nice, it doesn’t provide the flexibility that the daemon provides. In my case, I wanted to start a SSH tunnel when my network connection comes up. The NetworkManager GUI doesn’t allow this, but the daemon does.

Dispatcher

To accomplish this, NetworkManager includes a tool called Dispatcher to run scripts based on network events. The dispatcher scripts live in /etc/NetworkManager/dispatcher.d, are owned/run by root, and are executed in alphabetical order. In most cases, it is common to number the scripts (e.g, 10-network-up, 20-vpn-up, 30-dns-change, etc…) to make sure they execute in a specific order. Each dispatcher script receives two arguments: the name of the interface the action just happened on (from running ifconfig or ip a), and the action that was just performed on the interface. Of the latter, the allowed actions are as follows:

Action Description
pre-up The interface is connected, but not active
up The interface is connected and active
pre-down The interface will be deactivated, but has not been disconnected yet
down The interface has been disconnected and deactivated
vpn-pre-up The VPN is connected, but not active
vpn-up The VPN is connected and active
vpn-pre-down The VPN will be deactivated, but has not been disconnected yet
vpn-down The VPN has been disconnected and deactivated
hostname The hostname has changed
dhcp4-change The DHCPv4 lease has changed
dhcp6-change The DHCPv6 lease has changed
connectivity-change The network connectivity state has changed

As you can see, there are an almost unlimited number of possibilities here:

  • Start a SSH tunnel when an interface comes up.
  • Change DNS settings before a VPN goes down.
  • If network state changes, cycle NetworkManager.
  • If you receive a new IPv4 address, change your MAC address.

Script

In my case, I’m going to be running a script to start/stop the SSH tunnel based on the status of the ethernet interface.

#!/bin/bash

## NetworkManager Dispatcher script to start/stop a SSH tunnel when an interface goes up/down
## This needs to be placed in /etc/NetworkManager/dispatcher.d/ and will need to be chmod 700 and chown root:root

IF=$1
STATUS=$2

wait_for_process(){
    PNAME=$1
    PID=`pgrep $PNAME`
    while [ -z "$PID" ];do
        logger -s "NM ssh-tunnel: waiting 5 sec for $1"
        sleep 5;
        PID=`pgrep $PNAME`
    done
    logger "NM ssh-tunnel: $1 is running"
}

## Wait for nm-applet to start, otherwise this script will run at the login box and then die, since the user isn't logged in
wait_for_process nm-applet

if [ "$IF" = "enp0s3" ]; then
  case "$2" in
    up)
      logger -s "NM ssh-tunnel up triggered"
      nohup su -c "ssh -fN -M -S ~/.ssh-tunnel -D local_port_number -p ssh_port_number user@hostname" your_user_here
      ;;
    down)
      logger -s "NM ssh-tunnel down triggered"
      ## to check status of tunnel
      ## ssh -S ~/.ssh-tunnel -O check user@hostname
      su -c "ssh -S ~/.ssh-tunnel -O exit user@hostname" your_user_here
      ;;
  esac
fi

Now, let’s break down what is happening.

First, we’re waiting for the NetworkManager GUI (called nm-applet) to start. Otherwise, the script will run at boot and then die, since the user isn’t logged in. Next, we’re monitoring the status of the interface enp0s3. You can find your interface by running ifconfig or ip a. Next, we’re using a case statement for two interface actions: up and down. If the interface is up, write a message to /var/log/syslog, and then start the SSH tunnel. If the interface is down, write a message to /var/log/syslog, and then stop the tunnel. The real magic is in the SSH command, broken down below.

nohup – this tells the shell run the programs with the the SIGHUP signal ignored. This means when you log out, your program won’t be killed.

su -c "command" your_user_here – because this script is owned by root and runs as root, we need to run it as a different user. We’re using su to run it, and passing the -c option to tell it what command to run and as what user.

-f – tells SSH to go to the background (since I’m only using SSH to open a tunnel and won’t be interacting with the server)

-N – tells SSH to not execute a remote command (useful for opening a tunnel)

-M – tells SSH to run in master mode for connection sharing

-S – tells SSH the path of the control socket for running in master mode (SSH will use this file to manage the connection)

-D – tells SSH to use a specific local port (so I can redirect each program’s traffic to localhost:1234)

-p – tells SSH to use a specific remote port (in case you don’t run SSH on port 22)

user@hostname – the SSH user and server you’re connecting to

Testing

As long as you have SSH keys setup from the client (this box) to the server, you should be able to restart NetworkManager and see the SSH tunnel running. You can verify this with the command below.

ps -ef | grep ssh | grep -v grep

If it’s working, you should see your SSH tunnel up. You can also verify it (and look for errors) in the /var/log/syslog file.

grep ssh /var/log/syslog

 

This is a very specific use case, but it’s really helpful when applied.

Logan

 

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.