zx23 blog

DYI DDNS With Nsupdate and Bind

We have 3 letter location codes that we use to identify remote sites and some of those sites are on a dynamic IP connection. As a result, we need a way to keep the DNS records for all locations up to date.

Yes, many DNS providers offer a free dynamic DNS service and most higher end xDSL & cable routers have a dynamic DNS client built. And they may even do a pretty good job once you invest a couple of minutes into getting it all setup, but where’s the fun in that.

Lets start by generating a TSIG (Transaction SIGnature) key pair, which we will use to authenticate our update to the nameserver. dnssec-keygen is part of the bind package on FreeBSD. Note that we’re using example.net. domain here, you must change it to the domain you want to update. For explanation of the other options refer to dnssec-keygen(8) manual.

1
2
% dnssec-keygen -a HMAC-MD5 -b 512 -n HOST example.net.
Kexample.net.+157+32671

This created two files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
% ls
Kexample.net.+157+32671.key
Kexample.net.+157+32671.private

% cat Kexample.net.+157+32671.key
example.net. IN KEY 512 3 157 iczhJ1Fmo24DeaiQ0sgeJjKV6jd2fgsugx8OdDhnEFvxdC2MqEHxeICPtw+DyuOnOmzVF6wOEKbwXdTwg6aXdQ==

% cat Kexample.net.+157+32671.private
Private-key-format: v1.3
Algorithm: 157 (HMAC_MD5)
Key: iczhJ1Fmo24DeaiQ0sgeJjKV6jd2fgsugx8OdDhnEFvxdC2MqEHxeICPtw+DyuOnOmzVF6wOEKbwXdTwg6aXdQ==
Bits: AAA=
Created: 20140815223024
Publish: 20140815223024
Activate: 20140815223024

Now we setup this key on our bind server to which we’ll be sending the zone update to. Create /var/named/etc/namedb/example.net.key with the following contents; the secret is the same as key in the files generated with dnssec-keygen previously:

/var/named/etc/namedb/example.net.key
1
2
3
4
key "example.net." {
    algorithm hmac-md5;
    secret "gdmqtLsXiI954A3RsmmhNedcBxYjl62UBhHux99NyuKICV7Gw5AvPx4lLELhuZYOEXGZBU7m1UgNEW36TM1A9g==";
};

Then configure your zone in /var/named/etc/namedb/named.conf to allow updates with the key we setup:

/var/named/etc/namedb/named.conf
1
2
3
4
5
zone "example.net" {
    type master;
    file "/etc/namedb/master/example.net.db";
    allow-update { key "example.net."; };
};

Now reload bind and lets move to testing this using the nsupdate tool:

1
2
3
4
5
6
7
# named-checkconf /var/named/etc/namedb/named.conf
# rndc reload

% nsupdate -k Kexample.net.+157+32671.private
> update delete lhr.example.net A
> update add lhr.example.net 86400 A 10.22.23.101
> send

You’ll notice that by default nsupdate doesn’t give any output, uneless there was an error. Instead, you can issue a answer command to nsupdate after send, to see the server response:

1
2
3
4
5
6
7
8
9
10
11
12
13
# nsupdate -k Kexample.net.+157+32671.private
> update delete lhr.example.net A
> update add lhr.example.net 86400 A 10.1.1.10
> send
> answer
Answer:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id:  32101
;; flags: qr; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1
;; ZONE SECTION:
;example.net.                      IN      SOA

;; TSIG PSEUDOSECTION:
example.net.               0       ANY     TSIG    hmac-md5.sig-alg.reg.int. 1408203323 300 16 BoDENzUS1d+yT0Fyd6fq6A== 32101 NOERROR 0

Ok, so thats all working as expected. The last step is to script something that would update DNS automatically whenever the external IP changes. Here’s a basic shell script, notice it doesn’t do any error handling :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh

myip=`ifconfig tun0 | awk '/inet/ { print $2 }'`
oldip=`dig +short @ns0.example.net lhr.example.net`
key="/etc/namedb/Kexample.net.+157+32671.private"

updatedns() {
    logger -t 'ddnsupdate' "Updating lhr.example.net: $oldip -> $myip"
    cat <<EOF | nsupdate -k "$key"
update delete lhr.z23.net A
update add lhr.example.net 86400 A $myip
send
EOF
}

if [ "$myip" != "$oldip" ]; then
    updatedns
fi

Comments