Skip to content

Instantly share code, notes, and snippets.

@radiantly
Forked from lifehome/README.md
Last active November 12, 2021 23:24
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save radiantly/3dbff163624ca3dd32ee8ecf225a7b02 to your computer and use it in GitHub Desktop.
Save radiantly/3dbff163624ca3dd32ee8ecf225a7b02 to your computer and use it in GitHub Desktop.
Cloudflare API v4 Dynamic DNS Update in Bash

Cloudflare DDNS bash client with systemd

This is a bash script to act as a Cloudflare DDNS client, useful replacement for ddclient.

How to use?

  1. Put the cfupdater files to /usr/local/bin
  2. chmod +x /usr/local/bin/cfupdater
  3. Create a systemd service unit at /etc/systemd/system/, the cfupdate.service is shown as an example.
  4. Create a systemd timer unit at the same location of the service unit, the cfupdate.timer is shown as an example.
  5. sudo systemctl enable cfupdate.timer
  6. sudo systemctl start cfupdate.timer

Note

An advantage of using this script over others is that this one supports usage of Cloudflare API tokens. The default cfupdate.timer is set to execute the script hourly.

[Unit]
Description=Cloudflare DDNS service
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cfupdater
[Install]
WantedBy=multi-user.target
[Unit]
Description=Run cfupdate.service hourly
[Timer]
OnCalendar=hourly
[Install]
WantedBy=timers.target
#!/bin/bash
# You can find this script at radiantly/cloudflare-update-record.sh
# Which is an updated fork of lifehome/cloudflare-update-record.sh
# Which was originally forked from benkulbertis/cloudflare-update-record.sh
# CHANGE THESE
auth_token="longishcloudflaretokenthatslongerthanths" # Generate an API token at https://dash.cloudflare.com/profile/api-tokens with the permission dns_records:edit
zone_identifier="f1nd7h3fuck1n6z0n31d3n71f13r4l50" # Can be found in the "Overview" tab of your domain
record_name="ipv4.example.org" # Which record you want to be synced
# DO NOT CHANGE LINES BELOW
ip=$(curl -s https://ipv4.icanhazip.com/)
# SCRIPT START
echo "[Cloudflare DDNS] Check Initiated"
# Seek for the record
record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "Authorization: Bearer $auth_token" -H "Content-Type: application/json")
# Can't do anything without the record
if [[ "$record" == *"\"count\":0"* ]]; then
>&2 echo -e "[Cloudflare DDNS] Record does not exist, perhaps create one first?"
exit 1
fi
# Set existing IP address from the fetched record
old_ip=$(echo "$record" | grep -Po '(?<="content":")[^"]*' | head -1)
# Compare if they're the same
if [ "$ip" == "$old_ip" ]; then
echo "[Cloudflare DDNS] IP has not changed."
exit 0
fi
# Set the record identifier from result
record_identifier=$(echo "$record" | grep -Po '(?<="id":")[^"]*' | head -1)
# The execution of update
update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "Authorization: Bearer $auth_token" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip\"}")
# The moment of truth
case "$update" in
*"\"success\":false"*)
>&2 echo -e "[Cloudflare DDNS] Update failed for $record_identifier. DUMPING RESULTS:\n$update"
exit 1;;
*)
echo "[Cloudflare DDNS] IPv4 context '$ip' has been synced to Cloudflare.";;
esac
@andrewjs18
Copy link

hi,

how can we make this work with more than 1 dns record (subdomains, some proxied, some not, etc.)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment