Wednesday, February 13, 2013

How to stop processes when a VPN connection drops (on Ubuntu)

A very common concern among VPN users is that processes will switch to the insecure connection when the VPN drops.

Before talking about the solution(s), be aware that VPN connections do drop and will drop - so this problem needs to be tackled, independently of the provider.

I personally endorse the privateinternetaccess provider. Of the three I've tested (the others being BTGuard and TorrentPrivacy), it's been the only one with a working customer support. Other providers just don't have it, or don't reply to the support requests.
Moreover, the company releases an application, on Windows machines, which automatically stops the internet connection when the VPN connection drops.

On a general basis, it's possible to write some scripts on Ubuntu, which solve the problem.

The concept is very simple. In Ubuntu, we can write callbacks which are executed when a given connection drops. At the same time, we can use a background script which notices this and acts accordingly. We're using this structure (callback/listener) because there is a non-trivial problem to solve otherwise, caused by the fact that the root user doesn't have direct access to the VPN password stored by the user.

First script to add (give executable permissions): /etc/NetworkManager/dispatcher.d/90vpnhandletransmission

#!/bin/bash

SIGNAL_FILE=/tmp/start_vpn_operations.tmp
APP_NAME=firefox                                   # app to start/stop
DESKTOP_USER=username                              # name of the user!

if [ "$1" == "tun0" -a "$2" == 'vpn-down' ]
then
  logger -s "NM Script vpn-down triggered"

  pkill -f $APP_NAME

  sudo -u $DESKTOP_USER touch "$SIGNAL_FILE"
fi
The second script to add may reside anywhere. It's a ruby script, so one needs a ruby interpreter (tested on 1.9). Just save it and give executable permissions. There are two ways of operating:
  • Execute the script without options, and leave it running; manually start the VPN and launch the application
  • Execute the script with the '-f' option; it will automatically start the VPN and launch the application
#!/usr/bin/env ruby

require 'fileutils'

SIGNAL_FILE = '/tmp/start_vpn_operations.tmp'
VPN_NAME    = 'Privateinternetaccess'              # this is the name of the VPN connection as created by the user
APP_NAME    = 'firefox'

case ARGV[ 0 ]
when '-f'
  FileUtils.touch( SIGNAL_FILE )
when nil
else
  puts "Usage: #{ $0 } [-f]"
  puts "  [f]orce start"
  exit
end

while true
  if File.exists?( SIGNAL_FILE )
    vpn_connection_started = system( "nmcli con up id '#{ VPN_NAME }'" )

    exit if ! vpn_connection_started

    FileUtils.rm_f SIGNAL_FILE

    fork { 
      `#{ APP_NAME } > /dev/null 2>&1`
    }
  else
    sleep 1
  end
end