Dynamisches DNS Updater für Cloudflare

wen man eine Domain bei Cloudflare hinzugefügt hat und möchte seine Öffentliche Dynamische IP mit der Domain Updaten gibt es ein Script was man per Cron laufen lassen kann, ich habe eine log Funktion hinzugefügt.

Datei Anlegen
cloudflare-ddns.sh

#!/bin/bash
## change to "bin/sh" when necessary

auth_email="meine@email.de"                         # The email used to login 'https://dash.cloudflare.com'
auth_method="token"                                 # Set to "global" for Global API Key or "token" for Scoped API Token
auth_key="cloudflare-authkey-"                      # Your API Token or Global API Key
zone_identifier="cloudflare-zone-identifier"        # Can be found in the "Overview" tab of your domain
record_name="meinedomain.de"                        # Which record you want to be synced 
ttl="300"                                           # Set the DNS TTL (seconds)
proxy="true"                                        # Set the proxy to true or false
sitename=""                                         # Title of site "Example Site"
slackchannel=""                                     # Slack Channel #example
slackuri=""                                         # URI for Slack WebHook "https://hooks.slack.com/services/xxxxx"
discorduri=""                                       # URI for Discord WebHook "https://discordapp.com/api/webhooks/xxxxx"

logfile="/var/log/ddns.log"
###########################################
## Check if we have a public IP
###########################################
ipv4_regex='([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])'
ip=$(curl -s -4 https://cloudflare.com/cdn-cgi/trace | grep -E '^ip'); ret=$?
if [[ ! $ret == 0 ]]; then ## In the case that cloudflare failed to return an ip.
    ## Attempt to get the ip from other websites.
    ip=$(curl -s https://api.ipify.org || curl -s https://ipv4.icanhazip.com)
	## FritzBox IP abfragen ohne externe abfrage, fritzbox-ip.php benötigt auf einem HTTP Server mit PHP
	#ip=$(curl -s http://192.168.178.5:8080/fritzbox-ip.php)
else
    # Extract just the ip from the ip line from cloudflare.
    ip=$(echo $ip | sed -E "s/^ip=($ipv4_regex)$/\1/")
fi

# Use regex to check for proper IPv4 format.
if [[ ! $ip =~ ^$ipv4_regex$ ]]; then
    echo "$(date +'%Y-%m-%d %H:%M:%S') - DDNS Updater: Es konnte keine gültige IP gefunden werden." >> "$logfile"
    exit 2
fi

###########################################
## Check and set the proper auth header
###########################################
if [[ "${auth_method}" == "global" ]]; then
  auth_header="X-Auth-Key:"
else
  auth_header="Authorization: Bearer"
fi

###########################################
## Seek for the A record
###########################################

echo "$(date +'%Y-%m-%d %H:%M:%S') - DDNS Updater: Prüfung eingeleitet" >> "$logfile"
record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?type=A&name=$record_name" \
                      -H "X-Auth-Email: $auth_email" \
                      -H "$auth_header $auth_key" \
                      -H "Content-Type: application/json")

###########################################
## Check if the domain has an A record
###########################################
if [[ $record == *"\"count\":0"* ]]; then
  echo "$(date +'%Y-%m-%d %H:%M:%S') - DDNS Updater: Datensatz existiert nicht, vielleicht zuerst einen erstellen? (${ip} für ${record_name})" >> "$logfile"
  exit 1
fi

###########################################
## Get existing IP
###########################################
old_ip=$(echo "$record" | sed -E 's/.*"content":"(([0-9]{1,3}\.){3}[0-9]{1,3})".*/\1/')
# Compare if they're the same
if [[ $ip == $old_ip ]]; then
  echo "$(date +'%Y-%m-%d %H:%M:%S') - DDNS Updater: IP ($ip) für ${record_name} hat sich nicht geändert." >> "$logfile"
  exit 0
fi

###########################################
## Set the record identifier from result
###########################################
record_identifier=$(echo "$record" | sed -E 's/.*"id":"(\w+)".*/\1/')

###########################################
## Change the IP@Cloudflare using the API
###########################################
update=$(curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" \
                     -H "X-Auth-Email: $auth_email" \
                     -H "$auth_header $auth_key" \
                     -H "Content-Type: application/json" \
                     --data "{\"type\":\"A\",\"name\":\"$record_name\",\"content\":\"$ip\",\"ttl\":\"$ttl\",\"proxied\":${proxy}}")

###########################################
## Report the status
###########################################
case "$update" in
*"\"success\":false"*)
  echo -e "$(date +'%Y-%m-%d %H:%M:%S') - DDNS Updater: $ip $record_name DDNS gescheitert für $record_identifier ($ip). DUMPING RESULTS:\n$update" | >> "$logfile" 
  if [[ $slackuri != "" ]]; then
    curl -L -X POST $slackuri \
    --data-raw '{
      "channel": "'$slackchannel'",
      "text" : "'"$sitename"' DDNS Update Fehlgeschlagen: '$record_name': '$record_identifier' ('$ip')."
    }'
  fi
  if [[ $discorduri != "" ]]; then
    curl -i -H "Accept: application/json" -H "Content-Type:application/json" -X POST \
    --data-raw '{
      "content" : "'"$sitename"' DDNS Update Fehlgeschlagen: '$record_name': '$record_identifier' ('$ip')."
    }' $discorduri
  fi
  exit 1;;
*)
  echo "$(date +'%Y-%m-%d %H:%M:%S') - DDNS Updater: $ip $record_name DDNS Aktualisiert." >> "$logfile"
  if [[ $slackuri != "" ]]; then
    curl -L -X POST $slackuri \
    --data-raw '{
      "channel": "'$slackchannel'",
      "text" : "'"$sitename"' Aktualisiert: '$record_name''"'"'s'""' new IP Address is '$ip'"
    }'
  fi
  if [[ $discorduri != "" ]]; then
    curl -i -H "Accept: application/json" -H "Content-Type:application/json" -X POST \
    --data-raw '{
      "content" : "'"$sitename"' Aktualisiert: '$record_name''"'"'s'""' neue IP-Adresse ist '$ip'"
    }' $discorduri
  fi
  exit 0;;
esac

Eintrag in die crontab, jede Minute wird geprüft ob sich die IP geändert hat und aktualisiert es bei der Domain sollte sie sich geändert haben
*/1 * * * * ddns /root/cloudflare-ddns.sh

wer eine Fritzbox hat und seine Öffentliche IP von ihr abfragen will anstatt über externe Anbieter, der benötigt noch ein php script was auf einem lokalen Webserver läuft, gibt ja z.b. ganz kleine Webserver mit Docker

Datei Anlegen
fritzbox-ip.php

<?php
$soapUrl = 'http://192.168.178.1:49000/igdupnp/control/WANIPConn1';
$soapRequest = '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="." soap:encodingStyle=".">
  <soap:Body>
    <m:GetExternalIPAddress xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1"></m:GetExternalIPAddress>
  </soap:Body>
</soap:Envelope>';

$options = array(
  'http' => array(
    'method' => 'POST',
    'header' => "Content-Type: text/xml; charset=utf-8\r\n" .
                "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress\"\r\n" .
                "Content-Length: " . strlen($soapRequest) . "\r\n",
    'content' => $soapRequest
  )
);

$context = stream_context_create($options);
$result = file_get_contents($soapUrl, false, $context);

preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $result, $matches);
$ipAddress = $matches[0];

echo $ipAddress;

um das Script zu aktivieren müssen ein paar Zeilen auskommentiert werden und die Adresse zur fritzbox-ip.php angepasst

###########################################
## Check if we have a public IP
###########################################
ipv4_regex='([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])'
#ip=$(curl -s -4 https://cloudflare.com/cdn-cgi/trace | grep -E '^ip'); ret=$?
if [[ ! $ret == 0 ]]; then ## In the case that cloudflare failed to return an ip.
    ## Attempt to get the ip from other websites.
    #ip=$(curl -s https://api.ipify.org || curl -s https://ipv4.icanhazip.com)
	## FritzBox IP abfragen ohne externe abfrage, fritzbox-ip.php benötigt auf einem HTTP Server mit PHP
	ip=$(curl -s http://192.168.178.5:8080/fritzbox-ip.php)