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