|
|
|
|
@ -1,66 +1,167 @@
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
# Exit if any command fails
|
|
|
|
|
set -e
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
# Directory containing all .env config files
|
|
|
|
|
ENV_DIR="./zones"
|
|
|
|
|
# --- Configuration ---
|
|
|
|
|
: "${ENV_DIR:="./zones"}"
|
|
|
|
|
: "${LOG_DIR:="./logs"}"
|
|
|
|
|
DATE=$(date +%F)
|
|
|
|
|
LOG_FILE="$LOG_DIR/update_${DATE}.log"
|
|
|
|
|
|
|
|
|
|
# Fetch current IP from Mullvad
|
|
|
|
|
echo "[*] Fetching IP info from Mullvad..."
|
|
|
|
|
IP_INFO=$(curl -s https://ipv4.am.i.mullvad.net/json)
|
|
|
|
|
IP=$(echo "$IP_INFO" | jq -r '.ip')
|
|
|
|
|
# --- Ensure log directory exists ---
|
|
|
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
|
|
|
|
|
|
if [[ -z "$IP" ]]; then
|
|
|
|
|
echo "[!] Failed to extract IP address."
|
|
|
|
|
# --- Logging function ---
|
|
|
|
|
log() {
|
|
|
|
|
local message="$1"
|
|
|
|
|
echo "$message" | tee -a "$LOG_FILE"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# --- Check required tools ---
|
|
|
|
|
for cmd in curl jq; do
|
|
|
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
|
|
|
echo "[!] Required tool '$cmd' not found. Please install it." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# --- Fetch current IP from Mullvad ---
|
|
|
|
|
log "[*] Fetching IP from Mullvad..."
|
|
|
|
|
IP_INFO=$(curl -sf https://ipv4.am.i.mullvad.net/json) || {
|
|
|
|
|
log "[!] Failed to fetch IP from Mullvad"
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ! echo "$IP_INFO" | jq -e '.ip' >/dev/null; then
|
|
|
|
|
log "[!] Invalid Mullvad response or missing IP."
|
|
|
|
|
log "$IP_INFO"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo "[*] Current public IP is: $IP"
|
|
|
|
|
echo
|
|
|
|
|
CURRENT_IP=$(echo "$IP_INFO" | jq -r '.ip')
|
|
|
|
|
log "[*] Current public IP is: $CURRENT_IP"
|
|
|
|
|
log ""
|
|
|
|
|
|
|
|
|
|
# Loop through all .env files in ENV_DIR
|
|
|
|
|
for ENV_FILE in "$ENV_DIR"/*.env; do
|
|
|
|
|
echo "[*] Processing config: $ENV_FILE"
|
|
|
|
|
# --- Get list of .env files ---
|
|
|
|
|
shopt -s nullglob
|
|
|
|
|
ENV_FILES=("$ENV_DIR"/*.env)
|
|
|
|
|
|
|
|
|
|
if [[ ${#ENV_FILES[@]} -eq 0 ]]; then
|
|
|
|
|
log "[!] No .env files found in $ENV_DIR"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# --- Process each env file ---
|
|
|
|
|
for ENV_FILE in "${ENV_FILES[@]}"; do
|
|
|
|
|
log "[*] Processing config: $ENV_FILE"
|
|
|
|
|
|
|
|
|
|
# Load environment variables
|
|
|
|
|
set -a
|
|
|
|
|
source "$ENV_FILE"
|
|
|
|
|
set +a
|
|
|
|
|
|
|
|
|
|
# Check required variables
|
|
|
|
|
if [[ -z "$ZONE_ID" || -z "$DNS_RECORD_ID" || -z "$CLOUDFLARE_EMAIL" || -z "$CLOUDFLARE_API_KEY" || -z "$DNS_NAME" ]]; then
|
|
|
|
|
echo "[!] Missing required variables in $ENV_FILE"
|
|
|
|
|
# Validate required vars
|
|
|
|
|
missing_vars=()
|
|
|
|
|
[[ -z "${ZONE_ID:-}" ]] && missing_vars+=("ZONE_ID")
|
|
|
|
|
[[ -z "${DNS_NAME:-}" ]] && missing_vars+=("DNS_NAME")
|
|
|
|
|
[[ -z "${CLOUDFLARE_API_KEY:-}" ]] && missing_vars+=("CLOUDFLARE_API_KEY")
|
|
|
|
|
|
|
|
|
|
if (( ${#missing_vars[@]} )); then
|
|
|
|
|
log "[!] Missing variables in $ENV_FILE: ${missing_vars[*]}"
|
|
|
|
|
log ""
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Make the API request
|
|
|
|
|
echo "[*] Updating Cloudflare record for $DNS_NAME..."
|
|
|
|
|
# --- Check if DNS record exists ---
|
|
|
|
|
log "[*] Checking DNS record for $DNS_NAME..."
|
|
|
|
|
|
|
|
|
|
UPDATE_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$DNS_RECORD_ID" \
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
|
|
|
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
|
|
|
-d '{
|
|
|
|
|
"name": "'$DNS_NAME'",
|
|
|
|
|
"ttl": 3600,
|
|
|
|
|
"type": "A",
|
|
|
|
|
"comment": "Domain verification record",
|
|
|
|
|
"content": "'$IP'",
|
|
|
|
|
"proxied": true
|
|
|
|
|
}')
|
|
|
|
|
DNS_LOOKUP=$(curl -sf -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$DNS_NAME" \
|
|
|
|
|
-H "Authorization: Bearer $CLOUDFLARE_API_KEY" \
|
|
|
|
|
-H "Content-Type: application/json")
|
|
|
|
|
CURL_EXIT_CODE=$?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SUCCESS=$(echo "$UPDATE_RESPONSE" | jq -r '.success')
|
|
|
|
|
|
|
|
|
|
if [[ "$SUCCESS" == "true" ]]; then
|
|
|
|
|
echo "[+] $DNS_NAME updated successfully!"
|
|
|
|
|
else
|
|
|
|
|
echo "[!] Failed to update $DNS_NAME."
|
|
|
|
|
echo "Cloudflare response:"
|
|
|
|
|
echo "$UPDATE_RESPONSE"
|
|
|
|
|
if [[ $CURL_EXIT_CODE -ne 0 ]]; then
|
|
|
|
|
log "[!] Failed to query DNS record for $DNS_NAME (curl exit code $CURL_EXIT_CODE)"
|
|
|
|
|
log "$DNS_LOOKUP"
|
|
|
|
|
log ""
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
RECORD_ID=$(echo "$DNS_LOOKUP" | jq -r '.result[0].id // empty')
|
|
|
|
|
EXISTING_IP=$(echo "$DNS_LOOKUP" | jq -r '.result[0].content // empty')
|
|
|
|
|
|
|
|
|
|
if [[ -z "$RECORD_ID" ]]; then
|
|
|
|
|
log "[!] No existing record found. Creating new A record for $DNS_NAME..."
|
|
|
|
|
|
|
|
|
|
CREATE_RESPONSE=$(curl -sf -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
-H "Authorization: Bearer $CLOUDFLARE_API_KEY" \
|
|
|
|
|
-d "{
|
|
|
|
|
\"name\": \"$DNS_NAME\",
|
|
|
|
|
\"ttl\": 3600,
|
|
|
|
|
\"type\": \"A\",
|
|
|
|
|
\"comment\": \"Domain verification record\",
|
|
|
|
|
\"content\": \"$CURRENT_IP\",
|
|
|
|
|
\"proxied\": true
|
|
|
|
|
}")
|
|
|
|
|
CURL_EXIT_CODE=$?
|
|
|
|
|
|
|
|
|
|
if [[ $CURL_EXIT_CODE -ne 0 ]]; then
|
|
|
|
|
log "[!] curl failed creating DNS record (exit code $CURL_EXIT_CODE)"
|
|
|
|
|
log "$CREATE_RESPONSE"
|
|
|
|
|
log ""
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ $(echo "$CREATE_RESPONSE" | jq -r '.success') == "true" ]]; then
|
|
|
|
|
log "[+] Successfully created DNS record for $DNS_NAME → $CURRENT_IP"
|
|
|
|
|
else
|
|
|
|
|
log "[!] Failed to create DNS record for $DNS_NAME"
|
|
|
|
|
echo "$CREATE_RESPONSE" | tee -a "$LOG_FILE"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
log ""
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# --- If record exists, check if update is needed ---
|
|
|
|
|
if [[ "$EXISTING_IP" == "$CURRENT_IP" ]]; then
|
|
|
|
|
log "[=] No update needed. $DNS_NAME already points to $CURRENT_IP"
|
|
|
|
|
log ""
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# --- Patch the existing record ---
|
|
|
|
|
log "[*] IP has changed: $EXISTING_IP → $CURRENT_IP"
|
|
|
|
|
log "[*] Updating existing DNS record via PATCH..."
|
|
|
|
|
|
|
|
|
|
UPDATE_RESPONSE=$(curl -sf -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
-H "Authorization: Bearer $CLOUDFLARE_API_KEY" \
|
|
|
|
|
-d "{
|
|
|
|
|
\"name\": \"$DNS_NAME\",
|
|
|
|
|
\"ttl\": 3600,
|
|
|
|
|
\"type\": \"A\",
|
|
|
|
|
\"comment\": \"Domain verification record\",
|
|
|
|
|
\"content\": \"$CURRENT_IP\",
|
|
|
|
|
\"proxied\": true
|
|
|
|
|
}")
|
|
|
|
|
CURL_EXIT_CODE=$?
|
|
|
|
|
|
|
|
|
|
if [[ $CURL_EXIT_CODE -ne 0 ]]; then
|
|
|
|
|
log "[!] curl failed updating DNS record (exit code $CURL_EXIT_CODE)"
|
|
|
|
|
log "$UPDATE_RESPONSE"
|
|
|
|
|
log ""
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ $(echo "$UPDATE_RESPONSE" | jq -r '.success') == "true" ]]; then
|
|
|
|
|
log "[+] Successfully updated $DNS_NAME to $CURRENT_IP"
|
|
|
|
|
else
|
|
|
|
|
log "[!] Failed to update $DNS_NAME"
|
|
|
|
|
echo "$UPDATE_RESPONSE" | tee -a "$LOG_FILE"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
log ""
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|