Dynamic DNS on Cloudflare

Dynamic DNS on Cloudflare with your own domain name and a little help of Bash script

Spread the love

Overview

Sometimes available ready-to-use solutions for dynamic DNS are not enough for your needs, or need to be customised, or requires some additional actions every month (i.e. confirm the hostname, like on free account of no-ip.com).

If you have your own domain name you could register it in cloudflare.com (it is free) and use the capability of cloudflare for Dynamic DNS.

In my case I used my domain name, registered in cloudflare (free account), used cloudflare’s NSs (DNS records of my domain name are managed in cloudflare.com) and small bash script which is sending the HTTP PUT request to cloudflare every time when the IP address of the host (server, where the domain name should be pointed to) is changing.

What is Dynamic DNS?

Dynamic DNS is the service which is just updating DNS A record of your domain name (it can actually do much more), which is usually IPv4 (A record) or/and IPv6 (AAAA record).

Dynamic DNS is used when you don’t have a static (or white) IP address but you need to be able to reach your host (home laptop, router, NAS or server) remotely, from internet.

Most of the WiFi routers and other network devices have the option to “ping” the Dynamic DNS when the public (internet) IP of your device/host is changed (i.e. the router is restarted and your network provider granted you a new IP address). These settings may sit under the network, security or remote access section.

How?

To make your own mini Dynamic DNS you will need:

  1. Identify your new IP address
  2. Generate cloudflare API token in cloudflare admin panel
  3. Identify your cloudflare zone ID, cloudflare host DNS record ID
  4. Send a HTTP PUT request to cloudflare API to update the DNS A (or AAAA) record(s)

In my case I will be determining the IP address from the Google Cloud Compute Engine VM using the google metadata URL, and here is a bash script sample:

#!/bin/bash

############################################################
# Updating Ephemeral external IP address to Cloudflare DNS #
############################################################

metadata_external_ip_url=http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip
cf_api_token=<your_cloudflare_token>
cf_zone=<your_zone>
cf_dns_record=<your_host>
cf_zone_api_url=https://api.cloudflare.com/client/v4/zones

# Get the ephemeral external IP address from Metadata
echo "Updating A DNS RECORD for ${cf_dns_record} on Cloudflare ..."
external_ip=$(curl -H "Metadata-Flavor: Google" ${metadata_external_ip_url})
echo "Current VM instance external ephemeral IP is ${external_ip}"

if host $cf_dns_record 1.1.1.1 | grep "has address" | grep $external_ip; then
  echo "${cf_dns_record} is currently set to ${external_ip}. No change needed."
else
  cf_zone_id=$(curl -s -X GET "${cf_zone_api_url}?name=${cf_zone}&status=active" \
    -H "Authorization: Bearer ${cf_api_token}" \
    -H "Content-Type: application/json" | jq -r '{"result"}[] | .[0] | .id')

  cf_dns_record_id=$(curl -s -X GET "${cf_zone_api_url}/${cf_zone_id}/dns_records?type=A&name=${cf_dns_record}" \
    -H "Authorization: Bearer ${cf_api_token}" \
    -H "Content-Type: application/json" | jq -r '{"result"}[] | .[0] | .id')

  curl -s -X PUT "${cf_zone_api_url}/${cf_zone_id}/dns_records/${cf_dns_record_id}" \
    -H "Authorization: Bearer ${cf_api_token}" \
    -H "Content-Type: application/json" \
    --data "{\"type\":\"A\",\"name\":\"${cf_dns_record}\",\"content\":\"${external_ip}\",\"ttl\":1,\"proxied\":false}" | jq .

  echo "DNS Record A is updated for ${cf_dns_record}"
fi

As you can see in the example above I used such utilities as curl, host, jq and grep. Some of it may not be available in your linux distro, so install it before you run this script.

How to verify?

To be able to check whether the script worked out you can check the DNS A record of your host name, if it is updated – all good, if not – check the output of the script.

In my case I put this script as a startup script of my GCP compute engine vm and used the following command to see the script output (when connected to my VM via SSH):

sudo journalctl -u google-startup-scripts.service

And I see something like this:

Starting google-startup-scripts.service - Google Compute Engine Startup Scripts...
Starting startup scripts (version dev).
Found startup-script in metadata.
Updating A DNS RECORD for <your_host> on Cloudflare ...
Current VM instance external ephemeral IP is <your_IP_address>
{
   "result": {
     "id": "<your_id>",
     "zone_id": "your_zone_id",
     "zone_name": "<your_zone>",
     "name": "<your_host>",
     "type": "A",
     "content": "<your_IP_address>",
     "proxiable": true,
     "proxied": false,
     "ttl": 1,
     "locked": false,
     "meta": {
       "auto_added": false,
       "managed_by_apps": false,
       "managed_by_argo_tunnel": false,
       "source": "primary"
     },
     "comment": null,
     "tags": [],
     "created_on": "2023-04-11T17:32:06.167473Z",
     "modified_on": "2023-04-12T14:18:17.263299Z"
   },
   "success": true,
   "errors": [],
   "messages": []
}
DNS Record A is updated for <your_host>
startup-script exit status 0
Finished running startup scripts.
Deactivated successfully.
Finished google-startup-scripts.service - Google Compute Engine Startup Scripts.
google-startup-scripts.service: Consumed 0min 7.546s CPU time.

Which says that the new IP address has been successfully updated to cloudflare DNS A record of my host.