Skip to content

Instantly share code, notes, and snippets.

@ispiropoulos
Last active February 6, 2025 13:53
Show Gist options
  • Save ispiropoulos/2394cdd1b3a0cbc348e6571c5d8d5be9 to your computer and use it in GitHub Desktop.
Save ispiropoulos/2394cdd1b3a0cbc348e6571c5d8d5be9 to your computer and use it in GitHub Desktop.
Wireguard checker for OpenWRT to re-resolve ddns domains.

WireGuard DDNS Checker for OpenWRT

Purpose

This script addresses an issue where WireGuard peers fail to re-resolve the domain of their endpoint when the IP address changes.

This script was created to address this limitation by:

  • Checking the last handshake time: If the last handshake is older than a specified threshold (150 seconds in this case), the script triggers a re-resolution of the domain.
  • Re-resolving the domain: It extracts the domain and port from the WireGuard peer configuration and forces WireGuard to update the peer's endpoint with the new resolved IP address.

This ensures that the WireGuard connection remains functional even if the endpoint's IP address changes.

Why This Script Was Created

The wireguard_watchdog script that comes with OpenWrt is incompatible with GL.iNet firmware. Specifically, GL.iNet firmware has certain customizations that prevent the wireguard_watchdog from functioning as expected. To solve this problem, I created this script to provide similar functionality in a way that works reliably on GL.iNet devices, ensuring the WireGuard connection stays active by automatically updating the endpoint whenever the handshake is stale.

This script is intended to be used on OpenWrt-based systems, especially GL.iNet devices, and can be scheduled to run periodically (via cron or the LuCi System -> Scheduled Tasks page).

Usage

  1. Upload the script to your OpenWrt system (eg under /usr/bin/wireguard_checker.

  2. Make the script executable:

    chmod +x /usr/bin/wireguard_checker
  3. Add the script to your OpenWrt crontab or schedule it under LuCi -> System -> Scheduled Tasks. For example, to run the script every minute, you can add the following line to your crontab:

    * * * * * /usr/bin/wireguard_checker

If you have a glinet router, the interface currently for the wireguard client is wgclient. I have uploaded a new version (wireguard_checker_glinet) that does not iterate all the wireguard interfaces.

License

This script is licensed under the MIT License. You are free to use, modify, and distribute it, provided that you give appropriate credit to the original author (Iannis Spiropoulos).

#!/bin/sh
# Maximum allowed handshake time (in seconds)
MAX_HANDSHAKE_AGE=150
LOG_TAG="wireguard_checker"
logger -t $LOG_TAG "Starting Wireguard Checker"
# List all WireGuard interfaces that are up
wg show | grep -E 'interface: ' | awk '{print $2}' | while read iface; do
logger -t $LOG_TAG "Processing interface: $iface"
# Get the peer configuration name from uci for the WireGuard interface
config_name=$(uci get network.$iface.config)
# Get the peer's public key, endpoint, and PersistentKeepalive from uci using the config name
public_key=$(uci get wireguard.$config_name.public_key)
endpoint=$(uci get wireguard.$config_name.end_point)
persistent_keepalive=$(uci get wireguard.$config_name.persistent_keepalive)
if [ -n "$persistent_keepalive" ] && [ -n "$public_key" ] && [ -n "$endpoint" ]; then
logger -t $LOG_TAG "Found peer configuration for interface $iface with name '$config_name', public key '$public_key', endpoint '$endpoint' and persistent keepalive
'$persistent_keepalive'"
# Check if the endpoint is neither an IPv4 nor an IPv6 address using regex
if echo "$endpoint" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
logger -t $LOG_TAG "The endpoint $endpoint is an IPv4 address, skipping checks."
elif echo "$endpoint" | grep -qE '^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$'; then
logger -t $LOG_TAG "The endpoint $endpoint is an IPv6 address, skipping checks."
else
# Extract the domain and port from the endpoint
endpoint_host=$(echo "$endpoint" | cut -d ':' -f 1)
endpoint_port=$(echo "$endpoint" | cut -d ':' -f 2)
# Get the last handshake time for this interface
handshake_time=$(wg show "$iface" latest-handshakes | grep "$public_key" | awk '{print $2}')
if [ -z "$handshake_time" ]; then
logger -t $LOG_TAG "No handshake data for $iface - $config_name, skipping."
continue
fi
# Get the current time and calculate the age of the last handshake
current_time=$(date +%s)
handshake_age=$((current_time - handshake_time))
# Check if the handshake age exceeds the threshold
if [ "$handshake_age" -gt "$MAX_HANDSHAKE_AGE" ]; then
logger -t $LOG_TAG "Handshake for $iface - $config_name ($handshake_age seconds ago) is older than $MAX_HANDSHAKE_AGE seconds, re-resolving domain $endpoin
t."
# Re-set the peer's endpoint, which will force re-resolution
wg set "$iface" peer "$public_key" endpoint "${endpoint_host}:${endpoint_port}"
logger -t $LOG_TAG "Updated endpoint for $iface - $config_name to ${endpoint_host}:${endpoint_port}."
else
logger -t $LOG_TAG "$iface - $config_name handshake is recent ($handshake_age seconds ago), no action needed."
fi
fi
else
logger -t $LOG_TAG "Missing required fields for $iface, skipping processing., skipping domain resolution check."
fi
done
#!/bin/sh
# Logging Tag
LOG_TAG="wireguard_checker"
# Maximum allowed handshake time (in seconds)
MAX_HANDSHAKE_AGE=150
# Define the interface to check
TARGET_IFACE="wgclient"
# Check if the interface exists
if [ "$(uci get network.$TARGET_IFACE 2>/dev/null)" != "interface" ]; then
logger -t $LOG_TAG "$TARGET_IFACE not found, exiting."
exit 0
fi
# Get peer configuration from UCI
config_name=$(uci get network.$TARGET_IFACE.config 2>/dev/null)
public_key=$(uci get wireguard.$config_name.public_key 2>/dev/null)
endpoint=$(uci get wireguard.$config_name.end_point 2>/dev/null)
if [ -z "$public_key" ] || [ -z "$endpoint" ]; then
logger -t $LOG_TAG "Incomplete config for interface $TARGET_IFACE, skipping."
exit 0
fi
# Check if endpoint is a domain
if ! echo "$endpoint" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$|^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$'; then
endpoint_host=$(echo "$endpoint" | cut -d ':' -f 1)
endpoint_port=$(echo "$endpoint" | cut -d ':' -f 2)
# Get handshake time
handshake_time=$(wg show "$TARGET_IFACE" latest-handshakes | grep "$public_key" | awk '{print $2}')
[ -z "$handshake_time" ] && logger -t $LOG_TAG "No handshake data for interface $TARGET_INTERFACE, skipping." && exit 0
# Calculate handshake age
handshake_age=$(( $(date +%s) - handshake_time ))
if [ "$handshake_age" -gt "$MAX_HANDSHAKE_AGE" ]; then
logger -t $LOG_TAG "Re-resolving $endpoint_host ($handshake_age sec old) for interface $TARGET_INTERFACE"
wg set "$TARGET_IFACE" peer "$public_key" endpoint "${endpoint_host}:${endpoint_port}"
else
logger -t $LOG_TAG "$TARGET_IFACE handshake is recent ($handshake_age sec ago), no action needed."
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment