|
#!/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="WGC2" |
|
readonly RULES_FILE="/jffs/openvpn/vpndirector_rulelist" |
|
# 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" |
|
|
|
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. |
|
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Fetching latest Cloudflare IPv4 CIDRs..." |
|
if ! curl -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 CIDRs. |
|
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Extracting existing Cloudflare CIDRs from $RULES_FILE..." |
|
if [ -f "$RULES_FILE" ] && [ -s "$RULES_FILE" ]; then |
|
# Correct parsing: Use '<' as the record separator (RS). |
|
# A rule like "<1>label>>cidr>iface" becomes a record "1>label>>cidr>iface". |
|
# Fields are separated by '>'. The CIDR is the 4th field ($4). |
|
# The label is the 2nd field ($2). We match it against the prefix. |
|
awk -F'>' -v prefix="^${RULE_PREFIX}" 'BEGIN{RS="<"} $2 ~ prefix {print $4}' "$RULES_FILE" > "$OLD_CIDRS_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." |
|
touch "$OLD_CIDRS_FILE" |
|
fi |
|
|
|
# Step 3: Compare the old and new CIDR lists. |
|
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Comparing new and existing CIDR lists..." |
|
sort "$NEW_CIDRS_FILE" -o "$NEW_CIDRS_FILE" |
|
sort "$OLD_CIDRS_FILE" -o "$OLD_CIDRS_FILE" |
|
|
|
if diff -q "$OLD_CIDRS_FILE" "$NEW_CIDRS_FILE" >/dev/null 2>&1; then |
|
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: No changes detected. 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 4: Rebuild the entire rule list. |
|
|
|
# A. Extract all non-Cloudflare rules. |
|
CUSTOM_RULES_BLOCK="" |
|
if [ -f "$RULES_FILE" ] && [ -s "$RULES_FILE" ]; then |
|
# Use the same correct parsing logic to filter out our managed rules. |
|
# ORS="" prevents newlines. We prepend '<' to reassemble the rule correctly. |
|
CUSTOM_RULES_BLOCK=$(awk -F'>' -v prefix="^${RULE_PREFIX}" 'BEGIN{RS="<"; ORS=""} NF>0 && $2 !~ prefix {print "<"$0}' "$RULES_FILE") |
|
fi |
|
|
|
# B. 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}>>${cidr}>${VPN_CLIENT_ID}" |
|
counter=$((counter + 1)) |
|
done < "$NEW_CIDRS_FILE" |
|
|
|
# C. Combine custom rules with the new Cloudflare rules by simple concatenation. |
|
FINAL_RULE_LIST="${CUSTOM_RULES_BLOCK}${NEW_CLOUDFLARE_RULES_BLOCK}" |
|
|
|
# Step 5: 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 |
@AlexeyAM https://github.com/Ranger802004/asusmerlin/tree/main/domain_vpn_routing