#!/bin/sh # ============================================================================== # VPN Director - Cloudflare CIDR Updater # # This script automatically fetches the latest Cloudflare IPv4 CIDRs, # compares them against the currently configured rules in VPN Director, # and updates the rule list if any changes are detected. # # It correctly parses and generates the concatenated rule format. # Designed for POSIX-compliant shells (e.g., ash on BusyBox). # ============================================================================== # --- Configuration --- readonly RULE_PREFIX="Cloudflare v4_" readonly VPN_CLIENT_ID="WGC1" readonly RULES_FILE="/jffs/openvpn/vpndirector_rulelist" readonly LOCAL_SUBNET="" # (Optional) Specify a subnet (e.g., 192.168.1.0/24) or a single IP address to restrict the Cloudflare rules to a specific local network segment or client. # Leave empty ("") to apply the rules to ALL clients on the router. # Example: LOCAL_SUBNET="192.168.1.0/24" will only route traffic from this subnet through the VPN for Cloudflare IPs. # Use for testing #readonly RULES_FILE="./vpndirector_rulelist" # --- Script Logic --- set -e set -u set -o pipefail readonly TEMP_DIR="/tmp/cf_updater_$$" mkdir "$TEMP_DIR" trap 'rm -rf -- "$TEMP_DIR"' EXIT readonly NEW_CIDRS_FILE="$TEMP_DIR/cloudflare_cidrs_new.txt" readonly OLD_CIDRS_FILE="$TEMP_DIR/cloudflare_cidrs_old.txt" readonly NEW_CF_RULES_FILE="$TEMP_DIR/cloudflare_rules_new.txt" readonly OLD_CF_RULES_FILE="$TEMP_DIR/cloudflare_rules_old.txt" logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Starting Cloudflare CIDR update process." # Step 1: Fetch the latest Cloudflare IPv4 CIDRs. intrfce=$(echo "$VPN_CLIENT_ID" | awk '{print tolower($0)}') logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Fetching latest Cloudflare IPv4 CIDRs..." if ! curl --interface "$intrfce" -s "https://api.cloudflare.com/client/v4/ips" | jq -r '.result.ipv4_cidrs[]' > "$NEW_CIDRS_FILE"; then logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.error "ERROR: Failed to fetch or parse Cloudflare IPs. Exiting." exit 1 fi if [ ! -s "$NEW_CIDRS_FILE" ]; then logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.error "ERROR: Fetched CIDR list is empty. Exiting." exit 1 fi # Step 2: Extract currently configured Cloudflare rules block. logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Extracting existing Cloudflare rules from $RULES_FILE..." if [ -f "$RULES_FILE" ] && [ -s "$RULES_FILE" ]; then # Extract the full Cloudflare rules block (not just CIDRs) awk -F'>' -v prefix="^${RULE_PREFIX}" 'BEGIN{RS="<"; ORS=""} NF>0 && $2 ~ prefix {print "<"$0}' "$RULES_FILE" > "$OLD_CF_RULES_FILE" else logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Rules file does not exist or is empty. Will create it." : > "$OLD_CF_RULES_FILE" fi # Step 3: Generate the new block of Cloudflare rules. NEW_CLOUDFLARE_RULES_BLOCK="" counter=0 while IFS= read -r cidr; do label="${RULE_PREFIX}${counter}" NEW_CLOUDFLARE_RULES_BLOCK="${NEW_CLOUDFLARE_RULES_BLOCK}<1>${label}>${LOCAL_SUBNET}>${cidr}>${VPN_CLIENT_ID}" counter=$((counter + 1)) done < "$NEW_CIDRS_FILE" echo -n "$NEW_CLOUDFLARE_RULES_BLOCK" > "$NEW_CF_RULES_FILE" # Step 4: Compare the new and existing Cloudflare rules blocks. logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Comparing new and existing Cloudflare rules block..." if cmp -s "$OLD_CF_RULES_FILE" "$NEW_CF_RULES_FILE"; then logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: No changes detected in Cloudflare rules. No update necessary. Exiting." exit 0 fi logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Changes detected. Proceeding with rule update." # Step 5: Extract all non-Cloudflare rules. CUSTOM_RULES_BLOCK="" if [ -f "$RULES_FILE" ] && [ -s "$RULES_FILE" ]; then CUSTOM_RULES_BLOCK=$(awk -F'>' -v prefix="^${RULE_PREFIX}" 'BEGIN{RS="<"; ORS=""} NF>0 && $2 !~ prefix {print "<"$0}' "$RULES_FILE") fi # Step 6: Combine custom rules with the new Cloudflare rules by simple concatenation. FINAL_RULE_LIST="${CUSTOM_RULES_BLOCK}${NEW_CLOUDFLARE_RULES_BLOCK}" # Step 7: Atomically update the rule list and apply changes. logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Writing new rules to $RULES_FILE and applying changes." echo -n "$FINAL_RULE_LIST" > "$RULES_FILE.tmp" mv "$RULES_FILE.tmp" "$RULES_FILE" service restart_vpndirector logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "SUCCESS: VPN Director rules updated successfully." exit 0