From 4da33ceaedf509afbe703d7c8357ddd2fcc7e520 Mon Sep 17 00:00:00 2001 From: Nik Afiq Date: Tue, 10 Mar 2026 19:50:07 +0900 Subject: [PATCH] Refactor DDNS CronJob to improve DNS record handling and remove duplicates --- manifests/ddns-cronjob.yaml | 67 +++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/manifests/ddns-cronjob.yaml b/manifests/ddns-cronjob.yaml index f626016..9283696 100644 --- a/manifests/ddns-cronjob.yaml +++ b/manifests/ddns-cronjob.yaml @@ -39,10 +39,11 @@ spec: - -c - | set -euo pipefail - apk add --no-cache curl jq bind-tools -q + apk add --no-cache curl jq -q DOMAIN="nik4nao.com" RECORD_NAME="home" + RECORD_FQDN="${RECORD_NAME}.${DOMAIN}" TTL=300 timestamp() { date +"%Y-%m-%d %H:%M:%S%z"; } @@ -55,37 +56,61 @@ spec: fi echo "[$(timestamp)] WAN IP: $WAN_IP" - # Get current DNS record via dig - DNS_IP="$(dig +short ${RECORD_NAME}.${DOMAIN} @1.1.1.1 | head -1)" - echo "[$(timestamp)] DNS IP: $DNS_IP" - - # Skip if unchanged - if [ "$WAN_IP" = "$DNS_IP" ]; then - echo "[$(timestamp)] No change, skipping." - exit 0 - fi - - echo "[$(timestamp)] IP changed $DNS_IP -> $WAN_IP, updating..." - - # Retrieve existing records to get record ID + # Get current DNS records from Porkbun API RECORDS="$(curl -sf -X POST \ "https://api.porkbun.com/api/json/v3/dns/retrieve/${DOMAIN}" \ -H 'Content-Type: application/json' \ -d "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_KEY\"}")" - RECORD_ID="$(echo "$RECORDS" | jq -r \ - --arg name "$RECORD_NAME" \ - '.records[] | select(.type=="A" and .name==$name) | .id' | head -1)" + # Get all A record IDs for this subdomain + RECORD_IDS="$(echo "$RECORDS" | jq -r \ + --arg fqdn "$RECORD_FQDN" \ + '.records[] | select(.type=="A" and .name==$fqdn) | .id')" - if [ -z "$RECORD_ID" ]; then - # Create new record - echo "[$(timestamp)] No existing record, creating..." + # Get the current DNS IP from the first record + DNS_IP="$(echo "$RECORDS" | jq -r \ + --arg fqdn "$RECORD_FQDN" \ + '.records[] | select(.type=="A" and .name==$fqdn) | .content' | head -1)" + + echo "[$(timestamp)] DNS IP: ${DNS_IP:-none}" + + # Delete all stale duplicate records (keep none — we'll create/update cleanly) + RECORD_COUNT="$(echo "$RECORD_IDS" | grep -c . || true)" + if [ "$RECORD_COUNT" -gt 1 ]; then + echo "[$(timestamp)] Found $RECORD_COUNT duplicate records, deleting all..." + for ID in $RECORD_IDS; do + curl -sf -X POST \ + "https://api.porkbun.com/api/json/v3/dns/delete/${DOMAIN}/${ID}" \ + -H 'Content-Type: application/json' \ + -d "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_KEY\"}" > /dev/null + echo "[$(timestamp)] Deleted record ID $ID" + done + DNS_IP="" + fi + + # Get the single remaining record ID (if any) + RECORD_ID="$(echo "$RECORDS" | jq -r \ + --arg fqdn "$RECORD_FQDN" \ + '.records[] | select(.type=="A" and .name==$fqdn) | .id' | head -1)" + + + # Skip if already correct (single record, correct IP) + if [ "$RECORD_COUNT" -le 1 ] && [ "$WAN_IP" = "$DNS_IP" ]; then + echo "[$(timestamp)] No change, skipping." + exit 0 + fi + + echo "[$(timestamp)] IP changed ${DNS_IP:-none} -> $WAN_IP, updating..." + + if [ -z "$RECORD_ID" ] || [ "$RECORD_COUNT" -gt 1 ]; then + # Create fresh record + echo "[$(timestamp)] Creating new record..." RESP="$(curl -sf -X POST \ "https://api.porkbun.com/api/json/v3/dns/create/${DOMAIN}" \ -H 'Content-Type: application/json' \ -d "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_KEY\",\"type\":\"A\",\"name\":\"$RECORD_NAME\",\"content\":\"$WAN_IP\",\"ttl\":$TTL}")" else - # Update existing record + # Update existing single record echo "[$(timestamp)] Updating record ID $RECORD_ID..." RESP="$(curl -sf -X POST \ "https://api.porkbun.com/api/json/v3/dns/edit/${DOMAIN}/${RECORD_ID}" \