cmc/cleberg.net

My personal web garden & blog.

clone: git clone https://gitbay.org/cmc/cleberg.net.git

main: content/blog/2022-03-23-cloudflare-dns-api.org · raw

  1#+date:        [2022-03-23 Wed 00:00:00]
  2#+title:       Dynamic DNS Updates via the Cloudflare API
  3#+description: Automatically updating DNS A and AAAA records via the Cloudflare API.
  4#+slug:        cloudflare-dns-api
  5#+filetags:    :security:
  6
  7* DDNS: Dynamic DNS
  8:PROPERTIES:
  9:CUSTOM_ID: ddns-dynamic-dns
 10:END:
 11If you're hosting a service from a location with DDNS (Dynamic Domain Name
 12System), where your internet protocol (IP)address may change at any time, you
 13must have a solution to update the DNS (Domain Name System) so that you can
 14access your service even when the IP of the server changes.
 15
 16The process below uses the [[https://api.cloudflare.com/][Cloudflare API]] (application programming interface) to
 17update DNS =A= and =AAAA= records with the server's current IP. If you use
 18another DNS provider, you will have to find a way to update your DNS (or find a
 19way to get a static IP).
 20
 21First, install =jq= since we will use it in the next script:
 22
 23#+begin_src sh
 24sudo apt install jq
 25#+end_src
 26
 27Next, create a location for your DDNS update scripts and open the first script:
 28
 29#+begin_src sh
 30mkdir ~/ddns
 31nano ~/ddns/update.sh
 32#+end_src
 33
 34The following =update.sh= script will take all of your domains and subdomains
 35and check Cloudflare to see if the current =A= and =AAAA= records match your
 36server's IP address. If not, it will update the records.
 37
 38#+begin_src sh
 39# file: update.sh
 40#!/bin/bash
 41
 42# Update TLDs
 43domains=(example.com example.net)
 44
 45for domain in "${domains[@]}"
 46do
 47  echo -e "\nUpdating $domain..."
 48  zone_name=$domain /home/<your-username>/ddns/ddns.sh
 49done
 50
 51# Update subdomains for example.com
 52domain=example.com
 53subdomains=(photos.example.com)
 54
 55for subdomain in "${subdomains[@]}"
 56do
 57  echo -e "\nUpdating $subdomain..."
 58  zone_name=$domain  dns_record=$subdomain /home/<your-username>/ddns/ddns.sh
 59done
 60#+end_src
 61
 62Next, open up the =ddns.sh= script. Paste the following into the script and
 63update the =api_token= and =email= variables.
 64
 65#+begin_src sh
 66nano ~/ddns/ddns.sh
 67#+end_src
 68
 69*Note*: If you want your DNS records to be proxied through Cloudflare, find and
 70update the following snippet: ="proxied":false}"= to say =true= instead of
 71=false=.
 72
 73#+begin_src sh
 74# file: ddns.sh
 75#!/bin/bash
 76# based on https://gist.github.com/Tras2/cba88201b17d765ec065ccbedfb16d9a
 77# initial data; they need to be filled by the user
 78## API token
 79api_token=<YOUR_API_TOKEN>
 80## email address associated with the Cloudflare account
 81email=<YOUR_EMAIL>
 82
 83# get the basic data
 84ipv4=$(curl -s -X GET -4 https://ifconfig.co)
 85ipv6=$(curl -s -X GET -6 https://ifconfig.co)
 86user_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
 87               -H "Authorization: Bearer $api_token" \
 88               -H "Content-Type:application/json" \
 89          | jq -r '{"result"}[] | .id'
 90         )
 91
 92echo "Your IPv4 is: $ipv4"
 93echo "Your IPv6 is: $ipv6"
 94
 95# check if the user API is valid and the email is correct
 96if [ $user_id ]
 97then
 98    zone_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$zone_name&status=active" \
 99                   -H "Content-Type: application/json" \
100                   -H "X-Auth-Email: $email" \
101                   -H "Authorization: Bearer $api_token" \
102              | jq -r '{"result"}[] | .[0] | .id'
103             )
104    # check if the zone ID is
105    if [ $zone_id ]
106    then
107        # check if there is any IP version 4
108        if [ $ipv4 ]
109        then
110            dns_record_a_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=A&name=$dns_record"  \
111                                   -H "Content-Type: application/json" \
112                                   -H "X-Auth-Email: $email" \
113                                   -H "Authorization: Bearer $api_token"
114                             )
115            # if the IPv6 exist
116            dns_record_a_ip=$(echo $dns_record_a_id |  jq -r '{"result"}[] | .[0] | .content')
117            echo "The set IPv4 on Cloudflare (A Record) is:    $dns_record_a_ip"
118            if [ $dns_record_a_ip != $ipv4 ]
119            then
120                # change the A record
121                curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$(echo $dns_record_a_id | jq -r '{"result"}[] | .[0] | .id')" \
122                     -H "Content-Type: application/json" \
123                     -H "X-Auth-Email: $email" \
124                     -H "Authorization: Bearer $api_token" \
125                     --data "{"type":"A","name":"$dns_record","content":"$ipv4","ttl":1,"proxied":false}" \
126                | jq -r '.errors'
127            else
128                echo "The current IPv4 and DNS record IPv4 are the same."
129            fi
130        else
131            echo "Could not get your IPv4. Check if you have it; e.g. on https://ifconfig.co"
132        fi
133
134        # check if there is any IP version 6
135        if [ $ipv6 ]
136        then
137            dns_record_aaaa_id=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records?type=AAAA&name=$dns_record"  \
138                                      -H "Content-Type: application/json" \
139                                      -H "X-Auth-Email: $email" \
140                                      -H "Authorization: Bearer $api_token"
141                                )
142            # if the IPv6 exist
143            dns_record_aaaa_ip=$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .content')
144            echo "The set IPv6 on Cloudflare (AAAA Record) is: $dns_record_aaaa_ip"
145            if [ $dns_record_aaaa_ip != $ipv6 ]
146            then
147                # change the AAAA record
148                curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records/$(echo $dns_record_aaaa_id | jq -r '{"result"}[] | .[0] | .id')" \
149                     -H "Content-Type: application/json" \
150                     -H "X-Auth-Email: $email" \
151                     -H "Authorization: Bearer $api_token" \
152                     --data "{"type":"AAAA","name":"$dns_record","content":"$ipv6","ttl":1,"proxied":false}" \
153                | jq -r '.errors'
154            else
155                echo "The current IPv6 and DNS record IPv6 are the same."
156            fi
157        else
158            echo "Could not get your IPv6. Check if you have it; e.g. on https://ifconfig.co"
159        fi
160    else
161        echo "There is a problem with getting the Zone ID. Check if the Zone Name is correct."
162    fi
163else
164    echo "There is a problem with either the email or the password"
165fi
166#+end_src
167
168Once the script is saved and closed, make the scripts executable:
169
170#+begin_src sh
171chmod +x ~/ddns/ddns.sh
172chmod +x ~/ddns/update.sh
173#+end_src
174
175You can test the script by running it manually:
176
177#+begin_src sh
178./update.sh
179#+end_src
180
181To make sure the scripts run automatically, add it to the =cron= file so that it
182will run on a schedule. To do this, open the cron file:
183
184#+begin_src sh
185crontab -e
186#+end_src
187
188In the =cron= file, paste the following at the bottom of the editor:
189
190#+begin_src sh
191,*/5 ** ** ** ** bash /home/<your_username>/ddns/update.sh
192#+end_src