Having just wrapped a post on keeping FreeIPA DNS records correctly reflecting a GCP instance’s dynamic IP address, I figured I would do the same for Cloudflare DNS since I use both. This will be very to-the-point, so post a comment or send an email if any clarification is needed. In a nutshell, this is how I approached keeping my Cloudflare DNS record for gcloud.cthudson.com up to date given my free-tier e2-micro instance having a dynamic IP address.

Google Cloud CLI

First off, install the google-cloud-cli tool. OS-dependent docs over at Google can be found here. I happen to be running Fedora on this instance and went the generic Linux route (for some reason, although there is a dnf route as well):

Pull down archive with wget

$ wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-455.0.0-linux-x86_64.tar.gz
Note: The above version was the latest at the time of this writing.

Extract archive

$ tar xzvf google-cloud-cli-455.0.0-linux-x86_64.tar.gz

Install

$ ./google-cloud-sdk/install.sh

Initialize gcloud

$ ./google-cloud-sdk/bin/gcloud init
Note: This step is going to be different per environment, but it's a wizard and pretty self-explanatory.

Test

$ ./google-cloud-sdk/bin/gcloud compute instances list
NAME             ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP   STATUS
fedora-e2-micro  us-east1-b  e2-micro                   10.142.0.2   34.73.48.111  RUNNING

The Script

Now that we have the gcloud cli tool up and running, we can use that to determine our instances public IP to compare against what’s in Cloudflare and updating if necessary (I named this script gcp_cloudflare_dns_updater.sh for reference):

#!/bin/bash

LOCALIP=`./google-cloud-sdk/bin/gcloud compute instances describe fedora-e2-micro | grep natIP | sed s/"natIP: "//g | xargs`

ZONEID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=cthudson.com&status=active" \
  -H "X-Auth-Email: <email>" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" | grep id | cut -d'"' -f6)

DNSRECORDID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONEID/dns_records?type=A&name=gcloud.cthudson.com" \
  -H "X-Auth-Email: <email>" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" | grep id | cut -d'"' -f6)

CURRENTDNSIP=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONEID/dns_records?type=A&name=gcloud.cthudson.com" \
  -H "X-Auth-Email: <email>" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" | grep id | cut -d'"' -f26)

if [[ "$LOCALIP" == "$CURRENTDNSIP" ]]; then
        /usr/bin/logger "VM natIP matched Cloudflare gcloud.cthudson.com record. No update necessary."
        exit
else
curl --request PUT \
  --url https://api.cloudflare.com/client/v4/zones/$ZONEID/dns_records/$DNSRECORDID \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Email: <email>' \
  --header 'Authorization: Bearer <token>' \
  --data '{
  "content": "'"$LOCALIP"'",
  "name": "gcloud.cthudson.com",
  "proxied": false,
  "type": "A",
  "comment": "Domain verification record",
  "ttl": 3600
}'
/usr/bin/logger "VM natIP did not match Cloudflare gcloud.cthudson.com record. Updated."
fi

Script Callouts

  1. Change domain references to your domain, of course.
  2. X-Auth-Email is the email (login) associated with the Cloudflare account managing your blog domain.
  3. The Bearer <token> is created via the Cloudfare admin panel and needs the DNS:Edit permission for DNS zone. Details can be found here.
  4. If the log messages get too chatty, remove or comment out one or both of the logger lines.

Running

In my case, I rely on cron to perform a check every 5 minutes by placing the following in a file under /etc/cron.d/dns:

*/5 * * * * <user> /path/to/gcp_cloudflare_dns_updater.sh

Cheers!