Created
May 25, 2026 14:55
-
-
Save alecs/73e7d98e063a14a42b8b9242f975c94f to your computer and use it in GitHub Desktop.
wireguard extra routing for existing tunnel, without routing wildcard 0/0; good especially for hostnames that change destination, like cloudfront/aws-alb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| if [ "$EUID" -ne 0 ]; then | |
| echo "Error: Please run this script using sudo." >&2 | |
| exit 1 | |
| fi | |
| MY_IP="" | |
| ROUTED_IPS="" | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --my-ip) | |
| if [[ -n "$2" && "$2" != -* ]]; then | |
| MY_IP="$2" | |
| shift 2 | |
| else | |
| echo "Error: Argument for $1 is missing." >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| --routed-ips) | |
| if [[ -n "$2" && "$2" != -* ]]; then | |
| ROUTED_IPS="$2" | |
| shift 2 | |
| else | |
| echo "Error: Argument for $1 is missing." >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| *) | |
| echo "Error: Unknown argument: $1" >&2 | |
| echo "Usage: sudo $0 --my-ip <wireguard_ip> --routed-ips <ip1,ip2,ip3>" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| if [ ${#MY_IP} -eq 0 ]; then | |
| echo "Error: --my-ip parameter length is 0. This parameter is required." >&2 | |
| exit 1 | |
| fi | |
| if [ ${#ROUTED_IPS} -eq 0 ]; then | |
| echo "Error: --routed-ips parameter length is 0. This parameter is required." >&2 | |
| exit 1 | |
| fi | |
| OS_TYPE=$(uname -s) | |
| INTERFACE="" | |
| LINK_ID="" | |
| echo "Detecting WireGuard interface assigned to IP: $MY_IP..." | |
| if [ "$OS_TYPE" = "Darwin" ]; then | |
| INTERFACE=$(netstat -rn -f inet | grep "$MY_IP" | awk '{print $NF}' | head -n 1) | |
| LINK_ID=$(netstat -rn -f inet | grep "$INTERFACE" | awk '!/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $2}' | head -n 1) | |
| elif [ "$OS_TYPE" = "Linux" ]; then | |
| INTERFACE=$(ip -o addr show | grep "$MY_IP" | awk '{print $2}' | head -n 1) | |
| fi | |
| if [ ${#INTERFACE} -eq 0 ]; then | |
| echo "Error: Could not find an active tunnel interface for IP $MY_IP." >&2 | |
| exit 1 | |
| fi | |
| echo "Found interface: $INTERFACE" | |
| echo "----------------------------------------" | |
| FINAL_IP_LIST=() | |
| IFS=',' read -r -a RAW_TARGETS <<< "$ROUTED_IPS" | |
| for TARGET in "${RAW_TARGETS[@]}"; do | |
| TARGET=$(echo "$TARGET" | tr -d ' ') | |
| CLEAN_TARGET="${TARGET%%/*}" | |
| if [[ "$CLEAN_TARGET" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| if [[ ! "$TARGET" =~ / ]]; then | |
| TARGET="${TARGET}/32" | |
| fi | |
| FINAL_IP_LIST+=("$TARGET") | |
| else | |
| echo "Resolving FQDN: $TARGET..." | |
| unset TEMP_IPS | |
| while read -r line; do | |
| [ -n "$line" ] && TEMP_IPS+=("$line") | |
| done < <(dig +short "$TARGET" | grep -E '^[0-9.]+$') | |
| if [ ${#TEMP_IPS[@]} -eq 0 ]; then | |
| echo "Warning: Could not resolve any IPs for $TARGET. Skipping." >&2 | |
| continue | |
| fi | |
| for RESOLVED_IP in "${TEMP_IPS[@]}"; do | |
| FINAL_IP_LIST+=("${RESOLVED_IP}/32") | |
| done | |
| fi | |
| done | |
| UNIQUE_IP_LIST=($(printf "%s\n" "${FINAL_IP_LIST[@]}" | sort -u)) | |
| for TARGET_IP in "${UNIQUE_IP_LIST[@]}"; do | |
| echo "Routing $TARGET_IP through $INTERFACE..." | |
| if [ "$OS_TYPE" = "Darwin" ]; then | |
| route -q -n add -net "$TARGET_IP" -link "$LINK_ID" -ifscope "$INTERFACE" | |
| elif [ "$OS_TYPE" = "Linux" ]; then | |
| ip route add "$TARGET_IP" dev "$INTERFACE" | |
| fi | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment