Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Cloudflare as Dynamic DNS (revised)
#!/bin/bash
# Cloudflare as Dynamic DNS
# From: https://letswp.io/cloudflare-as-dynamic-dns-raspberry-pi/
# Based on: https://gist.github.com/benkulbertis/fff10759c2391b6618dd/
# Original non-RPi article: https://phillymesh.net/2016/02/23/setting-up-dynamic-dns-for-your-registered-domain-through-cloudflare/
# Fixed and documented version by Christian Gambardella (https://gambo.io)
# 1. Create a cloudflare account
# 2. Create a zone and a record with any ip address.
# It will be updated by the script.
# Just put 127.0.0.1 or so.
# 3. Create a token with those permissions:
# zone / zone / read
# zone / DNS / edit
# limit to all zones from your account or all zones. specific zones are not enough.
# 4. Update this script to include your zone name, record name and auth token.
# 5. Put it somewhere
# 6. Install a cronjob using `crontab -e`
# 7. Add line to update every 10 minutes: `*/10 * * * * /home/pi/cron/lwp-cloudflare-dyndns.sh`
# Update these with real values
auth_token="<auth-token-here>"
zone_name="domain.com"
record_name="home.domain.com"
# Don't touch these
ip=$(curl -s http://ipv4.icanhazip.com)
ip_file="ip.txt"
id_file="cloudflare.ids"
log_file="cloudflare.log"
# Keep files in the same folder when run from cron
current="$(pwd)"
cd "$(dirname "$(readlink -f "$0")")"
log() {
if [ "$1" ]; then
echo -e "[$(date)] - $1" >> $log_file
fi
}
log "Check Initiated"
if [ -f $ip_file ]; then
old_ip=$(cat $ip_file)
if [ $ip == $old_ip ]; then
log "IP has not changed."
exit 0
fi
fi
if [ -f $id_file ] && [ $(wc -l $id_file | cut -d " " -f 1) == 2 ]; then
zone_identifier=$(head -1 $id_file)
record_identifier=$(tail -1 $id_file)
else
zone_identifier=$( curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name" -H "Authorization: Bearer $auth_token" -H "Content-Type: application/json" | grep -Po '"id": *\K"[^"]*"' | head -1 | tr -d \" )
record_identifier=$( 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" | grep -Po '(?<="id":")[^"]*' )
echo "$zone_identifier" > $id_file
echo "$record_identifier" >> $id_file
fi
echo "record: $record_identifier"
echo "zone: $zone_identifier"
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\",\"name\":\"$record_name\",\"content\":\"$ip\"}")
if [[ $update == *"\"success\":false"* ]]; then
message="API UPDATE FAILED. DUMPING RESULTS:\n$update"
log "$message"
echo -e "$message"
exit 1
else
message="IP changed to: $ip"
echo "$ip" > $ip_file
log "$message"
echo "$message"
fi
@heygambo

This comment has been minimized.

Copy link
Owner Author

@heygambo heygambo commented Mar 17, 2020

I've debugged this script and made it work using cloudflare tokens:
https://gist.github.com/TheFirsh/c9f72970eaae3aec04beb1106cc304bc#file-lwp-cloudflare-dyndns-sh

Make sure to run rm cloudflare.ids ip.txt when trying to get this script to work.

@congthanhsem

This comment has been minimized.

Copy link

@congthanhsem congthanhsem commented Mar 23, 2020

Dear sir,

How can i update for multi domain... More domain..

Thank you

@heygambo

This comment has been minimized.

Copy link
Owner Author

@heygambo heygambo commented Mar 23, 2020

I've copied and pasted this script and have 2 crontabs running

@veryslothysloth

This comment has been minimized.

Copy link

@veryslothysloth veryslothysloth commented Apr 2, 2020

Hey, the script works like a charm for me, I just have a small problem. I have MX entries in Cloudflare that have the same name as the root record so sometimes it grabs the wrong ID. Would it be possible to edit the grep part of the curl request in a way that it also makes sure that it is a type A Record? I tried to wrap my head around the regex but couldn't figure it out since I have never one this before...

@sander12101

This comment has been minimized.

Copy link

@sander12101 sander12101 commented Apr 27, 2020

Hey there; thank you for the gist! I managed to get it to work but noticed that it would turn off the 'proxied' part on the setting exposing my servers' ip. To fix that I replaced the api call with this:

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\",\"name\":\"$record_name\",\"content\":\"$ip\",\"ttl\":\"1\",\"proxied\":\"true\"}")

With ttl set to 1 (auto)

@heygambo

This comment has been minimized.

Copy link
Owner Author

@heygambo heygambo commented May 15, 2020

Cool stuff! glad it works for you. I copied it from the other script. I've just adjusted it a bit.

I'm using it to host riot on my raspberry pi.

@Chafalleiro

This comment has been minimized.

Copy link

@Chafalleiro Chafalleiro commented Aug 28, 2020

Dear sir,

How can i update for multi domain... More domain..

Thank you

I did a multidomain, multizone mashup of the script using jq, more info in the readme of the repo. Updates A and AAA records based on a list of domains you provide, domains not listed are ignored.

https://github.com/Chafalleiro/Server-scripts/blob/Server-scripts/DYNDNS_for_Cloudflare.sh

@heygambo

This comment has been minimized.

Copy link
Owner Author

@heygambo heygambo commented Sep 16, 2020

🎉

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.