· 6 years ago · Feb 22, 2020, 11:20 PM
1#!/usr/bin/env bash
2
3## Author: Hyecheol (Jerry) Jang
4## Shell Script that check current public (dynamic) ip address of server,
5## and update it to the Cloudflare DNS record after comparing ip address registered to Cloudflare
6## basic shell scripting guide https://blog.gaerae.com/2015/01/bash-hello-world.html
7
8## Using dig command (https://en.wikipedia.org/wiki/Dig_(command)) to get current public IP address
9currentIP=$(dig -4 TXT +short o-o.myaddr.1.google.com @ns1.google.com)
10if [ $? == 0 ] && [ ${currentIP} ]; then ## when dig command run without error,
11 ## Making substring, only retrieving ip address of this server
12 ## https://stackabuse.com/substrings-in-bash/
13 currentIP=$(echo $currentIP | cut -d'"' -f 2)
14 echo "current public IP address is "$currentIP
15else ## error happens,
16 echo "Check your internet connection, or google DNS server maybe interruptted"
17 exit
18fi
19
20## Use Cloudflare API to retrieve recordIP
21## https://api.cloudflare.com/
22## Read configuration from separated cloudflare_config file (need to locate in the same directory)
23## https://stackoverflow.com/questions/10929453/read-a-file-line-by-line-assigning-the-value-to-a-variable
24## https://stackoverflow.com/questions/10586153/split-string-into-an-array-in-bash
25SCRIPT_PATH=$(dirname $(realpath $0))
26readResult="" ## Store configuration
27while IFS= read -r line || [[ -n "$line" ]]; do
28 readResult+=" " ## Delimiter: space
29 readResult+="$line"
30done < $SCRIPT_PATH"/cloudflare_config" ## use cloudflare_config file
31unset IFS
32unset SCRIPT_PATH
33
34## After retrieve information from file, cut result string into configuration elements
35key=$(echo $(echo $readResult | cut -d' ' -f 1) | cut -d'=' -f 2)
36email=$(echo $(echo $readResult | cut -d' ' -f 2) | cut -d'=' -f 2)
37zoneID=$(echo $(echo $readResult | cut -d' ' -f 3) | cut -d'=' -f 2)
38IFS=',' read -r -a updateTarget <<< "$(echo $(echo $readResult | cut -d' ' -f 4) | cut -d'=' -f 2)"
39unset readResult
40unset IFS
41
42## Make space for saving record's IP Address, Type, and Name
43declare -a recordIP
44declare -a recordType
45declare -a recordName
46declare -a dnsID
47declare -a recordProxied
48for string in ${updateTarget[@]}; do ## retrieve record's IP Address and save to recordIP
49 content=$(
50 curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records?name=${string}" \
51 -H "X-Auth-Email: $email" \
52 -H "X-Auth-Key: $key" \
53 -H "Content-Type: application/json"
54 )
55 ## Parse JSON https://stackoverflow.com/questions/42427371/cloudflare-api-cut-json-response
56 ## Using jq https://stedolan.github.io/jq/
57 ip=$(echo $content | jq '.result | map(.content) | add' | cut -d'"' -f 2)
58 rType=$(echo $content | jq '.result | map(.type) | add' | cut -d'"' -f 2)
59 name=$(echo $content | jq '.result | map(.name) | add' | cut -d'"' -f 2)
60 id=$(echo $content | jq '.result | map(.id) | add' | cut -d'"' -f 2)
61 proxied=$(echo $content | jq '.result | map(.proxied) | add' | cut -d'"' -f 2)
62 recordIP=(${recordIP[@]} $ip)
63 recordType=(${recordType[@]} $rType)
64 recordName=(${recordName[@]} $name)
65 dnsID=(${dnsID[@]} $id)
66 recordProxied=(${recordProxied[@]} $proxied)
67 unset id
68 unset rType
69 unset name
70 unset ip
71 unset content
72 unset string
73 unset proxied
74done
75unset updateTarget
76
77## Compare currentIP and recordIP
78declare -a needUpdate ## Array to store whether each record needs to be updated or not
79for string in ${recordIP[@]}; do
80 if [ ${string} == ${currentIP} ]; then
81 needUpdate=(${needUpdate[@]} 'False')
82 else
83 needUpdate=(${needUpdate[@]} 'True')
84 fi
85 unset string
86done
87unset recordIP ## X Need recordIP Anymore
88
89## Update record if needed
90count=0
91while [ $count -lt ${#needUpdate[@]} ]; do
92 if [ ${needUpdate[count]} == 'True' ]; then
93 echo "record IP needs to be updated for "${recordName[count]}
94 success=$(
95 curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records/${dnsID[count]}" \
96 -H "X-Auth-Email: $email" \
97 -H "X-Auth-Key: $key" \
98 -H "Content-Type: application/json" \
99 --data '{"type":"'"${recordType[count]}"'","name":"'"${recordName[count]}"'","content":"'"$currentIP"'","proxied":'${recordProxied[count]}'}' | \
100 jq '.success' | cut -d'"' -f 2
101 )
102 if [ $success == true ]; then
103 echo "Success update record IP of "${recordName[count]}
104 else
105 echo "Fail to update record IP of "${recordName[count]}"\n""Please Check result!!"
106 fi
107 else
108 echo "record IP does not need to be updated for "${recordName[count]}
109 fi
110 count=$((${count}+1))
111done
112unset count
113unset currentIP
114unset key
115unset email
116unset zoneID
117unset recordType
118unset recordName
119unset dnsID
120unset recordProxied
121unset needUpdate