-
-
Save chappy84/9606755 to your computer and use it in GitHub Desktop.
#!/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 [email protected] -zone example.com -name extra | |
# | |
# cronjob entry to run every 5 minutes: | |
# */5 * * * * /path/to/cloudflare_dyn_dns.sh -key 404613183ab3971a2118ae5bf03d63e032f9e -email [email protected] -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 [email protected] 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 |
@0neday Regular expressions shouldn't be used to parse JSON (similar to the reasons why you shouldn't parse X/HTML with RegEx). This script is a perfect example of why not, as it relies on the order of the properties in the object, which the JSON spec specifically defines as incorrect. If cloudflare have switched / do switch to a server-side technology that doesn't always output the properties in that specific order, then this will break.
As in my last response, by hacking around with that regex, you're masking the issue, and potentially creating a far worse issue by updating the wrong records. Please consider using a much better solution, i.e. not bash, and use something like ddclient
instead.
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.
@chappy84 thank you for reply. I use your scripts cannot get
record_id
, I just do a little of optimization. thanks again for your method that usesed
to parsejson
.