Last active
April 8, 2024 18:54
-
-
Save wrecker/5fabd383fee6f5bdb325f6f17d32c32a to your computer and use it in GitHub Desktop.
Cloudflare Dynamic DNS Script in Bash
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=Cloudflare DDNS Service | |
[Service] | |
Type=simple | |
ExecStart=/full/path/to/cloudflare-ddns.sh | |
TimeoutStopSec=20 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# A bash script to manage a DNS records in Cloudflare to create a simple | |
# Dynamic DNS service. This script will first try to get the external gateway | |
# IP of this machine and then update A records in cloudflare to point a set of | |
# hostnames to this external IP. This script expects the records to already be | |
# present in the zone. | |
# Setup: | |
# - Login to your cloudflare account and click on the Zone/Domain to update. | |
# In the overview page, look for the Zone ID and update the zone_id below | |
# - On the same page, look for 'Get you API Token' link and click on it. | |
# Click on 'API Tokens' tab and create a new token. Choose the 'Edit Zone DNS' | |
# template. Check the Permissions section has Zone -> DNS -> Edit in the three | |
# dropdowns. In Zone Resources, the first two dropdowns should be Include -> | |
# Specific Zone. In the third dropdown choose the zone to update. | |
# Click on 'Continue to summary' and verify everything looks good and click on | |
# 'Create Token'. The next page will display the API Token. Copy it and update | |
# cf_bearer_token line below. | |
# - domain: The domain name of the zone | |
# - hosts: The hostnames without the domain that need to be updated. | |
############################################################################### | |
## SCRIPT VARIABLES | |
# Cloudflare API/Bearer Token | |
cf_bearer_token= | |
# Cloudflare zone for the hostname | |
zone_id= | |
# This should match the zone_id | |
domain=example.com | |
# The hostnames to update | |
hosts=(www mail home) | |
# Set this to true to enable cloudflare DNS proxy (orange cloud) or | |
# false to disable (grey cloud) | |
proxy="true" | |
############################################################################## | |
verbose='' | |
force='' | |
while getopts ":fv" opt; do | |
case ${opt} in | |
f ) # force update | |
force='y' | |
;; | |
v ) # verbose mode | |
verbose='-v' | |
;; | |
\? ) | |
;; | |
esac | |
done | |
# No of records to update | |
num_hosts=${#hosts[@]} | |
if (( $num_hosts < 1 )); then | |
echo "No hosts to update. Check the script variable 'hosts'" | |
exit 0 | |
fi | |
dns_record_id="null" | |
function query_dns_record_id { | |
if [[ -z "$1" ]]; then | |
return 0 | |
fi | |
hostname=${1}.${domain} | |
dns_record_id=$(curl ${verbose} -s "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records?type=A&name=${hostname}" \ | |
-H "Authorization: Bearer $cf_bearer_token" \ | |
-H "Content-Type: application/json" | jq -r '.result[0].id') | |
} | |
# Query the external IP using the AWS CheckIP Service | |
ext_ip=$(curl -s https://checkip.amazonaws.com) | |
# Get the first DNS Record ID from cloudflare API | |
query_dns_record_id ${hosts[0]} | |
if [[ "$dns_record_id" == "null" ]]; then | |
cat <<-ENDMSG | |
** Unable to extract DNS record-id from Cloudflare APIs. Check the script variables | |
** or re-run with -v to check the Cloudflare API Call. | |
ENDMSG | |
exit 1 | |
fi | |
current_ip=$(curl ${verbose} -s "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${dns_record_id}" \ | |
-H "Authorization: Bearer $cf_bearer_token" \ | |
-H "Content-Type: application/json" | jq -r '.result.content') | |
if [[ "$force" == "y" || "$ext_ip" != "$current_ip" ]]; then | |
if [[ "$force" == "y" ]]; then | |
echo "Force mode: Updating Cloudflare records" | |
else | |
echo "External IP [${ext_ip}] is different from IP in cloudflare [${current_ip}]." | |
echo "Updating Cloudflare records." | |
fi | |
# Loop over every host in list $hosts | |
for (( i=0; i < ${num_hosts}; i++ )); do | |
# we have already queried the dns record-id for the first entry. Skip it. | |
if [[ $i -gt 0 ]]; then | |
query_dns_record_id ${hosts[$i]} | |
if [[ "$dns_record_id" == "null" ]]; then | |
echo "Unable to query record-id for ${hosts[$i]}.${domain}. Skipping..." | |
continue | |
fi | |
fi | |
# Update the record with the new ip. | |
curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${zone_id}/dns_records/${dns_record_id}" \ | |
-H "Authorization: Bearer $cf_bearer_token" \ | |
-H "Content-Type: application/json" \ | |
--data "{\"type\":\"A\",\"name\":\"${hostname}\",\"content\":\"${ext_ip}\",\"ttl\":1,\"proxied\":${proxy}}" | jq | |
echo "Updated Record for ${hostname}" | |
done | |
else | |
echo "No change in external IP and cloudflare IP." | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Unit] | |
Description=Run Cloudflare DDNS service hourly | |
[Timer] | |
OnCalendar=hourly | |
Persistent=true | |
[Install] | |
WantedBy=timers.target |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment