Skip to content

Instantly share code, notes, and snippets.

@wrecker
Last active April 8, 2024 18:54
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wrecker/5fabd383fee6f5bdb325f6f17d32c32a to your computer and use it in GitHub Desktop.
Save wrecker/5fabd383fee6f5bdb325f6f17d32c32a to your computer and use it in GitHub Desktop.
Cloudflare Dynamic DNS Script in Bash
[Unit]
Description=Cloudflare DDNS Service
[Service]
Type=simple
ExecStart=/full/path/to/cloudflare-ddns.sh
TimeoutStopSec=20
#!/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
[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