content/blog/2022-03-23-cloudflare-dns-api.org
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