How to Force Split Routing in Juniper Network Connect VPN on macOS: Bypass VPN for Local Traffic


12 views

When using Juniper Network Connect on macOS, the default configuration often routes all network traffic through the VPN tunnel. This becomes problematic when you need to:

  • Access local network resources
  • Stream media from services that block VPN traffic
  • Maintain faster connections to non-work resources

The Juniper client aggressively modifies /etc/resolv.conf to enforce the corporate DNS policy. You've correctly identified this behavior when observing:

search XXX.com [redacted]
nameserver 10.30.16.140
nameserver 10.30.8.140

The error -bash: /etc/resolv.conf: Permission denied occurs because modern macOS implements System Integrity Protection (SIP). Even root can't directly modify some system files. Here's why your command failed:

sudo echo "nameserver 192.168.0.1" >> /etc/resolv.conf

The shell handles the redirection before sudo elevation occurs.

Instead of fighting the file modification, try these approaches:

Method 1: Using networksetup

sudo networksetup -setdnsservers Wi-Fi 192.168.0.1

Method 2: Create a persistent resolver config

sudo mkdir -p /etc/resolver
sudo sh -c 'echo "nameserver 192.168.0.1" > /etc/resolver/local'

Here's how to modify your routing tables to bypass the VPN for specific traffic:

# Get your current default gateway (before VPN connection)
ORIGINAL_GW=$(netstat -rn | grep default | grep -v utun | awk '{print $2}')

# Connect to VPN, then add specific routes
sudo route -n add -net 192.168.0.0/16 $ORIGINAL_GW
sudo route -n add -net 10.0.0.0/8 $ORIGINAL_GW

# For media streaming services
sudo route -n add -host 54.239.25.200 $ORIGINAL_GW  # Example: Amazon Music

Create a script to handle this automatically:

#!/bin/bash

# Store original DNS
ORIGINAL_DNS=$(networksetup -getdnsservers Wi-Fi | tr '\n' ' ')

function cleanup {
    networksetup -setdnsservers Wi-Fi $ORIGINAL_DNS
    echo "Restored original DNS settings"
}
trap cleanup EXIT

# Connect to VPN
# ... your VPN connection command here ...

# Set split DNS
networksetup -setdnsservers Wi-Fi 192.168.0.1 10.30.16.140

# Set split routing
ORIGINAL_GW=$(netstat -rn | grep default | grep -v utun | awk '{print $2}')
for subnet in 192.168.0.0/16 10.0.0.0/8; do
    route -n add -net $subnet $ORIGINAL_GW
done

For persistent configuration, create a network preference profile:

<?xml version="1.0"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>DNS</key>
    <dict>
        <key>ServerAddresses</key>
        <array>
            <string>192.168.0.1</string>
            <string>10.30.16.140</string>
        </array>
    </dict>
</dict>
</plist>

Save as ~/Library/Preferences/Network/com.mycompany.splitdns.plist


The Juniper Network Connect client typically implements a full-tunnel VPN configuration by default, routing all network traffic through the VPN gateway. This becomes evident when examining these system changes:

# Original resolv.conf
nameserver 192.168.0.1

# After VPN connection
search XXX.com
nameserver 10.30.16.140
nameserver 10.30.8.140

The permission denied error occurs because Juniper modifies file permissions during connection. Try these alternative approaches:

# Method 1: Use tee with sudo
echo "nameserver 192.168.0.1" | sudo tee -a /etc/resolv.conf

# Method 2: Disable resolv.conf modification
sudo chattr +i /etc/resolv.conf  # Make file immutable
# Note: Reverse with chattr -i when needed

For true split tunneling, we need to modify the routing table. Create a script to preserve local routes:

#!/bin/bash

# Save current routes
netstat -rn > ~/original_routes.txt

# Connect to VPN
# (Juniper connection command here)

# Delete default route through VPN
sudo route -n delete default

# Add specific route for VPN resources only
sudo route -n add 10.0.0.0/8 [VPN_GATEWAY_IP]

Configure network service order in macOS:

  1. Open System Preferences → Network
  2. Click the gear icon → Set Service Order
  3. Drag your primary connection above the VPN
  4. This ensures non-VPN traffic uses your regular interface

For a more permanent solution, configure a custom DNS resolver:

# Install dnsmasq
brew install dnsmasq

# Configure /usr/local/etc/dnsmasq.conf
server=/clientdomain.com/10.30.16.140
server=/8.8.8.8

Use pf to create firewall rules for selective routing:

# /etc/pf.conf
table <vpn_networks> { 10.0.0.0/8 }
pass out on en0 route-to (lo0 127.0.0.1) from any to !<vpn_networks>

Use these commands to verify your configuration:

# Check active routes
netstat -rn

# Verify DNS resolution
scutil --dns

# Test traffic routing
traceroute 8.8.8.8