Categories
Guide Networking

A Cloudflare DDNS script that uses an API Token instead of your Global API Key

The script requires three information:

  1. Cloudflare API Token with the necessary permission
  2. Your DNS Zone ID
  3. Your DNS Record name & ID

Creating an API Token

To create the API Token required for this script, head down to My Profile from the top right corner, select API Tokens.

Choose Create Token, and use the Edit zone DNS template. The default template would allow editing any DNS records within your account. To further limit the Token’s permission, you can specify the specific domain name it could modify, and optionally include IP Address Filtering rules, if you happen to know the IP prefixes of the host.

Getting the Zone ID

You can get this dynamically via the script using the DNS name, if your API Token has the relevant read permissions. But I feel resolving that dynamically each time the script is run (i.e. every minute) is not necessary, as the resource ID is a static string that doesn’t change over time.

Head down to your Cloudflare portion, and click on the domain name. The Zone ID is in the URL:

https://dash.cloudflare.com/<Zone ID>/abc.com

The Zone ID is a 32-character hex string.

Getting the Record ID

Getting the Record ID is more involving. If you are using a Cloudflare Partner portal, you can obtain this from the partner site’s URL via a similar method as getting the Zone ID. Otherwise, you will have to do an API query to list all the DNS records in a zone, using the List DNS Records API. It is another 32-character hex string.

Putting everything together

#!/bin/bash

# CHANGE THESE
api_token="<API Token>"
zone="<Zone ID>"
record="<Record ID>"
dns_name="<DDNS name>"

ip=$(curl -s ifconfig.co)
ip_file="ip.txt"
log_file="cloudflare.log"

# LOGGER
log() {
    if [ "$1" ]; then
        echo -e "[$(date)] - $1" >> $log_file
    fi
}

# SCRIPT START
log "Check Initiated"

if ! [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
    message="Fetched IP does not look valid! Quitting"
    log "$message"
    echo -e "$message"
    exit 1 
fi

if [ -f $ip_file ]; then
    old_ip=$(cat $ip_file)
    if [ $ip == $old_ip ]; then
        echo "IP has not changed."
        exit 0
    fi
fi

update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone/dns_records/$record" -H "Authorization: Bearer $api_token" -H "Content-Type: application/json" --data "{\"type\":\"A\",\"name\":\"$dns_name\",\"content\":\"$ip\",\"ttl\":120}")

if [[ $update == *"\"success\":false"* ]]; then
    message="API UPDATE FAILED. DUMPING RESULTS:\n$update"
    log "$message"
    echo -e "$message"
    exit 1
else
    message="IP changed to: $ip"
    echo "$ip" > $ip_file
    log "$message"
    echo -e "$message"
fi

Finally, give the execute (+x) permission to the script, and set a crontab to execute it every 1 or 2 minutes, and you’re good to go!

Leave a Reply