content/blog/2022-03-23-cloudflare-dns-api.org

0b2895a90c7751ce06e4b26d4db41005ea2f40a9
cleberg.net/content/blog/2022-03-23-cloudflare-dns-api.org rendered · source · history · blame · raw

194 lines · 7091 bytes

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