Long story short, I need to be able to access my home machine(s) from the Internet. Unfortunately my ISP provides me with a dynamic IP address so I need to jump to another hoop to get where I want. Luckily there’s a lot of Dynamic DNS providers out there, for reason(s) I opted to use Duck DNS.
So go over to Duck DNS and sign-in to create an account, claim your subdomain
and grab the token
.
I wrote a simple shell script that checks the external interface for any IP changes and when needed updates the Duck DNS service. The only external dependency is on curl
which you can install quickly using pkg_add
.
I tried to create a somewhat secure execution environment for this script:
cerberus# groupadd -g 5000 _duckdns
cerberus# useradd -g 5000 -u 5000 -s /sbin/nologin -d /nonexistent -c "DuckDNS Updater" _duckdns
The script expects a writable directory to maintain a state file, so that we only go out to the DuckDNS service if needed.
cerberus# mkdir /var/db/duckdns
cerberus# chown -R _duckdns:wheel /var/db/duckdns
cerberus# chmod 750 /var/db/duckdns
Copy the script and put store it in a sane location: /usr/local/sbin/duckdns-update
Now you just need to change three variables in the scripts configuration section:
- INTERFACE
- DUCKDNS_TOKEN
- DUCKDNS_DOMAIN
Setup the permissions so that only root can make changes to the code and the sandbox user _duckdns
can execute it.
cerberus# chmod 750 /usr/local/sbin/duckdns-update
cerberus# chown root:_duckdns /usr/local/sbin/duckdns-update
The next step is to have the script run periodically from cron
by issuing the crontab -e
command. I run it every 5 minutes, make sure to change it to fit your needs.
cerberus# su -s /bin/sh _duckdns
cerberus$ crontab -e
# DuckDNS
1-59/5 * * * * /usr/local/sbin/duckdns-update > /dev/null 2>&1
Finally we want to make sure that the _duckdns
user isn’t receiving mail by adding the following to /etc/mail/aliases
:
cerberus# grep _duckdns /etc/mail/aliases
_duckdns: /dev/null
After any modifications to the /etc/mail/aliases
file you need to run the newaliases
command.
The script:
#!/bin/sh
# Simple DuckDNS update script
# enable for debugging
#set -x
# default umask
umask 026
# configuration
INTERFACE="<<YOUR EXT INTERFACE>>"
DUCKDNS_TOKEN="<<YOUR TOKEN>>"
DUCKDNS_DOMAIN="<<YOUR DUCKDNS SUBDOMAIN>>"
# code - no need for changes here
CURL="/usr/local/bin/curl"
STATE_FILE="/var/db/duckdns/${INTERFACE}.ip"
NEW_IP=$(/sbin/ifconfig ${INTERFACE} | grep "inet " | awk '{print $2}')
PRV_IP=$(/bin/cat ${STATE_FILE})
DUCKDNS_UPDATE_URL="https:/www.duckdns.org/update?domains=${DUCKDNS_DOMAIN}&token=${DUCKDNS_TOKEN}"
LOGGER() {
/usr/bin/logger -i -p local0.info -t $0 "$1"
}
if [ ! -x ${CURL} ]; then
LOGGER "Fatal error; curl is not installed"
exit 1
fi
if [ ! -w $(dirname ${STATE_FILE}) ]; then
LOGGER "Fatal error; state directory not writable"
exit 1
fi
if [ "${NEW_IP}" != "${PRV_IP}" ]; then
LOGGER "Detected a new IP for ${INTERFACE}: ${NEW_IP}"
STATUS=$(${CURL} --insecure --silent ${DUCKDNS_UPDATE_URL})
if [ ${STATUS} == "OK" ]; then
LOGGER "DuckDNS update successful"
LOGGER "Updating state file: ${STATE_FILE}"
echo ${NEW_IP} > ${STATE_FILE}
else
LOGGER "DuckDNS update failed"
exit 1
fi
fi
# EOF
I guess that’s all for today :).