Skip to content

Instantly share code, notes, and snippets.

@mhabedinpour
Last active February 15, 2022 19:42
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mhabedinpour/53cda88effbd997e2e271ae48cf9c143 to your computer and use it in GitHub Desktop.
Save mhabedinpour/53cda88effbd997e2e271ae48cf9c143 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
# !!!ONLY FOR MACOS!!!
# This script helps you to bypass VPN for specific CIDRs and domain names by adding a direct route to the default gateway for them.
# It also can be used to setup a killswitch. When killswitch is enabled, Only IP traffic to the VPN server (and other bypassed CIDRs) is allowed, All other packets would be dropped.
# Killswitch is powered by MacOS internal packet filter (PF) firewall.
# How to use?
# Download and save the script.
# Make it executable: sudo chmod +x /path/to/script.sh
# Update configuration parameters below.
# Either password-less sudo or run-as-root is required.
# Run the script: /path/to/script.sh
# If you want to remove all added routes and disable killswitch: /path/to/script.sh -c
# If you want to manually disable killswitch: sudo pfctl -d
# Configuration
default_interface="en0" # Update your default interface here, Use ifconfig to find your default interface. If using WiFi, It's usually "en0".
vpn_interfaces=("utun0" "utun1" "utun2" "utun3" "utun4" "utun5" "utun6" "utun7" "utun8" "utun9" "utun10" "ipsec0" "ipsec1") # List of interfaces used by your VPN service. Use ifconfig to find them. (Important only when using killswitch.)
dns_resolver="1.1.1.1" # DNS resolver for mapping domain names to IP addresses.
websites=("lahzenegar.com" "stdn.iau.ac.ir") # Domains to bypass VPN.
ip_addresses=("192.168.1.0/24" "192.168.0.0/24" "192.168.8.0/24") # CIDRs to bypass VPN. If you want to use killswitch, Add VPN server IP address here as well.
enable_ks=true # Whether to enable killswitch or not. Use "true" to enable it, Anything else to disable it.
clean_only=false
if getopts "c" arg; then
clean_only=true
fi
gateway=$(netstat -nr | grep default | grep ${default_interface} | awk '{print $2}' | head -1)
echo -e "Gateway: ${gateway}\n----------"
echo -e "Resolving Domains...\n----------"
for website in "${websites[@]}"; do
for ip_address in $(dig @${dns_resolver} +short "${website}" | grep '^[.0-9]*$'); do
if ! (printf '%s\n' "${ip_addresses[@]}" | grep -q "${ip_address}"); then
ip_addresses+=("${ip_address}/32")
fi
done
done
for ip_address in "${ip_addresses[@]}"; do
sudo route delete "${ip_address}" || true
if [ "$clean_only" = false ]; then
sudo route -n add -net "${ip_address}" "${gateway}"
fi
echo "----------"
done
if [ "$enable_ks" = true ] && [ "$clean_only" = false ]; then
pf_conf=$(cat <<-END
set block-policy drop
set ruleset-optimization basic
set skip on lo0
block all
block out quick inet6 all
pass from any to 255.255.255.255 keep state
pass from 255.255.255.255 to any keep state
pass proto udp from any to 224.0.0.0/4 keep state
pass proto udp from 224.0.0.0/4 to any keep state
pass on ${default_interface} proto {tcp,udp} from any port 67:68 to any port 67:68 keep state
pass from any to ${dns_resolver}
END
)
for vpn_interface in "${vpn_interfaces[@]}"; do
pf_conf="${pf_conf}\n pass on ${vpn_interface} all"
done
for ip_address in "${ip_addresses[@]}"; do
pf_conf="${pf_conf}\n pass from any to ${ip_address}"
done
echo -e "Starting Killswitch...\n----------"
echo -e "${pf_conf}" > ~/pf_ks.conf
sudo pfctl -Fa -f ~/pf_ks.conf -e || true
else
sudo pfctl -d || true
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment