Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
@anshgh

This comment has been minimized.

Copy link

anshgh commented Aug 22, 2014

Thank you!
script works:
[root @ router1 sbin] # /etc/rc.d/cf_eth0.sh
Attempting to get current public IP Address for content.
Record Updated.

But I there are several IP addresses. How can I update a specific (one of the four IP)?

@anshgh

This comment has been minimized.

Copy link

anshgh commented Aug 25, 2014

add "content=" )))

@chappy84

This comment has been minimized.

Copy link
Owner Author

chappy84 commented Aug 31, 2014

Hi @anshgh

apologies for the delayed response.

You are quite correct, using an command line argument of -content 8.8.8.8 will ensure the script ignores the current public IP address and sets the content of the DNS record to 8.8.8.8
The script was originally only intended for boxes with one public IP address, hence defaulting to the public IP address of whichever is the default network adapter

@farhantahirt

This comment has been minimized.

Copy link

farhantahirt commented Jul 25, 2018

got Stuck at line "++ curl -s http://myexternalip.com/raw"

replaces it with this line, to make it work:

curl -s checkip.dyndns.org | sed -e 's/.Current IP Address: //' -e 's/<.$//'

@chappy84

This comment has been minimized.

Copy link
Owner Author

chappy84 commented Aug 4, 2018

Not sure why this isn't working for you @farhantahirt , possibly some network issues? Could investigate with a traceroute?
If myexternalip.com doesn't work, can I suggest using a provider that doesn't require string a replace on the returned content? e.g. https://api.ipify.org/ or http://ipv4.icanhazip.com/

@wsokc

This comment has been minimized.

Copy link

wsokc commented Dec 10, 2018

I've got this

./cloudflare.sh -key <redacted> -email <redacted> -zone mandarin.web.id -name mandarin.web.id
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1713    0  1713    0     0   5023      0 --:--:-- --:--:-- --:--:--  5023
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   135    0   135    0     0    403      0 --:--:-- --:--:-- --:--:--   404
curl: (3) [globbing] nested brace in column 99
Record update failed.
@chappy84

This comment has been minimized.

Copy link
Owner Author

chappy84 commented Mar 18, 2019

@wsokc I edited your comment to redact the key and e-mail as these were valid for your Cloudflare account. May I also suggest you refresh the key via Cloudflare's UI, otherwise you may find some of your DNS records getting modified without your permission for anyone that had a copy.

I've updated the script to deal with the scenario you've provided. The script was originally written to deal with sub-hosts, not with the top level domain record, it should now deal with this scenario (e.g. it could deal with the record for www.example.com, but not for example.com . It should now deal with both.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.