How can you automatically run a script after connecting to a VPN on OS X?

If you already have the VPN configured, you can connect to it via commandline. As I explained in this answer, you can create two shell functions for login and logout, respectively, by adding them to your ~/.bash_profile – see the linked answer for the vpn-disconnect function.

function vpn-connect {
/usr/bin/env osascript <<-EOF
tell application "System Events"
        tell current location of network preferences
                set VPN to service "UniVPN" -- your VPN name here
                if exists VPN then connect VPN
        end tell
end tell
EOF 
# insert your commands here
}

Just include the custom commands you need after the EOF marker.


If you want a GUI way to do it, open up Automator.app and create a new Application. Then, drag the actions to run an AppleScript and Shell script from the left pane, and insert the commands as shown below.

You can then just put this pseudo-application into your Dock to quickly launch it.


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.


/etc/ppp/ip-up for pre-connection script, and /etc/ppp/ip-down for post-connection script. Do not forget to add execution permission bit. I use them for changing and restoring the route table before and after PPTP and L2TP VPN connections.

After you've written the two script named ip-up and ip-down, you run the following commands in the Terminal.app:

chmod +x ip-up ip-down
sudo cp ip-up ip-down /etc/ppp