9

I have a macbook pro with mavericks running. I am looking for a way to connect to a VPN network in terminal.

The reason why I want to do this is that I want to write a small program in Python which automatically detects the fastest VPN server among 30 servers. This is a self-motivated practicing project so I think I'll stick to Python language. So I break down the task and think the program may need to connect to one of the server first and after that, run a speed test.

So I'm now stuck in this first step because I realised establishing a VPN connection seems to be under system level because I can't find pre-written VPN module in python. So I guess it will be like I tell Python to tell system shell to connect to a VPN server.

As I poking around and I found a command by typing apropos vpn. It's called vpnagent. But man vpnagent doesn't provide useful information nor which vpnagent tells me the utility is not installed in my Mac. Another interesting thing I found was pppd but setting up the configuration file was very frustrating. I didn't manage to do that.

So is there a way of connecting to VPN using terminal? In addition, since I'm new to programming, any comment on my project is also welcome. Thank you in advance.

6

You can use these wonderful bash functions from @slhck at Super User:

To connect to different VPNs, have multiple VPNs in Network.prefpane.

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
                repeat while (current configuration of VPN is not connected)
                    delay 1
                end repeat
        end tell
end tell
EOF
}
function vpn-disconnect {
/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 disconnect VPN
        end tell
end tell
return
EOF
}

Don't forget to change the name of the VPN.

  • Thank you it works. But it becomes unhandy that I need to change the name of the VPN in ~/.bash-profile every time I need to conned to another vpn. Is there a way of adding argument into the function, so i can call like vpn-connect UniVPN? – Choushishi Apr 21 '14 at 19:14
  • @Choushishi You can just create multiple functions with different names and different VPNs. Duplicate the function and change the function name on the first line and the VPN name. – grg Apr 21 '14 at 19:17
  • Thanks this is a good solution. I think I will still try to find a way to add arguments to make it more elegant – Choushishi Apr 21 '14 at 19:32
  • 1
    @Choushishi you can make it take an argument just by replacing UniVPN by $1. (while keeping the double-quotes) – Timothée Boucher Oct 19 '14 at 23:10
6

scutil should be all you need.

scutil --nc start <service name>

So your Python script to connect to each in turn may include something like this:

import re
from subprocess import call, check_output

vpns_string = check_output(["scutil", "--nc", "list"]) # lists all VPN services

vpns = re.findall('"(.+)"', vpns_string) # service names are double-quoted

for vpn in vpns:
  call(["scutil", "--nc", "start", vpn])
  #...do stuff with your connection, test speed etc.
  call(["scutil", "--nc", "stop", vpn])

You can optionally specify username, password & secret to connect with - see scutil --nc help for usage.

2
#!/bin/sh
# Random UUID for this config
vpnUuid=``
# Address of VPN server
serverName=""
# The group of usernames that is allowed in
groupName=""
# The name of connection type displayed in GUI
labelName=""
# The Shared Secret
sharedSecret=""
# The user this VPN config is for
userName=""

# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 4 AND, IF SO, ASSIGN TO "serverName"
if [ "$4" != "" ] && [ "$ranAtImaging" == "" ]; then
    ranAtImaging=$4
fi

# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 5 AND, IF SO, ASSIGN TO "serverName"
if [ "$5" != "" ] && [ "$serverName" == "" ]; then
    serverName=$5
fi

# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 6 AND, IF SO, ASSIGN TO "groupName"
if [ "$6" != "" ] && [ "$groupName" == "" ]; then
    groupName=$6
fi

# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 7 AND, IF SO, ASSIGN TO "labelName"
if [ "$7" != "" ] && [ "$labelName" == "" ]; then
    labelName=$7
fi

# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 8 AND, IF SO, ASSIGN TO "sharedSecret"
if [ "$8" != "" ] && [ "$sharedSecret" == "" ]; then
    sharedSecret=$8
fi

# CHECK TO SEE IF A VALUE WAS PASSED IN PARAMETER 9 AND, IF SO, ASSIGN TO "userName"
if [ "$9" != "" ] && [ "$userName" == "" ]; then
    userName=$9
fi

loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`
macModel=`system_profiler SPHardwareDataType | grep "Model Name:" | awk '{ print $3 }'`

# Check that we are running this on a MacBook
if [ "$macModel" == "MacBook" ]; then

    # Setup Keychain shared secret granting appropriate access for the OS apps
    /usr/bin/security add-generic-password -a "$groupName" -l "$labelName" -D "IPSec Shared Secret" -w "$sharedSecret" -s "$vpnUuid".SS -T /System/Library/Frameworks/SystemConfiguration.framework/Resources/SCHelper -T /Applications/System\ Preferences.app -T /System/Library/CoreServices/SystemUIServer.app -T /usr/sbin/pppd -T /usr/sbin/racoon /Library/Keychains/System.keychain

    # Write a Network Config containing this keychain item directly to System Config
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:DNS dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPv4 dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPv4:ConfigMethod string Automatic" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPv6 dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:Proxies dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:Proxies:ExceptionList array" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:Proxies:ExceptionList:0 string \*\.local" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:Proxies:ExceptionList:1 string 169\.254\/16" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:Proxies:FTPPassive integer 1" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:SMB dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:UserDefinedName string $labelName" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:Interface dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:Interface:Type string IPSec" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:AuthenticationMethod string SharedSecret" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:LocalIdentifier string $groupName" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:LocalIdentifierType string KeyID" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:RemoteAddress string $serverName" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:SharedSecret string $vpnUuid\.SS" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:SharedSecretEncryption string Keychain" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:XAuthName string $userName" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :NetworkServices:$vpnUuid:IPSec:XAuthPasswordEncryption string Prompt" /Library/Preferences/SystemConfiguration/preferences.plist

    # At this point, we should have only one Network Set (Automatic) so we find out its UUID — errr, messy
    autoUuid=`/usr/libexec/Plistbuddy -c "Print :Sets" /Library/Preferences/SystemConfiguration/preferences.plist | grep -B1 -m1 Automatic | grep Dict | awk '{ print $1 }'`

    # and we add our newly created config to the default set
    /usr/libexec/PlistBuddy -c "Add :Sets:$autoUuid:Network:Service:$vpnUuid dict" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :Sets:$autoUuid:Network:Service:$vpnUuid:__LINK__ string \/NetworkServices\/$vpnUuid" /Library/Preferences/SystemConfiguration/preferences.plist
    /usr/libexec/PlistBuddy -c "Add :Sets:$autoUuid:Network:Global:IPv4:ServiceOrder: string $vpnUuid" /Library/Preferences/SystemConfiguration/preferences.plist

else
    echo "This mac is not a MacBook… so skipping…"
fi

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .