Using CloudFlare with a Raspberry Pi for Dynamic DNS
I've got a VPN server running on my Raspberry Pi at home, and because I'm on a domestic internet connection here in the UK, my IP address changes from time to time.
Because of that, I instruct my VPN clients to connect via a hostname, and use a simple bash script that I run on my Raspberry Pi, together with CloudFlare's API, to make sure that my host name always points to the correct IP address.
Pre-requisites
So, what do you need?
- A Raspberry Pi (or another Unix machine on your network)
- A domain using CloudFlare's name servers
That's it.
Setting up CloudFlare
Firstly, you need to set up a DNS record with a random IP address: I'll be using ddns.thms.uk
going forwards, so wherever you see this, replace it. You'll probably want to turn off proxying for it, too, although that depends on your use case.
Secondly, get an API token by going to My Profile > API Tokens > Create Token. Choose the 'Edit zone DNS' template, and create your token. Make sure you take a note of it, because you won't see it again.
Finally, get your Zone ID: This is in the bottom right of your zone's dashboard.
Preparing your Pi
To get started you need to install jq
which is a bash utility to read JSON:
$ sudo apt install jq
Create the script that will update your DNS record
Create a file called update-dns.sh
with the following content:
#!/bin/bash
DNS_ZONE_ID='xxx' # your dns zone ID
API_KEY='yyy' # your API Token created previously
HOST_NAME='ddns.thms.uk' # Your DNS record host name
#############################################
### Don't change anything below this line ###
#############################################
# get current external ip address
CURRENT_IP_ADDRESS=$(curl -s ip.me)
# get DNS Record
DNS_RECORD=$(curl -sX GET "https://api.cloudflare.com/client/v4/zones/${DNS_ZONE_ID}/dns_records/?name=${HOST_NAME}" -H "Authorization: Bearer ${API_KEY}" | jq -r '.result[] | select(.name == "'${HOST_NAME}'")')
DNS_RECORD_ID=$(echo $DNS_RECORD | jq -r '.id')
CURRENT_DNS_VALUE=$(echo $DNS_RECORD | jq -r '.content')
# if IP address has changed: update it
if [ ${CURRENT_DNS_VALUE} != ${CURRENT_IP_ADDRESS} ]; then
curl -sX PUT "https://api.cloudflare.com/client/v4/zones/${DNS_ZONE_ID}/dns_records/${DNS_RECORD_ID}" -H "Authorization: Bearer ${API_KEY}" -H "Content-Type:application/json" --data '{"type":"A","name":"'${HOST_NAME}'","content":"'${CURRENT_IP_ADDRESS}'"}' > /dev/null
fi
Give that script execute permissions, and execute it:
$ chmod +x update-dns.sh
$ ./update-dns.sh
Now log back into the CloudFlare dashboard, and check that the IP Address has updated.
Enable cron job
Finally, enable a cron job, that runs this script hourly, or at whatever frequency suits you:
0 * * * * /home/pi/update-dns.sh
And that's it - a simple bash script and a cron job, without installing a full tool kit.