#!/bin/bash # DDNS updater — keeps jongsma.me A record current in Cloudflare # Runs every 5 min via cron; only calls CF API when IP has changed ZONE_ID="60b98ddacd2a61df9ac36b39acfe8b2c" RECORD_NAME="jongsma.me" STATE_FILE="/tmp/ddns-last-ip" source /home/johan/.config/cloudflare.env # Get current public IP CURRENT_IP=$(curl -sf --max-time 5 https://api.ipify.org || curl -sf --max-time 5 https://ifconfig.me) if [ -z "$CURRENT_IP" ]; then echo "$(date -u): Failed to get public IP" >&2 exit 1 fi # Compare to last known LAST_IP=$(cat "$STATE_FILE" 2>/dev/null) if [ "$CURRENT_IP" = "$LAST_IP" ]; then exit 0 fi echo "$(date -u): IP changed: $LAST_IP -> $CURRENT_IP" # Get record ID RECORD_ID=$(curl -sf \ "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=$RECORD_NAME" \ -H "Authorization: Bearer $CF_API_TOKEN" | \ python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')") if [ -z "$RECORD_ID" ]; then echo "$(date -u): Record not found for $RECORD_NAME" >&2 exit 1 fi # Update record RESULT=$(curl -sf -X PUT \ "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"type\":\"A\",\"name\":\"$RECORD_NAME\",\"content\":\"$CURRENT_IP\",\"ttl\":1,\"proxied\":false}") if echo "$RESULT" | python3 -c "import sys,json; sys.exit(0 if json.load(sys.stdin).get('success') else 1)"; then echo "$(date -u): Updated $RECORD_NAME -> $CURRENT_IP" echo "$CURRENT_IP" > "$STATE_FILE" else echo "$(date -u): CF API error: $RESULT" >&2 exit 1 fi