blog.thms.uk

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?

  1. A Raspberry Pi (or another Unix machine on your network)
  2. 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.

Setting an A-record

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.