Dynamic Updates

Chances are your public IPv4 address changes from time to time.

We have to make sure that if this happens our DNS records get updated as quickly as possible to the new address.

Since we run our own DNS server, we can do this directly on the server, or more precisely in its MySQL database.

The MySQL query to do this looks like this:

UPDATE `records` SET content = "<NEW IP ADDRESS>", change_date "<TIME_NOW>"
    WHERE `type` = "A" AND `content` = "<OLD IP ADDRESS>";

This replaces the IPv4 addresses on every host record (A) in all domains we are authoritative for.

To do this automatically a small script can be started as cronjob every five minutes, which does the following:

  1. Check public IP address;

  2. Compare to known address;

  3. If its different, update database records, else exit;

  4. Exit;

Update Shell Script

 1#!/bin/bash
 2#
 3# PowerDNS Dynamic IP Updates
 4
 5# Host to query for stored public IP address
 6KNOWN_HOST=www.example.net
 7
 8# Router to query for current IP
 9ROUTER_IP=192.0.2.1
10ROUTER_USER=pdns_updater
11ROUTER_SSH_KEY=$HOME/.ssh/pdns_updater_rsa
12ROUTER_IFC=sfp1
13# Display IP Address on MicroTik Routers
14ROUTER_CMD=":put [/ip address get [find interface=\"$ROUTER_IFC\"] address];"
15
16# Our domain name master server to ask stored IP
17DNS_MASTER=2001:db8::41
18
19# MySQL server
20MYSQL_HOST=localhost
21MYSQL_USER=pdns_updater
22MYSQL_PASSWORD=********
23MYSQL_DB=pdns
24
25# Ask router for current interface IP
26READ_WAN_IP=`ssh $ROUTER_USER@$ROUTER_IP -i $ROUTER_SSH_KEY $ROUTER_CMD`
27
28# Strip netmask ("/24") from IP
29CURRENT_IP=`expr "$READ_WAN_IP" : '\([0-9\.]*\)'`
30
31# Get current DNS address
32DNS_IP=`dig +short @${DNS_MASTER} $KNOWN_HOST A`
33
34# Compare the IPs
35if [ $CURRENT_IP == $DNS_IP ]; then
36	#echo "*** IPs are the same :) ***"
37	exit
38else
39	echo "*** ALERT! Our WAN IP has changed! ***"
40	echo "Old IP Address: $DNS_IP"
41	echo "New IP Address: $CURRENT_IP"
42
43	# Update all records in PowerDNS database which use the old IP
44	mysql -u $MYSQL_USER -p${MYSQL_PASSWORD} -h $MYSQL_HOST $MYSQL_DB -e \
45	   "UPDATE \`records\` 
46	    	SET \`content\` = \"$CURRENT_IP\", \`change_date\`=\"`date +%s`\"
47        	WHERE \`type\` = \"A\" AND \`content\` = \"$DNS_IP\";" \
48    && echo "All DNS records with the old IP where updated." \
49    || echo "*** Update of DNS records failed! ***"
50fi

Database User

Create a database user pdns_updater with a very restrictive set of privileges:

CREATE USER 'pdns_updater'@'localhost'
    IDENTIFIED BY '********';
GRANT SELECT (`type`, `content`), UPDATE (`content`, `change_date`)
    ON `pdns`.`records` TO 'pdns_updater'@'localhost';
FLUSH PRIVILEGES;

Job Schedule

To define a cronjob which runs this every five minutes:

$ crontab -e
*/5 * * * * /$HOME/bin/pdns_update.sh