Skip to content

Instantly share code, notes, and snippets.

@weissmike
Forked from larrybolt/cf-ddns.sh
Last active July 14, 2016 18:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weissmike/837d008495e2618a4554b4795b74e9f8 to your computer and use it in GitHub Desktop.
Save weissmike/837d008495e2618a4554b4795b74e9f8 to your computer and use it in GitHub Desktop.
Automatically update your CloudFlare DNS record to the IP, Dynamic DNS for Cloudflare
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
# Automatically update your CloudFlare DNS record to the IP, Dynamic DNS
# Can retrieve cloudflare Domain id and list zone's, because, lazy
# Place at:
# /usr/local/bin/cf-ddns.sh
# run `chomod +x cf-ddns.sh`
# run `crontab -e` and add next line:
# 0 * * * * /usr/local/bin/cf-ddns.sh >/dev/null 2>&1
# run /usr/local/bin/cf-ddns.sh in terminal to check all settings are valid
# Usage:
# cf-ddns.sh -k cloudflare-api-key \
# -u user@example.com \
# -h host.example.com \ # fqdn of the record you want to update
# -z example.com \ # will show you all zones if forgot, but you need this
# Optional flags:
# -i cloudflare-record-id \ # script will show this
# -a true|false \ # auto get zone list and record id
# -f false|true \ # force dns update, disregard local stored ip
# default config
# API key, see https://www.cloudflare.com/a/account/my-account,
# incorrect api-key results in E_UNAUTH error
CFKEY=
# Zone name, will list all possible if missing, eg: example.com
CFZONE=
# Domain id, will retrieve itself by default
CFID=
# Username, eg: user@example.com
CFUSER=
# Hostname to update, eg: homeserver.example.com
CFHOST=
# Cloudflare TTL for record, between 120 and 86400 seconds
CFTTL=3600
# Get domain ID from Cloudflare using awk/sed and python json.tool
GETID=true
# Ignore local file, update ip anyway
FORCE=false
# Site to retrieve WAN ip, other examples are: bot.whatismyipaddress.com, https://api.ipify.org/ ...
WANIPSITE="http://icanhazip.com"
# get parameter
while getopts a:k:i:u:h:z:f: opts; do
case ${opts} in
a) GETID=${OPTARG} ;;
k) CFKEY=${OPTARG} ;;
i) CFID=${OPTARG} ;;
u) CFUSER=${OPTARG} ;;
h) CFHOST=${OPTARG} ;;
z) CFZONE=${OPTARG} ;;
f) FORCE=${OPTARG} ;;
esac
done
# If required settings are missing just exit
if [ "$CFKEY" = "" ]; then
echo "Missing api-key, get at: https://www.cloudflare.com/a/account/my-account"
echo "and save in ${0} or using the -k flag"
exit 2
fi
if [ "$CFUSER" = "" ]; then
echo "Missing username, probably your email-address"
echo "and save in ${0} or using the -u flag"
exit 2
fi
if [ "$CFHOST" = "" ]; then
echo "Missing hostname, what host do you want to update?"
echo "save in ${0} or using the -h flag"
exit 2
fi
# If the hostname is not a FQDN
if [ "$CFHOST" != "$CFZONE" ] && ! [ -z "${CFHOST##*$CFZONE}" ]; then
CFHOST="$CFHOST.$CFZONE"
echo " => Hostname is not a FQDN, assuming $CFHOST"
fi
# If CFZONE is missing, retrieve them all from CF
if [ "$CFZONE" = "" ]; then
echo "Missing zone"
if ! [ "$GETID" == true ]; then exit 2; fi
echo "listing all zones: (if api-key is valid)"
JSON=$(curl -s https://www.cloudflare.com/api_json.html \
-d a=zone_load_multi \
-d tkn=$CFKEY \
-d email=$CFUSER | sed -e 's/":"/:/g')
if [ -n "$(echo $JSON|grep -Po '"err_code:\K[^"]*')" ]; then
echo "Error:" $(echo -e $JSON|grep -Po '"msg:\K[^"]*')
exit 2
fi
for m in $(echo $JSON|grep -Po '"zone_name:\K[^"]*'); do
echo "* $m"
done
echo "Please specify the matching zone in ${0} or specify using the -z flag"
exit 2
fi
# Get current and old WAN ip
WAN_IP=`curl -s ${WANIPSITE}`
if [ -f $HOME/.wan_ip-cf.txt ]; then
OLD_WAN_IP=`cat $HOME/.wan_ip-cf.txt`
else
echo "No file, need IP"
OLD_WAN_IP=""
fi
# If WAN IP is unchanged an not -f flag, exit here
if [ "$WAN_IP" = "$OLD_WAN_IP" ] && [ "$FORCE" = false ]; then
echo "WAN IP Unchanged, to update anyway use flag -f true"
exit 0
fi
# If CFID is missing retrieve and use it
if [ "$CFID" = "" ]; then
echo "Missing DNS record ID"
if ! [ "$GETID" == true ]; then exit 2; fi
echo "fetching from Cloudflare..."
JSON=$(curl -s https://www.cloudflare.com/api_json.html \
-d a=rec_load_all \
-d tkn=$CFKEY \
-d email=$CFUSER \
-d z=$CFZONE | sed -e 's/":"/:/g')
if [ -n "$(echo $JSON|grep -Po '"err_code:\K[^"]*')" ]; then
echo "Error:" $(echo -e $JSON|grep -Po '"msg:\K[^"]*')
exit 2
fi
IDS=($(echo $JSON|grep -Po '"rec_id:\K[^"]*'))
NAMES=($(echo $JSON|grep -Po '"name:\K[^"]*'))
TYPES=($(echo $JSON|grep -Po '"type:\K[^"]*'))
RECORDS="ID RECORD TYPE";
for key in $(seq 0 $((${#IDS[@]}-1)) ); do
if [ "${NAMES[$key]}" == "$CFHOST" ] && [ "${TYPES[$key]}" == "A" ]; then
CFID=${IDS[$key]}
fi
RECORDS="${RECORDS}\n${IDS[$key]} ${NAMES[$key]} ${TYPES[$key]}"
done
fi
# If WAN is changed, update cloudflare
echo "Updating DNS to $WAN_IP"
RESPONSE=$(
curl -s https://www.cloudflare.com/api_json.html \
-d a=rec_edit \
-d tkn=$CFKEY \
-d email=$CFUSER \
-d z=$CFZONE \
-d id=$CFID \
-d ttl=$CFTTL \
-d type=A \
-d name=$CFHOST \
-d "content=$WAN_IP"
)
if [ "$RESPONSE" != "${RESPONSE%success*}" ]; then
echo "Updated succesfuly!"
echo $WAN_IP > $HOME/.wan_ip-cf.txt
exit
else
echo 'Something went wrong :('
echo "Response: $RESPONSE"
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment