Another solution is to use a LaunchDaemon to monitor a specific directory and launch an external script whenever that directory has modifications. The Mac Developer Library gives an outline of such a script (their example monitors /etc/hostconfig
and runs syslog -s -l notice "somebody touched /etc/hostconfig"
whenever that file's modification time changes.)
For our purpose, we note that every time you login to your VPN, the directory /Library/Preferences/SystemConfiguration
gets modified. So if you save the following plist file inside /Library/LaunchDaemons/vpn.connectscript.plist
, it will watch that directory:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>vpn.connectscript</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/opt/local/bin/vpn_some_script.sh</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Library/Preferences/SystemConfiguration</string>
</array>
</dict>
</plist>
Note that this LaunchDaemon will be called for more than just your VPN connecting (e.g., every time you connect to wifi). So your script /opt/local/bin/vpn_some_script.sh
should have a check that the tunnel is actually connected and the script should not create issues if its run multiple times in a row. (So if you mount shares, you may want to check that they aren't already mounted).
For example, my script /opt/local/bin/vpn_some_script.sh
is simply:
#!/bin/bash
# This only changes the routes table if utun0 exists.
# -n checks that `ifconfig utun0` returns something other than "" on STDOUT
# The 2> /dev/null redirects STDERR to null, to silence errors
if [[ -n `ifconfig utun0 2> /dev/null` ]] ; then
route -n add -net 10.0.0.0/8 -interface utun0
# Find the old default gateway
GATEWAY=`route -n get default -ifscope en0 | grep gateway | awk '{ print $2 }'`
# make everything (except blocks described above) go through old default gateway rather than VPN
route -n change default $GATEWAY
fi
which only adds the route to 10.0.0.0/8 through the tunnel if I am connected to the VPN tunnel (and change the default to go to the 192.168.1.1 router).
When you save the plist file, it will automatically be loaded on next reboot. However, you can also manually load it with:
sudo launchctl load -w /Library/LaunchDaemons/vpn.connectscript.plist
Note, if your bash script needs root permissions (e.g., my script which changes the routing table), you need to store it in /Library/LaunchDaemons/
(or /System/Library/LaunchDaemons
). If your script should be run as a normal user you should store it in ~/Library/LaunchAgents/
.
If your script runs as root and do not want to suffer privilege escalation attacks, the bash script that is being called should be in a directory like /opt/local/bin/
that can only be modified by root. If you stored in say ~/bin
a regular user could alter the script (or rename the file/directory and replace it with a file they wrote) and gain full access to your system.
1this work OK, but if you need to so something with the vpn network interface (like adding IPs to the route table for the VPN) this approach does not work completely as the shell script executes long before the VPN is connected – Felipe Sabino – 2014-08-12T16:49:10.787
1
just found this https://discussions.apple.com/thread/2255494?start=0&tstart=0 the guys does a delay and waits for the VPN to connect before continuing :)
– Felipe Sabino – 2014-08-12T17:08:59.683You mean the shell script "exits" before it is connected rather than "executes"? Yeah, a simple delay would do the trick – not aware of any other method. – slhck – 2014-08-12T17:15:46.087
This won't work with IKEv2 VPNs. Here's how you can do that: http://matt.coneybeare.me/how-to-setup-an-auto-reconnect-script-for-an-ikev2-vpn -service-on-your-mac/
– coneybeare – 2017-09-30T21:38:16.037