# Work Around An Always-On VPN on macOS (Global Protect Edition) > ⚠️ **This trick will undo itself after 5 minutes** as Global Protect resets routes automatically. > ❌ This is meant to unblock **very temporarily** you for things like finding out your real latency or debugging network issues. Remember that your company's IT team uses Global Protect's MITM for good reasons. You should have Global Protect enabled at all times. IT might require an always-on VPN on your Mac. It is sometimes useful to disable it for a time, e.g., to check your real latency or to diagnose network issues. What the always-on VPN does is that it creates a catch-all route, and all traffic goes to the `utun0` interface which gets routed to the VPN's gateway: ```console $ netstat -rn | head Destination Gateway Flags Netif default 10.220.43.57 UGScg utun0 <- cuplrit default 192.168.1.1 UGScIg en8 ``` ## TL;DR ```bash #!/bin/bash # # Remember that GlobalProtect will reset these changes after a few minutes. # set -euo pipefail vpn_if=$(ifconfig utun0 | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}') if route get default "$vpn_if" 2>&1 | grep -vq "not in table"; then sudo route delete default "$vpn_if" fi if route get "0/2" "$vpn_if" 2>&1 | grep -vq "not in table"; then sudo route delete "0/2" "$vpn_if" fi # phys_if=$(netstat -rn -f inet | grep '^default' | awk '{print $4}' | grep -v utun | head -1 || en13) phys_ifs=$(for iface in $(networksetup -listallhardwareports | awk '/Device/ { print $2 }'); do if ifconfig "$iface" 2>&1 | grep -q "status: active"; then echo "$iface" fi done) # dig +short clouddocsdev.s3-website-us-west-2.amazonaws.com @10.220.30.1 | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | # xargs -I@ sudo route add @ "$vpn_if" for phys_if in $phys_ifs; do sudo route add default "$(ifconfig "$phys_if" | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}')" done if ! scutil < <(echo $'open\nget State:/Network/Global/DNS\nd.show') | grep cyber-ark.com; then echo "cyber-ark.com is already in DNS search domains" else vpn_id=$(scutil < <(echo $'open\nget State:/Network/Global/DNS\nd.show') | grep __CONFIGURATION_ID__ | awk '{print $4}') sudo scutil <<EOF open d.init d.add ServerAddresses * 8.8.8.8 set State:/Network/Global/DNS set State:/Network/Service/$vpn_id/DNS set State:/Network/Service/gpd.pan/DNS quit EOF fi ``` ## Workaround: What does this script do? To work around the VPN, all you have to do is replace this route with a catch-all route routing everything to your physical networking card. In my example, that's `en8` with the gateway IP 192.168.1.1. So, all I have to do is to remove the utun0 route: ```bash sudo route delete default 10.220.43.57 ``` And replace it with my NIC's interface and gateway: ```bash sudo route add default 192.168.1.1 ``` The top-level default route is now your network interface card (in my case, `en8` is an ethernet adapter): ``` $ netstat -rn | head Destination Gateway Flags Netif default 192.168.1.1 UGScg en8 <- no longer MITM'ed! default 192.168.1.1 UGScIg en8 ``` Let's check that the traffic no longer goes through `utun0`: ``` $ route get google.fr route to: mad01s26-in-f163.1e100.net destination: default mask: default gateway: 192.168.1.1 interface: en8 flags: <UP,GATEWAY,DONE,STATIC,PRCLONING,IFSCOPE,GLOBAL> ``` Yes! And if you wonder how to undo these changes, click "Refresh Connection": ![Screenshot 2025-01-17 at 18.33.44](https://hackmd.io/_uploads/rk9iEGOD1e.png) ## Split Tunnelling: Selectively Forward to the VPN Some internal sites can only be accessed from the VPN's gateway IPs. For these, I wrote a tool to help me calculate the biggest CIDR from a list of IPs: ```bash go install github.com/maelvls/cidrcalc@latest ``` For example: ```console $ cidrcalc -hostname foo.s3-website-us-west-2.amazonaws.com -debug debug: Resolved IPs for foo.s3-website-us-west-2.amazonaws.com: [52.92.251.99 52.92.179.115 52.92.225.163 52.92.208.171 52.92.153.179 52.92.193.203 52.92.239.51 52.92.149.107] 52.92.128.0/17 ``` Thus, the biggest CIDR is 52.92.128.0/17. I can now configure a route to make sure these IPs are routed through the VPN: ```bash vpn_ip=$(ifconfig utun0 | grep "inet " | grep -Fv 127.0.0.1 | awk '{print $2}') sudo route delete default $vpn_ip sudo route add default 192.168.1.1 sudo route add $(cidrcalc -hostname foo.s3-website-us-west-2.amazonaws.com) $vpn_ip ``` With that, I can access the internal site https://foo.s3-website-us-west-2.amazonaws.com. ## Does it stay that way forever? No. Global Protect regularly refreshes the connection every few minutes after which your changes will be undone. ## DNS First, find the `State:` string that corresponds to your VPN: ```bash scutil <<< list | grep DNS | awk '{print $4}' \ | xargs -L1 -I@ sh -c "echo @; sudo scutil <<< $'open\nget @\nd.show'" ``` In my case, it was this one: ```text State:/Network/Service/FABE6D8F-7603-40A7-A232-9A041DF24C17/DNS <dictionary> { SearchDomains : <array> { } ServerAddresses : <array> { 0 : 10.220.43.1 } ServiceIdentifier : 2 SupplementalMatchDomains : <array> { 0 : } } ``` Thus, you can do: ```bash sudo scutil <<EOF open d.init d.add ServerAddresses * 8.8.8.8 set State:/Network/Service/FABE6D8F-7603-40A7-A232-9A041DF24C17/DNS set State:/Network/Service/gpd.pan/DNS set State:/Network/Global/DNS quit EOF ```