DDNS for GoDaddy domains with OpenWRT

Most of the domains I own are registered at GoDaddy. To be able to run a VPN to my home, I need its current IP address. I used to have a CNAME entry pointing to the name I registered at Synology for my NAS. The IP address doesn’t change often, but it does change and of course it happens when you are away.

But I’m ‘decommissioning’ my Synology NAS and thus can’t point to its name anymore. Instead of using a commercial DDNS provider, I decided to write a small script for my OpenWRT router and schedule it.
This script requires the curl package to be installed.
At home, the OpenWRT box is directly connected to the internet, at another place it is behind an ISP provided router. In the first case, it can use the address for the wan network, in the second case it uses wwan but since that gets an internal IP, we need to query ipinfo.io for the current external IP.

The script accepts 2 arguments. The first one is the name of the entry, the second is the interface name used in OpenWRT and is optional (defaults to wan).
Save the script in /root as godaddydyndns.sh, make it executable and add a line in crontab for it.
The entry I use is:
*/5 * * * * /root/godaddydyndns.sh city.country wwan >/dev/null 2>&1
It checks the IP address every 5 minutes and if it is changed, it updates the A record for city.country.example.org

#!/bin/sh

# === CONFIGURE THESE ===

API_KEY=“your key here”

API_SECRET=“your secret”

DOMAIN=“example.org”

TTL=600

# === LOGGING CONFIG ===

LOG_FILE=”/tmp/godaddydyndns.log”

# === Check for DNS record name argument ===

if [ -z “$1” ]; then

echo “Error: No name provided!”

echo “Usage: $0 <name>”

exit 1

fi

RECORD_NAME=”$1″ # The first argument is the record name

INTERFACE=”wan” #default to wan

if [ -n “$2” ]; then

INTERFACE=”$2″

fi

echo “using interface $INTERFACE”

# === FUNCTION TO CHECK IF IP IS INTERNAL ===

is_internal_ip() {

IP=”$1″

# Check if the IP is in any of the private ranges. ash uses basic regular expressions…

if [[ “$IP” =~ 10\. ]] || [[ “$IP” =~ 192\.168\. ]] || [[ “$IP” =~ 172\.\(1[6-9]|2[0-9]|3[01]\)\. ]]; then

return 0 # IP is internal

else

return 1 # IP is not internal

fi

}

# === GET CURRENT PUBLIC IP ===

CURRENT_IP=$(ubus call network.interface.$INTERFACE status) 2>/dev/null |

jsonfilter -e “@[‘.ipv4-address’][0][‘address’]”

# If no internal IP found, fallback to ipify.org

if [ -z “$CURRENT_IP” ] || is_internal_ip “$CURRENT_IP”; then

echo “Querying ipify.org for external IP address.”

CURRENT_IP=$(curl https://api.ipify.org?format=json | jsonfilter -e “@.ip”)

fi

echo “Public IP: $CURRENT_IP”

# === GET CURRENT DNS IP ===

DNS_IP=$(curl -s -X GET \

-H “Authorization: sso-key $API_KEY:$API_SECRET” \

“https://api.godaddy.com/v1/domains/$DOMAIN/records/A/$RECORD_NAME” | \

jsonfilter -e “@[0].data”)

echo “DNS IP: $DNS_IP”

# === COMPARE ===

if [[ “$CURRENT_IP” == “$DNS_IP” ]]; then

echo “IP is up to date. No action needed.”

echo “$(date) – No update needed, IP is the same.” >> $LOG_FILE

exit 0

fi

# === UPDATE GODADDY ===

echo “IP has changed. Updating DNS…”

UPDATE_RESULT=$(curl -s -X PUT \

-H “Authorization: sso-key $API_KEY:$API_SECRET” \

-H “Content-Type: application/json” \

-d “[{\”data\”:\”$CURRENT_IP\”,\”ttl\”:$TTL}]” \

“https://api.godaddy.com/v1/domains/$DOMAIN/records/A/$RECORD_NAME”)

# Log update to file

echo “$(date) – DNS update request sent for $RECORD_NAME.$DOMAIN to IP $CURRENT_IP” >> $LOG_FILE

exit 0