Last active
April 13, 2025 15:54
-
-
Save lukedesu/80751505133828c122470bc56388b6c0 to your computer and use it in GitHub Desktop.
Automating Cloudflare DNS Updates Using Bash and Cron
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 | |
# PREREQUISITES: | |
# 1. Ensure you have an active Cloudflare account. | |
# 2. Create an A record for each domain manually in your Cloudflare dashboard. | |
# - You can use a dummy IP address like 1.1.1.1 during setup. | |
# - This script will update the IP automatically during execution. | |
# 3. Obtain your Global API Key from "My Profile" in Cloudflare. | |
# 4. Make sure `curl` is installed on your system. | |
# MODIFY BELOW AS NEEDED | |
auth_email="[email protected]" # Email for Cloudflare login 'https://dash.cloudflare.com' | |
auth_key="YOUR_KEY" # Found in "My Profile" > "Global API Key" on Cloudflare | |
# Domain names with corresponding zone IDs and proxy settings | |
# Example: ["domain_name"]="zone_id:proxy_flag" | |
declare -A domain_config_map=( | |
["www.example.com"]="9c7eb4f4abeaaca8fa029e2a03c76df7:true" | |
["shop.example2.com"]="4d8dbe2cd4380be4954dff50cc238354:false" | |
) | |
# DO NOT MODIFY BELOW THIS LINE UNLESS YOU ARE AN EXPERT | |
# Create the logs directory if it doesn't exist | |
log_dir="$(dirname "$0")/logs" | |
mkdir -p "$log_dir" | |
########################################### | |
## Function to log messages with timestamp | |
########################################### | |
log_message() { | |
local message=$1 | |
local log_file=$2 | |
local timestamp=$(date +"%Y-%m-%d %H:%M:%S") | |
echo -e "[$timestamp] $message" | tee -a "$log_file" | |
} | |
########################################### | |
## Function to update DNS record for a domain | |
########################################### | |
update_record() { | |
local record_name=$1 | |
local zone_identifier=$2 | |
local proxy_setting=$3 | |
local log_file=$4 | |
local ip_file=$5 | |
# Ensure the log file is writable | |
if [ ! -e "$log_file" ]; then | |
touch "$log_file" | |
fi | |
chmod 664 "$log_file" | |
# Ensure the IP file exists | |
if [ ! -e "$ip_file" ]; then | |
touch "$ip_file" | |
fi | |
########################################### | |
## Check if we have a public IP | |
########################################### | |
ip=$(curl -s https://ipv4.icanhazip.com/ || curl -s https://api.ipify.org) | |
if [ -z "$ip" ]; then | |
log_message "No public IP found." "$log_file" | |
exit 1 | |
fi | |
########################################### | |
## Check if the IP has changed | |
########################################### | |
if [ -e "$ip_file" ]; then | |
stored_ip=$(cat "$ip_file") | |
if [ "$ip" == "$stored_ip" ]; then | |
log_message "IP has not changed. Current IP: $ip" "$log_file" | |
return 0 | |
fi | |
fi | |
########################################### | |
## Seek for the A record | |
########################################### | |
log_message "Check Initiated for $record_name" "$log_file" | |
record=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records?name=$record_name" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json") | |
########################################### | |
## Set the record identifier from result | |
########################################### | |
record_identifier=$(echo "$record" | grep -Po '(?<="id":")[^"]*' | head -1) | |
if [ -z "$record_identifier" ]; then | |
log_message "Record identifier for $record_name not found. Creating empty file." "$log_file" | |
# Create an empty file to indicate the record was not found | |
touch "$(dirname "$0")/dns_record_not_found_$record_name.txt" | |
return 1 | |
fi | |
########################################### | |
## Change the IP@Cloudflare using the API | |
########################################### | |
update=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$zone_identifier/dns_records/$record_identifier" -H "X-Auth-Email: $auth_email" -H "X-Auth-Key: $auth_key" -H "Content-Type: application/json" --data "{\"type\":\"A\",\"proxied\":${proxy_setting},\"name\":\"$record_name\",\"content\":\"$ip\"}") | |
########################################### | |
## Report the status | |
########################################### | |
case "$update" in | |
*""success":false"*) | |
log_message "$ip $record_name DDNS failed for $record_identifier ($ip). DUMPING RESULTS:\n$update" "$log_file" | |
return 1 | |
;; | |
*) | |
log_message "$ip $record_name DDNS updated." "$log_file" | |
;; | |
esac | |
########################################### | |
## Save the current IP | |
########################################### | |
echo "$ip" > "$ip_file" | |
} | |
########################################### | |
## Loop through all domain names, zone identifiers, and proxy settings | |
########################################### | |
for record_name in "${!domain_config_map[@]}"; do | |
config="${domain_config_map[$record_name]}" | |
zone_identifier=$(echo "$config" | cut -d':' -f1) | |
proxy_setting=$(echo "$config" | cut -d':' -f2) | |
log_file="$log_dir/${record_name}_update_logs.log" | |
ip_file="$log_dir/${record_name}_current_ip.txt" | |
update_record "$record_name" "$zone_identifier" "$proxy_setting" "$log_file" "$ip_file" | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Two Steps to Automate the Script
1. Assign Permissions
Make the script executable by setting the appropriate permissions:
sudo chmod +x ~/cloudflare.sh
2. Add the Script to Your Crontab
Open the crontab editor to schedule the script:
Append the following line to the end of the crontab file to run the script every 10 minutes: