[ Bash script ] Cloudflare Dynamic IP Update - IPv4 (DDNS)

Script này sẽ khiểm tra public IP hiện tại của bạn và update IP domain trên Cloudflare một cách nhanh chóng

cloudflare_dynamic_ip_update.bash

  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
#!/usr/bin/env bash

## Author: Hyecheol (Jerry) Jang
## Shell Script that check current public (dynamic) ip address of server,
## and update it to the Cloudflare DNS record after comparing ip address registered to Cloudflare
## basic shell scripting guide https://blog.gaerae.com/2015/01/bash-hello-world.html
## Using dig command (https://en.wikipedia.org/wiki/Dig_(command)) to get current public IP address
currentIP=$(dig -4 TXT +short o-o.myaddr.1.google.com @ns1.google.com)
if [ $? == 0 ] && [ ${currentIP} ]; then  ## when dig command run without error,
    ## Making substring, only retrieving ip address of this server
    ## https://stackabuse.com/substrings-in-bash/
    currentIP=$(echo $currentIP | cut -d'"' -f 2)
    echo "current public IP address is "$currentIP
else  ## error happens,
    echo "Check your internet connection, or google DNS server maybe interruptted"
    exit
fi
## Use Cloudflare API to retrieve recordIP
## https://api.cloudflare.com/
## Read configuration from separated cloudflare_config file (need to locate in the same directory)
## https://stackoverflow.com/questions/10929453/read-a-file-line-by-line-assigning-the-value-to-a-variable
## https://stackoverflow.com/questions/10586153/split-string-into-an-array-in-bash
SCRIPT_PATH=$(dirname $(realpath $0))
readResult=""  ## Store configuration
while IFS= read -r line || [[ -n "$line" ]]; do
    readResult+=" "  ## Delimiter: space
    readResult+="$line"
done < $SCRIPT_PATH"/cloudflare_config"  ## use cloudflare_config file
unset IFS
unset SCRIPT_PATH
## After retrieve information from file, cut result string into configuration elements
key=$(echo $(echo $readResult | cut -d' ' -f 1) | cut -d'=' -f 2)
email=$(echo $(echo $readResult | cut -d' ' -f 2) | cut -d'=' -f 2)
zoneID=$(echo $(echo $readResult | cut -d' ' -f 3) | cut -d'=' -f 2)
IFS=',' read -r -a updateTarget <<< "$(echo $(echo $readResult | cut -d' ' -f 4) | cut -d'=' -f 2)"
unset readResult
unset IFS
## Make space for saving record's IP Address, Type, and Name
declare -a recordIP
declare -a recordType
declare -a recordName
declare -a dnsID
declare -a recordProxied
for string in ${updateTarget[@]}; do  ## retrieve record's IP Address and save to recordIP
    content=$(
        curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records?name=${string}" \
            -H "X-Auth-Email: $email" \
            -H "X-Auth-Key: $key" \
            -H "Content-Type: application/json"
    )
    ## Parse JSON  https://stackoverflow.com/questions/42427371/cloudflare-api-cut-json-response
    ## Using jq  https://stedolan.github.io/jq/
    ip=$(echo $content | jq '.result | map(.content) | add' | cut -d'"' -f 2)
    rType=$(echo $content | jq '.result | map(.type) | add' | cut -d'"' -f 2)
    name=$(echo $content | jq '.result | map(.name) | add' | cut -d'"' -f 2)
    id=$(echo $content | jq '.result | map(.id) | add' | cut -d'"' -f 2)
    proxied=$(echo $content | jq '.result | map(.proxied) | add' | cut -d'"' -f 2)
    recordIP=(${recordIP[@]} $ip)
    recordType=(${recordType[@]} $rType)
    recordName=(${recordName[@]} $name)
    dnsID=(${dnsID[@]} $id)
    recordProxied=(${recordProxied[@]} $proxied)
    unset id
    unset rType
    unset name
    unset ip
    unset content
    unset string
    unset proxied
done
unset updateTarget
## Compare currentIP and recordIP
declare -a needUpdate  ## Array to store whether each record needs to be updated or not
for string in ${recordIP[@]}; do
    if [ ${string} == ${currentIP} ]; then
        needUpdate=(${needUpdate[@]} 'False')
    else
        needUpdate=(${needUpdate[@]} 'True')
    fi
    unset string
done
unset recordIP  ## X Need recordIP Anymore
## Update record if needed
count=0
while [ $count -lt ${#needUpdate[@]} ]; do
    if [ ${needUpdate[count]} == 'True' ]; then
        echo "record IP needs to be updated for "${recordName[count]}
        success=$(
            curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zoneID/dns_records/${dnsID[count]}" \
                -H "X-Auth-Email: $email" \
                -H "X-Auth-Key: $key" \
                -H "Content-Type: application/json" \
                --data '{"type":"'"${recordType[count]}"'","name":"'"${recordName[count]}"'","content":"'"$currentIP"'","proxied":'${recordProxied[count]}'}' | \
            jq '.success' | cut -d'"' -f 2
        )
        if [ $success == true ]; then
            echo "Success update record IP of "${recordName[count]}
        else
            echo "Fail to update record IP of "${recordName[count]}"\n""Please Check result!!"
        fi
    else
        echo "record IP does not need to be updated for "${recordName[count]}
    fi
    count=$((${count}+1))
done
unset count
unset currentIP
unset key
unset email
unset zoneID
unset recordType
unset recordName
unset dnsID
unset recordProxied
unset needUpdate

cloudflare_config

1
2
3
4
Auth-Key=[Global API Key]
Auth-Email=[Login Mail]
Zone-ID=[Zone ID]
Update-Target=subdomain.pnghia.com

Credit: Hyecheol (Jerry) Jang
  • cloudflare_config (104bytes - 0 Download.)
  • cloudflare_dynamic_ip_update.bash (4kB - 0 Download.)
  • author

    Nghia Phan

    Mọi ý kiến đóng góp hoặc thắc mắc, cứ thoải mái liên hệ với mình qua các kênh mạng xã hội dưới đây. CÁM ƠN CÁC BẠN ĐÃ ĐỌC và CHÚC CÁC BẠN MỘT NGÀY TỐT LÀNH 😉.

    Previous Post Next Post