Skip to content

Instantly share code, notes, and snippets.

@devinderaujla
Forked from Tras2/cloudflare-ddns-update.sh
Last active March 31, 2024 16:21
Show Gist options
  • Save devinderaujla/191e53c7abc5db379166c13715d102e8 to your computer and use it in GitHub Desktop.
Save devinderaujla/191e53c7abc5db379166c13715d102e8 to your computer and use it in GitHub Desktop.
A bash script to update a Cloudflare DNS A record with the external IP of the source machine
#!/bin/bash
# based on https://gist.github.com/Tras2/cba88201b17d765ec065ccbedfb16d9a
# initial data; they need to be filled by the user
#### Added multiple sub domains and proxy state set options
## API token; e.g. FErsdfklw3er59dUlDce44-3D43dsfs3sddsFoD3
api_token=<>
## the email address associated with the Cloudflare account; e.g. email@gmail.com
email=<>
## the zone (domain) should be modified; e.g. example.com
zone_name=example.com
## the dns record (sub-domain) should be modified; e.g. sub.example.com
hostname=(a.example.com,b.example.com)
## Set Proxy for the hostname to 'true' or 'false'
proxy=true
# get the basic data
ipv4=$(curl -s -X GET -4 https://checkip.amazonaws.com --max-time 30)
ipv6=$(curl -s -X GET -6 https://ifconfig.co --max-time 30)
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'
)
# write down IPv4 and/or IPv6
if [ $ipv4 ]; then echo -e "\033[0;32m [+] Your public IPv4 address: $ipv4"; else echo -e "\033[0;33m [!] Unable to get any public IPv4 address."; fi
if [ $ipv6 ]; then echo -e "\033[0;32m [+] Your public IPv6 address: $ipv6"; else echo -e "\033[0;33m [!] Unable to get any public IPv6 address."; fi
# 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'
)
for dns_record in ${hostname//,/ } ##--> Beginning of the for loop to process all hostnames
do
echo -e "\033[0;35m Working on $dns_record"
# check if the zone ID is avilable
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 IPv4 exist
dns_record_a_ip=$(echo $dns_record_a_id | jq -r '{"result"}[] | .[0] | .content')
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\":$proxy}" \
| jq -r '.errors'
# write the result
echo -e "\033[0;32m [+] The IPv4 is successfully set on Cloudflare as the A Record with the value of: $dns_record_a_ip"
else
echo -e "\033[0;37m [~] The current IPv4 and the existing on on Cloudflare are the same; there is no need to apply it."
fi
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')
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\":$proxy}" \
| jq -r '.errors'
# write the result
echo -e "\033[0;32m [+] The IPv6 is successfully set on Cloudflare as the AAAA Record with the value of: $dns_record_aaaa_ip"
else
echo -e "\033[0;37m [~] The current IPv6 address and the existing on on Cloudflare are the same; there is no need to apply it."
fi
fi
else
echo -e "\033[0;31m [-] There is a problem with getting the Zone ID (subdomain) or the email address (username). Check them and try again."
fi
done ###--> End of the for loop to process all hostnames
else
echo -e "\033[0;31m [-] There is a problem with either the API token. Check it and try again."
fi
@devinderaujla
Copy link
Author

devinderaujla commented Feb 20, 2023

Modified to add multiple domains and proxy state.

@thomastthai
Copy link

thomastthai commented Feb 20, 2024

It may save a few curl command executions to check earlier in the script if the current dynamic IPs match what's already in DNS and exit:

...
# get the basic data
ipv4=$(curl -s -X GET -4 https://ifconfig.co)
ipv6=$(curl -s -X GET -6 https://ifconfig.co)

# If the current dynamic IPs match what's already in DNS, exit the script
# Added by Thomas 2024-02-19
if [ $check_record_ipv4 == $ipv4 ]; then
        echo -e "\033[0;37m [~] No change: The current IPv4 address ${ipv4} matches the existing DNS records ${check_record_ipv4}."
        exit 0
fi
if [ $check_record_ipv6 == $ipv6 ]; then
        echo -e "\033[0;37m [~] No change: The current IPv6 address ${ipv6} matches the existing DNS records ${check_record_ipv6}."
        exit 0
fi

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'
         )

# write down IPv4 and/or IPv6
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment