-
-
Save icecreammatt/57d9351e52ebc9047a79 to your computer and use it in GitHub Desktop.
Automatically update your CloudFlare DNS record to the IP, Dynamic DNS for Cloudflare
This file contains 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
#!/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 `crontab -e` and add next line: | |
# 0 * * * * /usr/local/bin/cf-ddns.sh >/dev/null 2>&1 | |
# if you're lazy (like me) copy/paste the command BETWEEN the EOT | |
: <<'EOT' | |
curl https://gist.githubusercontent.com/larrybolt/6295160/raw > /usr/local/bin/cf-ddns.sh && chmod +x /usr/local/bin/cf-ddns.sh | |
(crontab -l 2>/dev/null; echo "0 * * * * /usr/local/bin/cf-ddns.sh >/dev/null 2>&1") | crontab - | |
$EDITOR /usr/local/bin/cf-ddns.sh | |
/usr/local/bin/cf-ddns.sh | |
EOT | |
# run /usr/local/bin/cf-ddns.sh in terminal to check all settings are valid | |
# Usage: | |
# cf-ddns.sh -k cloudflare-api-key \ | |
# -u [email protected] \ | |
# -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: [email protected] | |
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)" | |
curl -s https://www.cloudflare.com/api_json.html \ | |
-d a=zone_load_multi \ | |
-d tkn=$CFKEY \ | |
-d email=$CFUSER \ | |
| grep -Eo '"zone_name":"([^"]+)"' \ | |
| cut -d':' -f2 \ | |
| awk '{gsub("\"","");print "* "$1}' | |
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..." | |
if ! CFID=$( | |
curl -s https://www.cloudflare.com/api_json.html \ | |
-d a=rec_load_all \ | |
-d tkn=$CFKEY \ | |
-d email=$CFUSER \ | |
-d z=$CFZONE \ | |
| grep -Eo '"(rec_id|name|type)":"([^"]+)"' \ | |
| cut -d':' -f2 \ | |
| awk 'NR%3{gsub("\"","");printf $0" ";next;}1' \ | |
| grep -E "${CFHOST//./\\.}" \ | |
| grep -e '"A"' \ | |
| grep -Eo "(^|\s)(\d+)(\s|$)" | |
); then | |
echo " => Incorrect zone, or zone doesn't contain the A-record ${CFHOST}!" | |
echo "listing all records for zone ${CFZONE}:" | |
(printf "ID RECORD TYPE\n"; | |
curl -s https://www.cloudflare.com/api_json.html \ | |
-d a=rec_load_all \ | |
-d tkn=$CFKEY \ | |
-d email=$CFUSER \ | |
-d z=$CFZONE \ | |
| grep -Eo '"(rec_id|name|type)":"([^"]+)"' \ | |
| cut -d':' -f2 \ | |
| awk 'NR%3{gsub("\"","");printf $0" ";next;}1' | |
)| column -t | |
exit 2 | |
fi | |
echo " => Found CFID=${CFID}, advising to save this to ${0} or set it using the -i flag" | |
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