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
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash
#
# PowerDNS Dynamic IP Updates

# Host to query for stored public IP address
KNOWN_HOST=www.example.net

# Router to query for current IP
ROUTER_IP=192.0.2.1
ROUTER_USER=pdns_updater
ROUTER_SSH_KEY=$HOME/.ssh/pdns_updater_rsa
ROUTER_IFC=sfp1
# Display IP Address on MicroTik Routers
ROUTER_CMD=":put [/ip address get [find interface=\"$ROUTER_IFC\"] address];"

# Our domain name master server to ask stored IP
DNS_MASTER=2001:db8::41

# MySQL server
MYSQL_HOST=localhost
MYSQL_USER=pdns_updater
MYSQL_PASSWORD=********
MYSQL_DB=pdns

# Ask router for current interface IP
READ_WAN_IP=`ssh $ROUTER_USER@$ROUTER_IP -i $ROUTER_SSH_KEY $ROUTER_CMD`

# Strip netmask ("/24") from IP
CURRENT_IP=`expr "$READ_WAN_IP" : '\([0-9\.]*\)'`

# Get current DNS address
DNS_IP=`dig +short @${DNS_MASTER} $KNOWN_HOST A`

# Compare the IPs
if [ $CURRENT_IP == $DNS_IP ]; then
	#echo "*** IPs are the same :) ***"
	exit
else
	echo "*** ALERT! Our WAN IP has changed! ***"
	echo "Old IP Address: $DNS_IP"
	echo "New IP Address: $CURRENT_IP"

	# Update all records in PowerDNS database which use the old IP
	mysql -u $MYSQL_USER -p${MYSQL_PASSWORD} -h $MYSQL_HOST $MYSQL_DB -e \
	   "UPDATE \`records\` 
	    	SET \`content\` = \"$CURRENT_IP\", \`change_date\`=\"`date +%s`\"
        	WHERE \`type\` = \"A\" AND \`content\` = \"$DNS_IP\";" \
    && echo "All DNS records with the old IP where updated." \
    || echo "*** Update of DNS records failed! ***"
fi

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