Is there a VPN Monitor/Kill Switch application for Ubuntu?

9,760

Solution 1

I had the same setup, and "VPN kill switches" are trickier than one would think.

Following your specification though, which reads as "kill certain apps when the VPN falls", there is a simple solution.

On Ubuntu, the network monitor has callbacks for network events, so that you can write a script to kill the apps you want. Example follows:

Edit /etc/NetworkManager/dispatcher.d/50vpndownkillapps.rb:

#!/usr/bin/env ruby

if ARGV == [ 'tun0', 'vpn-down' ]
  `pkill -f transmission`
  `pkill -f deluge`
end

The make it executable: chmod 755 /etc/NetworkManager/dispatcher.d/50vpndownkillapps.rb, and enjoy :-)

This script is in Ruby (so it requires ruby), but it can be trivially converted to a shell script.

It also assumes that the VPN adapter is tun0, which is the standard for OpenVPN configurations.

Solution 2

I had this same need and I developed my own solution as there seems to be no dedicated tool for this on Linux. There's no need to drop/close opened applications! :)

You need to setup iptables firewall, so your machine can connect ONLY to specified VPN servers (no other traffic allowed, except local, so there will be no "leaks"). Here's a script for that (found it on the web):

#!/bin/bash

# iptables setup on a local pc
# dropping all traffic not going trough vpn
# allowes traffic in local area network
# special rules for UPNP and Multicast discovery

FW="/sbin/iptables"
LCL="192.168.1.0/24"
VPN="10.0.0.0/12"
local_interface="eno1"
virtual_interface="tun0"

# VPN Servers
servers=(
123.123.123.123
124.124.124.124
)

#---------------------------------------------------------------
# Remove old rules and tables
#---------------------------------------------------------------
echo "Deleting old iptables rules..."
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X

echo "Setting up new rules..."

#---------------------------------------------------------------
# Default Policy - Drop anything!
#---------------------------------------------------------------
$FW -P INPUT DROP
$FW -P FORWARD DROP
$FW -P OUTPUT DROP

#---------------------------------------------------------------
# Allow all local connections via loopback.
#---------------------------------------------------------------
$FW -A INPUT  -i lo  -j ACCEPT
$FW -A OUTPUT -o lo  -j ACCEPT

#---------------------------------------------------------------
# Allow Multicast for local network.
#---------------------------------------------------------------
$FW -A INPUT  -j ACCEPT -p igmp -s $LCL -d 224.0.0.0/4 -i $local_interface
$FW -A OUTPUT -j ACCEPT -p igmp -s $LCL -d 224.0.0.0/4 -o $local_interface

#---------------------------------------------------------------
# UPnP uses IGMP multicast to find media servers.
# Accept IGMP broadcast packets.
# Send SSDP Packets.
#---------------------------------------------------------------
$FW -A INPUT  -j ACCEPT -p igmp -s $LCL -d 239.0.0.0/8  -i $local_interface
$FW -A OUTPUT -j ACCEPT -p udp  -s $LCL -d 239.255.255.250 --dport 1900  -o $local_interface

#---------------------------------------------------------------
# Allow all bidirectional traffic from your firewall to the
# local area network
#---------------------------------------------------------------
$FW -A INPUT  -j ACCEPT -s $LCL -i $local_interface
$FW -A OUTPUT -j ACCEPT -d $LCL -o $local_interface

#---------------------------------------------------------------
# Allow all bidirectional traffic from your firewall to the
# virtual privat network
#---------------------------------------------------------------

$FW -A INPUT  -j ACCEPT -i $virtual_interface
$FW -A OUTPUT -j ACCEPT -o $virtual_interface

#---------------------------------------------------------------
# Connection to VPN servers (UDP 443)
#---------------------------------------------------------------
server_count=${#servers[@]}
for (( c = 0; c < $server_count; c++ ))
do
    $FW -A INPUT  -j ACCEPT -p udp -s ${servers[c]} --sport 1194 -i $local_interface
    $FW -A OUTPUT -j ACCEPT -p udp -d ${servers[c]} --dport 1194 -o $local_interface
    $FW -A INPUT  -j ACCEPT -p tcp -s ${servers[c]} --sport 443 -i $local_interface
    $FW -A OUTPUT -j ACCEPT -p tcp -d ${servers[c]} --dport 443 -o $local_interface
done

#---------------------------------------------------------------
# Log all dropped packages, debug only.
# View in /var/log/syslog or /var/log/messages
#---------------------------------------------------------------
#iptables -N logging
#iptables -A INPUT -j logging
#iptables -A OUTPUT -j logging
#iptables -A logging -m limit --limit 2/min -j LOG --log-prefix "IPTables general: " --log-level 7
#iptables -A logging -j DROP


# Disable internet for "no-internet" user
#iptables -A OUTPUT -m owner --gid-owner no-internet -j DROP

You will need to setup table servers=(). Just specify there IP's of your favourite VPN servers.

Also check that other variables at the beginning of the script are set properly, otherwise it will block your entire connection.

Be sure to make iptables backup with:

sudo iptables-save > working.iptables.rules

(restore with sudo iptables-restore < working.iptables.rules)

It supports TCP and UDP connections, if you need only one of those, remove unwanted two lines from for () loop. Also check if your provider is using same ports - might be different.

Run this script with f.e. sudo /home/user/vpn.sh.

If you want to load it on boot (iptables usually resets after re-boot), add to your /etc/rc.local file f.e. line like bash /home/user/vpn.sh.


Next part is VPN auto-connector & monitor. Here's my own contraption for this:

#!/bin/bash

# CONNECTIONS
# Those values can be checked by running `nmcli con show`

vpn=(
85e60352-9e93-4be4-8b80-f6aae28d3c94
)

# NUMBER OF CONNECTIONS
total=${#vpn[@]}

# SLEEP
amount=10 # number of seconds to wait after each connection checking cycle
countdown=true # enable/disable animated countdown
skip=1 # how many seconds to substract between each animated countdown iteration

# LOGS
dir='/home/user/logs-vpn' # directory for storing logs
name='vpn' # prefix/name for a log file
seperate=true # create a seperate log file for each init session or log to single file
init=false # log init event (with logging setup)
start=false # log vpn start event
yes=false # log connected events
no=false # log disconnected events

# STYLE
clean='\e[1A\033[K' # clean & move to previous line
default='\e[0m' # default
blink='\e[5m' # blinking (works only in couple terminals, e.g. XTerm or tty)
dim='\e[2m' # dim/half-bright
disconnected='\e[91m' # light red
connected='\e[92m' # light green
count='\e[94m' # light blue
reconnecting='\e[96m' # light cyan
initializing='\e[93m' # light yellow
connection='\e[1m\e[91m' # bold light red

# SETUP
time=$(date +"%Y-%m-%d_%H-%M-%S")
if $separate; then
    file="$dir/$time.log"
else
    file="$dir/$name.log"
fi

# RESET
reset # reset screen
tput civis -- invisible # disable cursor

# RE-TIME
time=$(date +"%Y.%m.%d %H:%M:%S")

# INITIALIZATION
if $init; then
    printf "$time INIT" >> $file
    if $yes; then
        printf " -y" >> $file
    fi
    if $no; then
        printf " -n" >> $file
    fi
    printf "\n" >> $file
fi

# START CONNECTION
con=$(nmcli con show --active | grep "  vpn")
if [[ $con == '' ]]; then

    if $start; then
        printf "$time START\n" >> $file
    fi

    time=$(date +"%H:%M:%S")
    echo -e "${dim}[$time]${default} ${initializing}INITIALIZING...${default}"
    echo ""
    echo ""

    random=$(((RANDOM % $total)-1))
    try=${vpn[$random]}

    (sleep 1s && nmcli con up uuid $try) >& /dev/null
    sleep 10s
fi

# LOOP
while [ "true" ]; do
        time=$(date +"%H:%M:%S")

        # CLEAN AFTER COUNTDOWN
        if $countdown; then
            echo -en $clean
            echo -en $clean
        fi

        # CHECK CONNECTION
        con=$(nmcli con show --active | grep "  vpn" | cut -f1 -d " ")

        if [[ $con == '' ]]; then
                if $no; then
                    printf "$time NO\n" >> $file
                fi
                echo -e "${dim}[$time]${default} ${disconnected}DISCONNECTED !!${default}"
                echo -e "${blink}${reconnecting}re-connecting ...${default}"

                random=$(((RANDOM % $total)-1))
                try=${vpn[$random]}

                (sleep 1s && nmcli con up uuid $try) >& /dev/null
        else
                if $yes; then
                    printf "$time YES\n" >> $file
                fi

                arr=(${con//./ })

                echo -en $clean
                echo -e "${dim}[$time]${default} ${connected}CONNECTED${default} (${connection}${arr[0]^^}${default})"
        fi

        # SLEEP
        if $countdown; then
            echo -e "${count}$amount${default}"
            for (( c=$amount; c>=1; c=c-$skip )); do
                echo -en $clean
                echo -e "${count}$c${default}"
                sleep $skip
            done
            echo -e "${count}0${default}"
        else
            sleep $amount
        fi
done

It will auto-connect on start and monitor your connection with given interval (amount=10 gives 10 seconds interval) and re-connect on connection lost. Got logging feature and some other options.

Check your connections UUID's using nmcli con show and add your favourites (matching with IP's added to firewall) to vpn=() table. Everytime it will randomly select a connection specified in this table.

You can add it to your auto-start (does not need sudo priviledge). Here's an example how to start it in terminal:

mate-terminal --command="/home/user/vpn-reconnect.sh"

...and here's how it looks running in terminal:

enter image description here

...and here's how a leak-proof ping looks like after your VPN connection drops:

enter image description here

Enjoy :)

Solution 3

I've been able to setup a simple VPN kill switch with UFW. It works with all the vpn's i have.

Here's my ufw settings:

sudo ufw default deny outgoing
sudo ufw default deny incoming`
sudo ufw allow out 443/tcp
sudo ufw allow out 1194/udp
sudo ufw allow out on tun0 from any to any port 80
sudo ufw allow out on tun0 from any to any port 53
sudo ufw allow out on tun0 from any to any port 67
sudo ufw allow out on tun0 from any to any port 68

Works for me just fine :)

Share:
9,760

Related videos on Youtube

ras red2004
Author by

ras red2004

As I write this, I am back with Windows. I've been trying out Ubuntu every 2 years for the past 5 years, in hopes that it would be a suitable replacement for Windows for me as my daily driver. At the end of the day, it is still not for me. Ubuntu is a software that's heading on the right direction, but I don't think it's ready for mass adoption yet, as the software is way too broken out-of-the-box. I spend more time trying to get it to work optimally like Windows, then actually enjoying the piece of software. Don't get me wrong, it has many pros, however, I feel that the cons outweigh the pros for my needs. There are tons of online resources available to help the transitional user, but many of the guides are outdated. Many of the issues can be fixed with the help of users on askubuntu, but on the other hand, there are many problems that don't have solution. In which case, I had ran into a few issues well there are no solutions yet. So for these reasons combined, I am out! See you in 2 years Ubuntu.

Updated on September 18, 2022

Comments

  • ras red2004
    ras red2004 over 1 year

    Hi I am looking for a VPN Monitor/Kill Switch application that will ensure my VPN connection is always connected. Should my secured connection drop, the application will drop the applications that it's monitoring to prevent data leak. I know there are such applications for Windows. However, I have yet to find a suitable alternative for Linux.

  • Andrea Lazzarotto
    Andrea Lazzarotto over 7 years
    Regarding loading the script at boot, why won't you simply use /etc/rc.local?
  • GreggD
    GreggD over 7 years
    Beautiful idea (works like charm!), thank you :)
  • Norr
    Norr almost 7 years
    This is awesome, thank you so much. Verified to still be working as of July 2017.
  • halfer
    halfer over 6 years
    Looks OK, but I guess sudo ufw allow out 443/tcp permits secure website leakage when the VPN is not connected. Wouldn't you want to stop that? An HTTPS site with AJAX or WebSockets might re-connect in the background on its own, perhaps via a JavaScript timer.
  • zezollo
    zezollo almost 6 years
    For an unknown reason, ARGV did start with 'tun0' for a long while, and suddenly changed to 'tun1' without notice. So, to keep the kill switch working despite this first (useless) changing value, I had to change the test to if ARGV.last == 'vpn-down'