#!/bin/bash # pdns-ddns - Updates both Infomaniak and PowerDNS with external IP # Speichern als: /usr/local/bin/pdns-ddns # Load config source /etc/powerdns/ddns.conf # IP History file IP_HISTORY_FILE="/var/log/ddns-ip-history.log" # Get external IP EXTERNAL_IP=$(curl -s ifconfig.me) if [[ ! $EXTERNAL_IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then echo "ERROR: Invalid IP address: $EXTERNAL_IP" exit 1 fi echo "$(date): External IP: $EXTERNAL_IP" # Read last known IP LAST_IP="" if [[ -f "$IP_HISTORY_FILE" ]]; then LAST_IP=$(grep "IP changed from" "$IP_HISTORY_FILE" | tail -1 | sed 's/.*to \([0-9.]*\)$/\1/') fi # Log IP change if different if [[ -n "$LAST_IP" && "$LAST_IP" != "$EXTERNAL_IP" ]]; then echo "$(date): IP changed from $LAST_IP to $EXTERNAL_IP" | tee -a "$IP_HISTORY_FILE" echo "$(date): Previous IPs in last 10 changes:" grep "IP changed from" "$IP_HISTORY_FILE" | tail -10 | sed 's/.*from \([0-9.]*\) to \([0-9.]*\)$/Old: \1 -> New: \2/' || echo "No previous changes found" elif [[ -z "$LAST_IP" ]]; then echo "$(date): IP unchanged: $EXTERNAL_IP (first run or no history)" else echo "$(date): IP unchanged: $EXTERNAL_IP" fi # Check and update Infomaniak (external) echo "$(date): Checking Infomaniak A record for $INFOMANIAK_DOMAIN..." CURRENT_RECORD=$(curl -s -H "Authorization: Bearer $INFOMANIAK_TOKEN" \ "https://api.infomaniak.com/2/zones/$INFOMANIAK_DOMAIN/records" | \ jq -r '.data[] | select(.type=="A" and .source==".") | .target' | head -1) if [[ "$CURRENT_RECORD" == "$EXTERNAL_IP" ]]; then echo "$(date): Infomaniak A record already correct: $EXTERNAL_IP" else if [[ -n "$CURRENT_RECORD" ]]; then echo "$(date): Infomaniak A record needs update: $CURRENT_RECORD -> $EXTERNAL_IP" echo "$(date): Infomaniak change: $CURRENT_RECORD -> $EXTERNAL_IP" >> "$IP_HISTORY_FILE" else echo "$(date): Infomaniak A record does not exist, creating with IP: $EXTERNAL_IP" echo "$(date): Infomaniak created: -> $EXTERNAL_IP" >> "$IP_HISTORY_FILE" fi RECORD_ID=$(curl -s -H "Authorization: Bearer $INFOMANIAK_TOKEN" \ "https://api.infomaniak.com/2/zones/$INFOMANIAK_DOMAIN/records" | \ jq -r '.data[] | select(.type=="A" and .source==".") | .id' | head -1) if [[ -z "$RECORD_ID" ]]; then echo "$(date): Creating new Infomaniak A record..." if curl -s -X POST "https://api.infomaniak.com/2/zones/$INFOMANIAK_DOMAIN/records" \ -H "Authorization: Bearer $INFOMANIAK_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"type\": \"A\", \"source\": \".\", \"target\": \"$EXTERNAL_IP\", \"ttl\": $TTL}" > /dev/null; then echo "$(date): SUCCESS: Infomaniak A record created" else echo "$(date): ERROR: Failed to create Infomaniak A record" fi else echo "$(date): Updating Infomaniak A record..." if curl -s -X PUT "https://api.infomaniak.com/2/zones/$INFOMANIAK_DOMAIN/records/$RECORD_ID" \ -H "Authorization: Bearer $INFOMANIAK_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"target\": \"$EXTERNAL_IP\", \"ttl\": $TTL}" > /dev/null; then echo "$(date): SUCCESS: Infomaniak A record updated" else echo "$(date): ERROR: Failed to update Infomaniak A record" fi fi fi # Check and update PowerDNS (internal) echo "$(date): Checking PowerDNS A records..." for domain in $POWERDNS_DOMAINS; do echo "$(date): Checking zone $domain..." # Check if zone exists if ! pdnsutil list-zone "$domain" >/dev/null 2>&1; then echo "$(date): Zone $domain does not exist, creating..." echo "$(date): PowerDNS $domain created: -> $EXTERNAL_IP" >> "$IP_HISTORY_FILE" pdnsutil create-zone "$domain" NEEDS_UPDATE=true else # Check current A record - match actual PowerDNS output format CURRENT_IP=$(pdnsutil list-zone "$domain" 2>/dev/null | grep -E "^${domain}\s+[0-9]+\s+IN\s+A\s+" | awk '{print $5}' | head -1) if [[ "$CURRENT_IP" == "$EXTERNAL_IP" ]]; then echo "$(date): PowerDNS A record for $domain already correct: $EXTERNAL_IP" NEEDS_UPDATE=false else if [[ -n "$CURRENT_IP" ]]; then echo "$(date): PowerDNS A record for $domain needs update: $CURRENT_IP -> $EXTERNAL_IP" echo "$(date): PowerDNS $domain change: $CURRENT_IP -> $EXTERNAL_IP" >> "$IP_HISTORY_FILE" else echo "$(date): PowerDNS A record for $domain does not exist, creating with IP: $EXTERNAL_IP" echo "$(date): PowerDNS $domain created: -> $EXTERNAL_IP" >> "$IP_HISTORY_FILE" fi NEEDS_UPDATE=true fi fi # Update if needed if [[ "$NEEDS_UPDATE" == "true" ]]; then echo "$(date): Updating PowerDNS A record for $domain..." if pdnsutil replace-rrset "$domain" @ A "$TTL" "$EXTERNAL_IP" 2>/dev/null; then echo "$(date): SUCCESS: PowerDNS A record updated for $domain" pdnsutil rectify-zone "$domain" 2>/dev/null pdnsutil increase-serial "$domain" 2>/dev/null else echo "$(date): ERROR: Failed to update PowerDNS A record for $domain" fi fi done echo "$(date): DDNS check/update completed" # Show recent IP history summary if [[ -f "$IP_HISTORY_FILE" ]]; then echo "$(date): Recent changes (last 5):" tail -5 "$IP_HISTORY_FILE" | grep -E "(change:|created:)" || echo "No recent changes" fi