MacOS built-in VPN IKEv2 force remove VPN DNS resolver

This is for once not a security related post, but as I couldn’t find one important detail on the Internet, I thought I’ll share my story.

When connecting with the MacOS built-in VPN service to a server, MacOS (12, Monterey) will happily accept all the parameters coming from the VPN server: Force their DNS setting, force their IP address routes (in this case route all traffic through VPN), etc. and there is no GUI to change it.

As I didn’t want to route all traffic through the VPN and as I didn’t want DNS resolutions to go through the VPN (except for certain domains), I wanted to reconfigure MacOS to do as I like. The routing issue was straight forward by adding a couple of specific routes (10.0.0.0/8) and then telling MacOS to use my usual default gateway in my network (192.168.1.1):

/sbin/route -nv add -net 10.0.0.0/8 -interface ipsec0
/sbin/route change default 192.168.1.1

However, when it came to changing the DNS settings, there are many not very helpful links that suggest that you change the “Service Order” (you can’t, IKEv2 VPNs do not get a Service entry) via GUI (network settings) or command line (networksetup). So that was not an option.

What sounded absolutely plausible is to change the SearchOrder (sometimes shown as just “order” in MacOS tools) of the VPN-DNS server to a higher value. This was attempted by Rakhesh but also didn’t work as he explains. He then explains that he overwrites the VPN’s DNS IP address with the one we want (d.add ServerAddresses * 192.168.1.1), but that didn’t work for me either and that just lead to not being able to do DNS resolving at all (my guess would be MacOS then tries to reach 192.168.1.1 via the VPN interface, which doesn’t work). So for me all available approaches didn’t work.

However, I found out that I can change something called “PrimaryRank” from “first” to “second”, which then made the DNS server of the VPN disappear as a resolver in the “DNS configuration” section of scutil --dns and everything worked as expected:

$ sudo scutil
> get State:/Network/Service/E6[REDACTED]57C
> d.show
<dictionary> {
  PrimaryRank : First
}
> d.add PrimaryRank Second
> set State:/Network/Service/E6[REDACTED]57C
> exit

The only problem is that I need to change that value back to “first” before I connect again. So the entire script I run and then prompts me to connect the VPN:

#!/bin/bash

# OPTIONS: default gateway and DNS server to use for normal Internet connection
GW_TO_USE="192.168.1.1"
DNS_TO_USE="$GW_TO_USE"

# Run this script after connection in the Network settings of MacOS to the VPN

# Check if running as root
if [ $EUID -ne 0 ]; then
    echo "This script should be run as root." > /dev/stderr
    exit 1
fi

echo "+ Fixing PrimaryRank to the original value"
scutil << EOF
get State:/Network/Service/E6[REDACTED]57C
d.add PrimaryRank First
set State:/Network/Service/E6[REDACTED]57C
exit
EOF

read -p "Connect VPN now, then press Enter to continue" </dev/tty

echo "+ Sleeping 3 seconds to make sure VPN is correctly connected..."
sleep 3

##
# Routing part
##

echo "+ Adding a manual route for VPN IP address range"
/sbin/route -nv add -net 10.0.0.0/8 -interface ipsec0

echo "+ Removing VPN as the default gateway"
/sbin/route change default "$GW_TO_USE"

##
# DNS part
##

echo "+ Last line of /etc/resolv.conf:"
tail -1 /etc/resolv.conf

echo "+ add DNS for *.example.org in /etc/resolver/example.org, it will be the last line from /etc/resolv.conf!"
tail -1 /etc/resolv.conf > /etc/resolver/example.org
echo "+ Last time we checked this was:"
echo 'nameserver 10.15.7.8'

echo "+ Fixing VPN DNS always being used"
scutil << EOF
get State:/Network/Service/E6[REDACTED]57C
d.add PrimaryRank Second
set State:/Network/Service/E6[REDACTED]57C
exit
EOF

echo "+ sleeping for 2 seconds"
sleep 2

echo "+ Your new /etc/resolv.conf:"
tail -1 /etc/resolv.conf