How to Configure VPN Split Tunnel on Mac

Keith Njagi
4 min readMay 4, 2022

In this current world of remote work, we continue to see a growth in the use of VPNs in enterprises to support secure remote experiences for their users within their corporate network.

The norm in most institutions is that there will be an entire IT-support team dedicated to the provisioning of the required tools configured for use within the organization. Such are configured for accessing internal servers while accessing resources on internet and jumping into video calls. But then again this does not apply for every use case…

In certain cases you’ll need a device not configured for use within the corporate network or some institutions may not offer the support in setting up your devices for the same as well.

A VPN routes all the network connections through the VPN tunnel, this dramatically slows down connection to internet resources, at times refusing connection to others.

Split tunneling works by tunneling some of your traffic through a VPN and routing the rest via the open network. This way, you have both the benefits of a secure connection to your on-premise servers as well as a speedy connection to the open internet.

In this article we will set up split tunelling with Openconnect alongside vpn-slice(a vpnc-script replacement) to set your own split tunnel on client side. vpn-slice routes only traffic for specific hosts and/or subnets through the VPN by looking up named hosts, using the VPN's DNS servers, and adding these entries to the /etc/hosts (which it cleans up after VPN disconnection).

  1. Install Openconnect via Homebrew.
brew update
brew install openconnect

2. Add password-less sudo ability for the openconnect command.

sudo sh -c \
'echo "%admin ALL=(ALL) NOPASSWD: /usr/local/bin/openconnect" > /etc/sudoers.d/foo'

3. Test the vpn connection with:

sudo openconnect --user=<VPN username> <VPN host>

You will be prompted for your vpn password if any exists.

To exit use control + c(for mac).

Note: Newer versions of OpenConnect will use the utun device on OS X which does not require the TUN/TAP driver or other additional kernel modules to be installed.

4. Now we install vpn-slice via Homebrew.

brew install vpn-slice

5. While it would be easy to initiate the connection with a single command as below;

sudo openconnect --user=<VPN username> <VPN host> \
--script 'vpn-slice <internal host> <internal CIDR range>'

At times this becomes cumbersome in a practical daily use basis. Just to put this into context, if my vpn is set up for VPN host vpn.example.com with my internal host example.com and internal CIDR range 192.168.1.0/24for user johndoe

sudo openconnect --user=johndoe vpn.example.com \
--script 'vpn-slice example.com 192.168.1.0/24'

Any new internal host or CIDR range would require to be added to the list and the list may end up becoming too long. The easiset way out would be transforming this into shell functions and designing simpler commands that will be used for each new connection.

Scripting our shell functions

  1. Create a vpn-slice-addresses script in ~/vpn-slice-addresseswith the following:
#!/bin/sh

# Script that starts vpn-slice with the hostnames/subnets
# that need to be accessed via the VPN.

vpn-slice \
<host 1> \
<host 2> \
<CIDR range A> \
<CIDR range B>

Note: This file can live in any location as long as it is correctly referenced in the following steps.

2. Set the right permissions for the script:

chmod a+x ~/vpn-slice-addresses

3. Create the following functions in your .zprofile (or in a file that you source from your .zprofile). By default .zprofile located at ~/.zprofile (I’ve found the .zprofile to be a more permanent solution compared to the .zshrc as in this article) :

export COMPANY_VPN_HOST=<VPN host>export VPN_USER=<VPN USER>function vpn-up() {
if [[ -z $COMPANY_VPN_HOST ]]
then
echo "Please set COMPANY_VPN_HOST env var"
return
fi
if [[ "$1" == "split" ]]
then
echo "Starting the vpn with split tunneling ..."
sudo openconnect --background --script='~/vpn-slice-addresses' --user=$VPN_USER $COMPANY_VPN_HOST
else
echo "Starting the vpn ..."
sudo openconnect --background --user=$VPN_USER $COMPANY_VPN_HOST
fi
}

function vpn-split() {
vpn-up split
}

function vpn-down() {
sudo kill -2 `pgrep openconnect`
}

For any help on the various tags used in the openconnect command, you can click here.

4. Reconfigure your .zprofile . Note with .zprofile, the next time you log in to your terminal, everything within the file will automatically be loaded.

source ~/.zprofile

5. Connect!

If you wand to send all the traffic through the VPN do:

vpn-up

If instead you want to do Split Tunneling, use:

vpn-split

6. Disconnect

Given OpenConnect is started using the --background flag, we can't use Ctrl-Cto close it, instead we need to run:

vpn-down

--

--

Keith Njagi

- DevSecOps Engineer at Safaricom PLC | AiOps & Founder Ticko Africa