Skip to content

Instantly share code, notes, and snippets.

@Co0olCat
Forked from lyoshenka/ddns.sh
Last active March 7, 2018 18:40
Show Gist options
  • Select an option

  • Save Co0olCat/3d678a07b1e4f6c1340157fcbff3d5a7 to your computer and use it in GitHub Desktop.

Select an option

Save Co0olCat/3d678a07b1e4f6c1340157fcbff3d5a7 to your computer and use it in GitHub Desktop.
Quick and dirty DDNS using Bash and Cloudflare (API v4 compatible)
#!/usr/bin/env bash
# Step 1: Fill in EMAIL, TOKEN, DOMAIN and SUBDOMAIN. Your API token is here: https://www.cloudflare.com/a/account/my-account
# Make sure the token is the Global token, or has these permissions: #zone:read, #dns_record:read, #dns_records:edit
# Step 2: Create an A record on Cloudflare with the subdomain you chose
# Step 3: Run "./ddns.sh -l" to get the zone_id and rec_id of the record you created.
# Fill in ZONE_ID and REC_ID below
# This step is optional, but will save you 2 requests every time you this script
# Step 4: Run "./ddns.sh". It should tell you that record was updated or that it didn't need updating.
# Step 5: Run it every hour with cron. Use the '-s' flag to silence normal output
# 0 * * * * /path/to/ddns.sh -s
# Thank you. Use it at your own risk.
EMAIL='' # Registration email for CloudFlare
TOKEN='' # Global API Key
DOMAIN='' # Root domain, e.g. example.com
SUBDOMAIN='' # Full doamin name, e.g. www.example.com or example.com when you are not using www prefix
MAIN_IP='' # If accessible and backup is used -> will be switched to main, e.g. 1.2.3.4
BACKUP_IP='' # If main IP is not accessible -> will be switched to backup, e.g. 9.8.7.6
ZONE_ID='' # For DOMAIN -> Run "./ddns.sh -l" to get value
REC_ID='' # For SUBDOMAIN -> Run "./ddns.sh -l" to get value
set -euo pipefail
#set -x # enable for debugging
VERBOSE="[ '${1:-}' != '-s' ]"
LOOKUP="[ '${1:-}' == '-l' ]"
API_URL="https://api.cloudflare.com/client/v4"
CURL="curl -s \
-H Content-Type:application/json \
-H X-Auth-Key:$TOKEN \
-H X-Auth-Email:$EMAIL "
if [ -z "$ZONE_ID" ] || $LOOKUP; then
ZONE_ID="$($CURL "$API_URL/zones?name=$DOMAIN" | sed -e 's/[{}]/\n/g' | grep '"name":"'"$DOMAIN"'"' | sed -e 's/,/\n/g' | grep '"id":"' | cut -d'"' -f4)"
$VERBOSE && echo "ZONE_ID='$ZONE_ID'"
fi
if [ -z "$REC_ID" ] || $LOOKUP; then
REC_ID="$($CURL "$API_URL/zones/$ZONE_ID/dns_records" | sed -e 's/[{}]/\n/g' | grep '"name":"'"$SUBDOMAIN"'"' | sed -e 's/,/\n/g' | grep '"id":"' | cut -d'"' -f4)"
$VERBOSE && echo "REC_ID='$REC_ID'"
fi
$LOOKUP && exit 0
# Get Cloudflare current IP for SUBDOMAIN
RECORD_IP="$($CURL "$API_URL/zones/$ZONE_ID/dns_records/$REC_ID" | sed -e 's/[{}]/\n/g' | sed -e 's/,/\n/g' | grep '"content":"' | cut -d'"' -f4)"
$VERBOSE && echo "CloudFlare IP for '$SUBDOMAIN' is $RECORD_IP"
$VERBOSE && echo "Checking whether it is accessible..."
# Check whether server is accessible
if ping -c1 -W1 $RECORD_IP &> /dev/null
then
# Domain is accessible -> no change is required
$VERBOSE && echo "$DOMAIN is UP"
#Check whether it is main address
if [ "$RECORD_IP" == "$MAIN_IP" ]; then
$VERBOSE && echo "This is MAIN IP -> Keeping existing settings"
else
$VERBOSE && echo "This is BACKUP IP"
# Check whether MAIN IP is accessible
if ping -c1 -W1 $MAIN_IP &> /dev/null
then
$VERBOSE && echo "MAIN IP is accessible -> Switching..."
$VERBOSE && echo "Setting IP to $MAIN_IP"
$CURL -X PUT "$API_URL/zones/$ZONE_ID/dns_records/$REC_ID" --data '{"type":"A","name":"'"$SUBDOMAIN"'","content":"'"$MAIN_IP"'","proxied":true}' 1>/dev/null
else
$VERBOSE && echo "MAIN IP is NOT accessible -> Keeping existing settings"
fi
fi
else
# Domain is not accessible ->
$VERBOSE && echo "$DOMAIN is down -> switching IP"
# Check whether it is main address
if [ "$RECORD_IP" == "$MAIN_IP" ]; then
$VERBOSE && echo "This is MAIN IP"
else
$VERBOSE && echo "This is BACKUP IP"
BACKUP_IP = $MAIN_IP
fi
# Check whether BACKUP IP is accessible
if ping -c1 -W1 $BACKUP_IP &> /dev/null
then
$VERBOSE && echo "BACKUP IP is accessible -> Switching..."
$VERBOSE && echo "Setting IP to $BACKUP_IP"
$CURL -X PUT "$API_URL/zones/$ZONE_ID/dns_records/$REC_ID" --data '{"type":"A","name":"'"$SUBDOMAIN"'","content":"'"$BACKUP_IP"'","proxied":true}' 1>/dev/null
else
$VERBOSE && echo "BACKUP IP is NOT accessible -> Keeping existing settings"
fi
fi
exit 0
@Co0olCat

Co0olCat commented Jan 5, 2017

Copy link
Copy Markdown
Author

The source file is excellent -> but it assumes that the script is running on server hosting web content.
This modification requires setting explicitly two IPs: MAIN and BACKUP.

When SUBDOMAIN is not accessible -> CloudFlare will be instructed to switch IP for SUBDOMAIN to BACKUP_IP.
When SUBDOMAIN is accessible, but it uses BACKUP_IP and MAIN_IP became accessible -> CloudFlare will be instructed to switch back to MAIN_IP

@Co0olCat

Co0olCat commented Jan 6, 2017

Copy link
Copy Markdown
Author

@pascalandy

Copy link
Copy Markdown

Looks great! This is exactly what I'm looking for :)

Do we still need to run monit on each servers ?

@Co0olCat

Copy link
Copy Markdown
Author

Sorry for late reply. In my case only cron is required. The script performs ping check to servers. Thank you.

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