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

DNS zone transfer

Today I thought it would be cool to have a list of all domains that exist in Switzerland. As it turns out, the swiss registrar (Switch) has configured their nameservers correctly, so you can not do a DNS zone transfer 🙁 . But I found out that a lot of other TLDs allow to make zone transfers. I don’t know if its on purpose, but I don’t think so, because not all of their DNS root servers allow to do the transfer… Try it yourself (bash script):

tlds="AC AD AE AERO AF AG AI AL AM AN AO AQ AR ARPA AS ASIA AT AU AW AX AZ BA BB BD BE BF BG BH BI BIZ BJ BM BN BO BR BS BT BV BW BY BZ CA CAT CC CD CF CG CH CI CK CL CM CN CO COM COOP CR CU CV CX CY CZ DE DJ DK DM DO DZ EC EDU EE EG ER ES ET EU FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GOV GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN INFO INT IO IQ IR IS IT JE JM JO JOBS JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MG MH MIL MK ML MM MN MO MOBI MP MQ MR MS MT MU MUSEUM MV MW MX MY MZ NA NAME NC NE NET NF NG NI NL NO NP NR NU NZ OM ORG PA PE PF PG PH PK PL PM PN PR PRO PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR ST SU SV SY SZ TC TD TEL TF TG TH TJ TK TL TM TN TO TP TR TRAVEL TT TV TW TZ UA UG UK US UY UZ VA VC VE VG VI VN VU WF WS XN XXX YE YT ZA ZM ZW"
    
for tld in $tlds
do
   echo "Doing TLD $tld"
   for f in `dig ns $tld. | grep "NS" | cut -f 7 | grep "$tld." | grep -v "ANSWER"`
   do
       echo "$tld : $f"
       dig axfr $tld @$f >> output.txt
   done
done

For me it worked for the following TLDs: an, bi, ci, cr, er, et, ga, ge, gy, jm, km, mc, mm, mo, mw, ni, np, pg, pro, sk, sv, tt, uk, uy, ye, zw. Might change in the future. For me the winner is… Slovakia (sk)! Never seen so many DNS entries in one file 😀

Update: I just uploaded my results here. When I talked to Max he decided to put his treasures (.DE for example!) up as well, you’ll find his domains here.