My setup to do dynamic DNS updates to Cloudflare. The package available are too heavyweight or don’t do what I want; it’s simple enough to write my own.
Create script /usr/local/bin/cloudflare-update.sh
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
#!/usr/bin/env bash
# Copyright 2022 Mike Carlton
#
# Released under terms of the MIT License:
# http://www.opensource.org/licenses/mit-license.php
# https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
if hash jq 2>/dev/null; then
: # all good
else
echo 'Please install 'jq': https://stedolan.github.io/jq/'
exit 1
fi
if [ -z "$CLOUDFLARE_API_TOKEN" ] ; then
echo 'Please define 'CLOUDFLARE_API_TOKEN' environment var'
echo 'Create token if needed: https://dash.cloudflare.com/profile/api-tokens'
exit 1
fi
api_token="$CLOUDFLARE_API_TOKEN"
domain="carltons.us"
records=( "home.$domain" "unifi.$domain" )
ipv4_re='^([0-9]{1,3}\.){3}[0-9]{1,3}$' # simple, but good enough
current_ip=$(curl -s -4 icanhazip.com)
if ! [[ $current_ip =~ $ipv4_re ]]; then
echo "Unable to get current ip: got '$current_ip'"
exit 1
fi
zone=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$domain" \
-H "Authorization: Bearer $api_token" -H 'Content-Type:application/json' \
| jq -r '.result[0].id')
for record in "${records[@]}" ; do
dns=( $(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone/dns_records?type=A&name=$record" \
-H "Authorization: Bearer $api_token" -H 'Content-Type:application/json' \
| jq -r '.result[0].content,.result[0].id') )
dns_ip=${dns[0]}
dns_id=${dns[1]}
if ! [[ $dns_ip =~ $ipv4_re ]]; then
echo "Unable to get DNS ip: got '$dns_ip'"
exit 1
fi
if ! [[ "$dns_ip" == "$current_ip" ]]; then
response=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone/dns_records/$dns_id" \
-H "Authorization: Bearer $api_token" -H 'Content-Type:application/json' \
--data '{ "type": "A", "name": "'${record}'", "content": "'${current_ip}'", "ttl": 1, "proxied": true }')
if [[ $(echo $response | jq -r .success) == "true" ]]; then
>&2 echo "Updated Cloudflare DNS record $record: $dns_ip to $current_ip"
else
message=$(echo $response | jq -r '.errors[0].message') # just report the first error
>&2 echo "Fail to update Cloudflare DNS record $record: $dns_ip to $current_ip: $message"
exit 1
fi
fi
done
exit 0
Create /etc/cron.d/cloudflare-ddns
with
1
2
3
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/5 * * * * root [ -x /usr/local/bin/cloudflare-update.sh ] && . /root/.cloudflare && /usr/local/bin/cloudflare-update.sh
Create /root/.cloudflare
1
CLOUDFLARE_API_TOKEN=<TOKEN>