Autostart a VPN when away from the Office network

So getting a VPN configured and connected is pretty easy.

But I have a customer that would like their Linux Laptop to connect back to their office when it detects that it is away from the office.

The tricky part is that they use both the wireless and LAN (not both at the same time) when in the office, so I can’t just mess with the start script of one interface.

My dream system is one that detects if either the LAN or WiFi goes live, waits a reasonable stabalisation period, say 5 seconds, then checks if the address is on a particular range, and if not then triggers a VPN connection. If the interface fails to get an address from DHCP then it would be great if that was treated as a fail-condition and the VPN didn’t try and start (no address, why bother trying?)

Now I assume that this is not that unusual a scenario, so either it’s a feature/setting that I’ve missed, or there is a daemon to help manage this.

Does anyone have any suggestions? Is there a package to help with this, or is there built in functionality that I am just missing?

1 Like

Hello @outsidefactor :wink:

Well just an idea, but you can use udev rules and systemd service.

Let`s say you have 2 devices: Ethernet and Wifi.

Udev rule

  1. You can check for example the conditions with:
udevadm info /sys/class/net/enp4s0/
  1. Write a rule like that:
SUBSYSTEMS=="net", ID_NET_NAME=enp4s0, ATTR{operstate}="up", RUN+="systemctl start openvpn.service"

SUBSYSTEMS=="net", ID_NET_NAME=wl0, ATTR{operstate}="up", RUN+="systemctl start openvpn.service"

SUBSYSTEMS=="net", ID_NET_NAME=enp4s0, ATTR{operstate}="down", RUN+="systemctl stop openvpn.service"

SUBSYSTEMS=="net", ID_NET_NAME=wl0, ATTR{operstate}="down", RUN+="systemctl stop openvpn.service"

This does basically:

  • If enp4s0 or wl0 is UP (has a connection), then run the vpn service.
  • If enp4s0 or wl0 is DOWN (no connection), then stop the vpn service.

Systemd Service

You can write a service like that:

[Unit]
Description=My VPN
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
TimeoutStartSec=5
TimeoutStopSec=5
ExecStart=/usr/bin/vpn
ExecStop=/bin/pkill vpn

[Install]
WantedBy=multi-user.target

Script

On the script vpn, you do a basic check. For example, you can check if the router is there…

check=$(ping -4 -c1 router.domain)
condition=192.168.178.1
if [[ "$condition" =~ "$check" ]]
then
        echo "skip"
else
        exec run_vpn
fi

Sure you can check more…

That is just a basic example of what can be done.

Thanks, that’s a great starting point!

I can see how I can adjust all this to suit my needs. Thanks for the great idea!

I have to wonder if there isn’t an appetite for a package with a config wizard for this. I might have a crack at that.

We’re so close here…

I am not trying to run OpenVPN (I am specifically using Network Manager L2TP) so I am not triggering a service. In theory, if I can use UDEV attributes to test for an IP address the rule would be something like

SUBSYSTEMS=="net", ID_NET_NAME=enp4s0, ATTR{operstate}="up", ATTR{routeraddress}=10.75.1.1, RUN+="ifconfig vpninstance up"

SUBSYSTEMS=="net", ID_NET_NAME=enp4s0, ATTR{operstate}="down", RUN+="ifconfig vpninstance down"

But I can’t seem to tease either the interface IP address or interface default router address out of UDEV.

EDIT

I guess I could trigger a script in each case and do the “away from home” test in the up one…

If you use NetworkManager, why not use the NM dispatcher capabilities? It is intended exactly for this.

These Scripts will run every time a connections state changes, with some if-logic you can only start or stop your VPN if there is a specific IP or a connection is about to be stopped. Check out the examples in the Arch Wiki, and all actions and environment variables in the man page.

NetworkManager - ArchWiki

NetworkManager-dispatcher(8) — Arch manual pages

Thanks… this is exactly what I had assumed existed, but didn’t know the name for.

OK, so my script looks something like:

#!/bin/sh

interface=$1 status=$2
if [ "$IP4_GATEWAY" != "office router IP" ]; then
  case $status in
    up)
      nmcli c up vpnname
     ;;
    pre-down)
      nmcli c down vpnname
      ;;
  esac
fi

That is epic. Thank you so much for putting me on to this, it’s made it exactly as easy as I had hoped.

EDIT: I changed down to pre-down… Make sense to kill the vpn before the link shuts down.