Namecheap offers a free dynamic DNS service with the purchase of a domain. They also offer a Windows client to update DDNS records, but if you are in a Linux or macOS environment, you’re out of luck.
To improve upon my Bash scripting skills, I decided to write my own DDNS update script. The script was written for one of my CentOS 7 servers, but it should work on most versions of Linux and macOS, provided the dependencies are installed.
Installation
First, we need to install the dependencies. In CentOS 7, everything is installed in a Minimal installation except for dig
, which is included in bind-utils
.
The following commands assume this is a fresh installation of CentOS. You do not, however, need to dedicate an entire VM to this script, and if you are using an existing VM that is already up to date, you can skip the second and third commands.
yum install -y bind-utils
yum update -y
reboot
Next, we are going to create a service account without root privileges for running the script.
adduser ddns -s /sbin/nologin
This command creates a directory for the script.
mkdir /opt/ddns
This command creates the script itself. Be sure to modify the variables near the beginning of the script.
cat >/opt/ddns/ddns.sh <<"EOL"
#!/bin/bash
## Namecheap DDNS update client for CentOS 7
## Written by Ian Harrier
###
# Variables
# These need to be customized to your environment
#
# FQDN
# The FQDN of the (A) record to be updated
# Supports either 'domain.tld' or 'subdomain.domain.tld'
# PASSWORD
# The DDNS update password from Namecheap
# NAME_SERVER
# The DNS server used to resolve the DDNS (A) record
# Recommended to use one of the domain's name servers
###
FQDN='subdomain.domain.tld'
PASSWORD='<secret password from Namecheap admin console>'
NAME_SERVER='dns1.registrar-servers.com'
###
# Start
###
echo -n "Starting at "
date +%Y-%m-%d\ %H:%M:%S
###
# Retrieve the current public IPv4 address from ident.me
# For more information: http://api.ident.me/
###
IPv4_CURRENT=$(curl -s v4.ident.me)
echo "The current IPv4 address is: $IPv4_CURRENT"
###
# Retrieve the current public IPv4 address from the DDNS entry
#
# The query is built as follows:
# 1. resolve using this external DNS server ( '@$NAME_SERVER' )
# 2. name to resolve ( '$FQDN' )
# 3. get only IPv4 address(es) ( 'A' )
# 4. get only the IP address ( '+short' )
###
IPv4_DDNS=$(dig @$NAME_SERVER $FQDN A +short)
echo "The DDNS (A) record reports: $IPv4_DDNS"
###
# Compare the results
###
if [[ "$IPv4_CURRENT" == "$IPv4_DDNS" ]]; then
echo -n "IPv4 addresses match. No need to update. Exiting at "
date +%Y-%m-%d\ %H:%M:%S
echo
exit 0
else
echo "IPv4 addresses DO NOT match. Need to update."
fi
###
# Generate $HOST and $DOMAIN
###
# If $FQDN is in the format 'domain.tld' (i.e. one occurrence of '.')
if [[ $(echo $FQDN | awk 'BEGIN{FS="."} {print NF?NF-1:0}') == 1 ]]; then
HOST='@'
DOMAIN=$FQDN
fi
# If $FQDN is in the format 'subdomain.domain.tld' (i.e. two occurrences of '.')
if [[ $(echo $FQDN | awk 'BEGIN{FS="."} {print NF?NF-1:0}') == 2 ]]; then
HOST=$(echo $FQDN | awk -F "." '{print $1}')
DOMAIN=$(echo $FQDN | awk -F "." '{print $2 "." $3}')
fi
###
# Update DDNS
###
echo "Updating $FQDN to $IPv4_CURRENT . . ."
UPDATE_RESULT=$(curl -s "https://dynamicdns.park-your-domain.com/update?host=$HOST&domain=$DOMAIN&password=$PASSWORD&ip=$IPv4_CURRENT")
###
# Report error(s)
###
UPDATE_ERRORS=$(xmllint --xpath "string(//ErrCount)" - <<<"$UPDATE_RESULT")
echo "There were $UPDATE_ERRORS error(s)."
if [[ $UPDATE_ERRORS -gt 0 ]]; then
echo "Error(s):"
for (( i = 1; i <= $UPDATE_ERRORS; i++ )); do
echo -n " $i. "
xmllint --xpath "string(//Err$i)" - <<<"$UPDATE_RESULT"
done
echo
fi
###
# Exit
###
echo -n "Finished. Exiting at "
date +%Y-%m-%d\ %H:%M:%S
echo
exit 0
EOL
The following two commands have to do with permissions. The first makes the script executable. The second gives ownership of the /opt/ddns
directory and its contents to the ddns
service account.
chmod +x /opt/ddns/ddns.sh
chown -R ddns:ddns /opt/ddns
This script also has some basic output built in, so we will create a directory for the log and give ownership of the directory to the ddns
service account.
mkdir /var/log/ddns
chown ddns:ddns /var/log/ddns
To allow this script to run on a schedule, we will create a cron
job under the ddns
service account. In this example, the script will run every five minutes, indicated by */5
. There are many articles on the Internet documenting the functionality of cron
, so instead of re-describing it here, I would recommend taking a look at this article if you have further questions about creating cron
jobs.
crontab -u ddns -e
*/5 * * * * /opt/ddns/ddns.sh >> /var/log/ddns/ddns.log
In order to verify the creation of the job, the following command will list all cron
jobs under the ddns
service account.
crontab -u ddns -l
Conclusion
That’s about it. Whenever your public IP address changes, this script will update your Namecheap DDNS record. If you want to see what the script is doing, you can run the following command to look at the log.
cat /var/log/ddns/ddns.log