Skip to content

Instantly share code, notes, and snippets.

@chappy84
Last active October 29, 2023 11:10
Show Gist options
  • Save chappy84/9606755 to your computer and use it in GitHub Desktop.
Save chappy84/9606755 to your computer and use it in GitHub Desktop.
CloudFlare Dynamic DNS Shell Script
#!/bin/sh
#
# CloudFlare Dynamic DNS
#
# Updates CloudFlare records with the current public IP address
#
# Takes the same basic arguments as A/CNAME updates in the CloudFlare v4 API
# https://www.cloudflare.com/docs/client-api.html#s5.2
#
# Use with cron jobs etc.
#
# e.g.
#
# manually run:
# cloudflare_dyn_dns.sh -key 404613183ab3971a2118ae5bf03d63e032f9e -email test@example.com -zone example.com -name extra
#
# cronjob entry to run every 5 minutes:
# */5 * * * * /path/to/cloudflare_dyn_dns.sh -key 404613183ab3971a2118ae5bf03d63e032f9e -email test@example.com -zone example.com -name extra >> /path/to/cloudflare_dyn_dns.log
#
# will both set the type A DNS record for extra.example.com to the current public IP address for user test@example.com with the provided API key
key=
email=
zone=
zone_id=
type=A
rec_id=
name=
content=
ttl=1
proxied=false
while [ "$1" != "" ]; do
case $1 in
-key ) shift
key=$1
;;
-email ) shift
email=$1
;;
-zone ) shift
zone=$1
;;
-zone_id ) shift
zone_id=$1
;;
-type ) shift
type=$1
;;
-rec_id ) shift
rec_id=$1
;;
-name ) shift
name=$1
;;
-content ) shift
content=$1
;;
-ttl ) shift
ttl=$1
;;
-proxied ) shift
proxied=$1
;;
* ) echo "unknown parameter $1"
exit 1
esac
shift
done
if [ "$content" = "" ]
then
content=`curl -s http://myexternalip.com/raw`
if [ "$content" = "" ]
then
date
echo "No IP address to set record value with."
exit 1
fi
fi
if [ "$name" = "" ]
then
echo "You must provide the name of the record you wish to change."
exit 1
fi
if [ "$zone" = "" ]
then
echo "You must provide the domain you wish to change."
exit 1
fi
if [ "$name" = "$zone" ]
then
hostname="$name"
else
hostname="$name.$zone"
fi
command -v host > /dev/null 2>&1
if [ "$?" = "1" ]
then
command -v nslookup > /dev/null 2>&1
if [ "$?" = "1" ]
then
echo "Cannot find a way to check existing $type record for $hostname"
exit 1
fi
existing_content=`nslookup -type=$type $hostname | awk -F 'Address: ' 'NR==6 { print $2 }'`
else
existing_content=`host -t $type $hostname | sed -E 's/.+?\s+([^\s]+)$/\1/'`
fi
if [ "$content" = "$existing_content" ]
then
echo "Existing record value $existing_content is the same as provided content $content. Exiting."
exit
fi
if [ "$key" = "" ]
then
echo "You must provide your user API token."
exit 1
fi
if [ "$email" = "" ]
then
echo "You must provide your user email."
exit 1
fi
# Get the zone id for the entry we're trying to change if it's not provided
if [ "$zone_id" = "" ]
then
zone_response_json=`curl -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone" -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H "Content-Type: application/json"`
zone_id=`echo $zone_response_json | sed -E "s/.+\"result\":\[\{\"id\":\"([a-f0-9]+)\"[^\}]+$zone.+/\1/g"`
if [ "$zone_id" = "" ]
then
echo "Cloudflare DNS Zone id could not be found, please make sure it exists"
exit 1
fi
fi
# Get the record id for the entry we're trying to change if it's not provided
if [ "$rec_id" = "" ]
then
rec_response_json=`curl -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?name=$hostname" -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H "Content-Type: application/json"`
rec_id=`echo $rec_response_json | sed -E "s/.+\"result\":\[\{\"id\":\"([a-f0-9]+)\"[^\}]+\"type\":\"$type\"[^\}]+$hostname.+/\1/g"`
if [ "$rec_id" = "" ]
then
echo "Cloudflare DNS Record id could not be found, please make sure it exists"
exit 1
fi
fi
# Update the DNS record
update_response=`curl -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$rec_id" -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H "Content-Type: application/json" --data "{\"id\":\"$rec_id\",\"type\":\"$type\",\"name\":\"$hostname\",\"content\":\"$content\",\"ttl\":$ttl,\"proxied\":$proxied}"`
success_val=`echo $update_response | sed -E "s/.+\"success\":(true|false).+/\1/g"`
if [ "$success_val" = "true" ]
then
echo "Record Updated."
else
echo "Record update failed."
exit 1
fi
@chappy84
Copy link
Author

chappy84 commented Sep 1, 2021

Anyone coming to this, please consider this script "abandonware".
I've not maintained it, or used it myself, in years, instead switching to ddclient. It was a quick hacky script that's purpose can be much better served, as mentioned above, by using ddclient, now that they support cloudflare, which they didn't when I wrote this script.
If I could archive it (like you can projects on github), I would, but they've not ported that feature to gists yet.

N/B: Comments after this will be deleted to make this more obvious, so it's not lost in a sea of comments.

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