Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@minhng99
Forked from lifehome/README.md
Last active September 16, 2020 12:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save minhng99/e1e0d5dd1215ad388b2763869b88bd07 to your computer and use it in GitHub Desktop.
Save minhng99/e1e0d5dd1215ad388b2763869b88bd07 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
  • If you are using IPv4 for A record, append -v4 to cfupdater in the following systemd service unit.
  • If you are using IPv6 for AAAA record, append -v6 to cfupdater in the following systemd service unit.
  • If you prefer a dual-stack record, append -dualstack to cfupdater in the following systemd service unit.
  1. chmod +x /usr/local/bin/cfupdater
  2. Create a systemd service unit at /etc/systemd/system/, the cfupdate.service is shown as an example.
  3. Create a systemd timer unit at the same location of the service unit, the cfupdate.timer is shown as an example.
  4. sudo systemctl enable cfupdate.timer
  5. sudo systemctl start cfupdate.timer

Note

The default cfupdate.timer is set to execute the script every minute.
Please keep in mind not to spam the API or you will be rate limited.

The dual-stack script has NOT been tested, use with caution. The dual-stack script will always sync upon either IPv4 or IPv6 has changed.

A quote from Cloudflare FAQ:

All calls through the Cloudflare Client API are rate-limited to 1200 every 5 minutes.

-- https://support.cloudflare.com/hc/en-us/articles/200171456

[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 every minute
[Timer]
OnCalendar=*:0/1
#!/bin/bash
# Forked by benkulbertis/cloudflare-update-record.sh
# CHANGE THESE
auth_email="john.appleseed@example.org" # The email used to login 'https://dash.cloudflare.com'
auth_key="f1nd7h47fuck1n6k3y1ncl0udfl4r3c0n50l3" # Top right corner, "My profile" > "Global API Key"
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
ip4=$(curl -s https://ipv4.icanhazip.com/)
ip6=$(curl -s https://ipv6.icanhazip.com/)
# SCRIPT START
echo "[Cloudflare DDNS] Check Initiated"
# Seek for the record
update4=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip4\",\"ttl\":120}")
update6=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"AAAA\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip6\",\"ttl\":120}")
# Can't do anything without both record
if [[ $record4 == *"\"count\":0"* || $record6 == *"\"count\":0"* ]]; then
>&2 echo -e "[Cloudflare DDNS] Dual stack records do not exist, perhaps create them first?"
exit 1
fi
# Set existing IP address from the fetched record
old_ip4=$(echo "$record4" | grep -Po '(?<="content":")[^"]*' | head -1)
old_ip6=$(echo "$record6" | grep -Po '(?<="content":")[^"]*' | head -1)
# Compare either one is the same
# NOTE: The script will update even one IP remains the same.
if [[ $ip4 == $old_ip4 && $ip6 == $old_ip6 ]]; then
echo "[Cloudflare DDNS] IPs have not changed."
exit 0
fi
# Set the record identifier from result
record4_identifier=$(echo "$record4" | grep -Po '(?<="id":")[^"]*' | head -1)
record6_identifier=$(echo "$record6" | grep -Po '(?<="id":")[^"]*' | head -1)
# The execution of update
update4=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record4_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip4\",\"ttl\":120}")
update6=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record6_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"AAAA\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip6\",\"ttl\":120}")
# The moment of truth
if [[ $update4 == *"\"success\":false"* || $update6 == *"\"success\":false"* ]]; then
>&2 echo -e "[Cloudflare DDNS] Update failed. DUMPING RESULTS:\n$update4\n$update6"
exit 1
else
echo "[Cloudflare DDNS] IPv4 address '$ip4' and IPv6 address '$ip6' has been synced to Cloudflare."
fi
#!/bin/bash
# Forked by benkulbertis/cloudflare-update-record.sh
# CHANGE THESE
auth_email="john.appleseed@example.org" # The email used to login 'https://dash.cloudflare.com'
auth_key="f1nd7h47fuck1n6k3y1ncl0udfl4r3c0n50l3" # Top right corner, "My profile" > "Global API Key"
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 "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -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 "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"A\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip\",\"ttl\":120}")
# 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
#!/bin/bash
# Forked by benkulbertis/cloudflare-update-record.sh
# CHANGE THESE
auth_email="john.appleseed@example.org" # The email used to login 'https://dash.cloudflare.com'
auth_key="f1nd7h47fuck1n6k3y1ncl0udfl4r3c0n50l3" # Top right corner, "My profile" > "Global API Key"
zone_identifier="f1nd7h3fuck1n6z0n31d3n71f13r4l50" # Can be found in the "Overview" tab of your domain
record_name="ipv6.example.org" # Which record you want to be synced
# DO NOT CHANGE LINES BELOW
ip=$(curl -s https://ipv6.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 "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -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." | systemd-cat -p notice
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 "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"id\":\"$zone_identifier\",\"type\":\"AAAA\",\"proxied\":false,\"name\":\"$record_name\",\"content\":\"$ip\",\"ttl\":120}")
# 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] IPv6 address '$ip' has been synced to Cloudflare.";;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment