DNS with Dynamic Updates

reading time ( words)

I manage my own DNS servers for my personal projects and some of the causes I support. This gives me absolute control over the operation. Recently I decided to return to a 100% dynamically updated setup – where records can be added securely and in real time from anywhere. This post includes some notes on how to do this easily.

A nice thing about running my own servers is that I can easily roll mass changes such as the one in my earlier post on DNSSEC batch configuration. In this post I’ll discuss using RFC-2136 Dynamic Updates and RFC-2845 TSIG authentication to easily and securely manage records in your DNS zones in real time, from anywhere in the world.

TSIG and Keys

TSIG authentication is a great mechanism to authenticate DNS operations. I’ve been using it since forever to authenticate zone transfers between master and slave servers. TSIG requires generation of a key which will be used to authenticate requests such as “Please send me the contents of the zone lem.click” or “Please add this record to the zone”. Think of the TSIG key as a password and protect it accordingly.

TSIG does not encrypt requests in any way. Anybody watching your network traffic can see the request and its responses. All TSIG does is ensure that whomever issued a request, likely knows the right TSIG key.

For TSIG to work reliably, client and server should always be time-synchronized. This translates to using NTP properly, which you should be doing anyway. TSIG authentication employs a fudge factor, a time interval during which the signature can be replayed. With proper time synchronization in place, this time window can be very short.

For starters, generate a new TSIG key. I usually do this on my master DNS server, but the TSIG key is just a plain file you can move around. Just be careful of never transporting the key without encryption: Remember to treat it just like a password.

The following command generates a new sensible TSIG key in a format that Bind can read via its configuration file. I won’t go into crypto details in this post, as this is not required for a simple configuration. Make sure that permissions on this file are restricted.

tsig-keygen -a hmac-sha512 -r /dev/urandom mykey > /etc/bind/mykey.conf
chown root:bind /etc/bind/mykey.conf
chmod 0440 /etc/bind/mykey.conf

To complete the setup of the new key, add the following line to your Bind configuration (in my case, /etc/bind/named.conf.local):

include "/etc/bind/mykey.conf";

Then proceed to reconfigure or restart your instance of Bind

rndc reconfig

At this point Bind should be running normally and the new TSIG key should be known to its configuration file as “mykey”.

Configuring Dynamic Updates

For our setup, we want this key to allow only dynamic updates. We’ll have to modify the configuration of the zones we wish to enable for dynamic updates via the update-policy on line 4 below:

1
2
3
4
5
6
7
zone "lem.click" {
  type master;
  ⋮
  update-policy { grant mykey zonesub ANY; };
  allow-transfer { ⋯ };
  also-notify { ⋯ };
};

In this case, I’m allowing dynamic update transactions to any record anywhere under lem.click provided a valid TSIG authorization with the key we just created, mykey.

At this point, we can reconfigure Bind again:

rndc reconfig lem.click

If all went well, your next best friend will be the nsupdate command, which we’ll use to add and remove DNS records from now on.

nsupdate is a very handy program that can generate and send signed dynamic update requests to your name server. Updates are constructed from instructions that can be provided through the standard input or via files. The updates themselves can include prerequisites, conditions that must be met for the update to take place. For complex update cases, go ahead and read the documentation for the nsupdate command. I’ll explain the two simplest use cases below to get you started.

For the examples below, I securely transferred the contents of the mykey.conf file we generated to my home directory, restricting the file access so that only I can read it.

Adding records with nsupdate

In the example below, I add a simple test record to the DNS zone that I configured above. I simply invoke nsupdate and tell it where to find the TSIG key.

In many cases nsupdate is smart enough to know where to send the dynamic updates. It’ll look into the SOA record of your zone, which usually contains the name of your master DNS server. If this does not suit your configuration (and it often doesn’t, for a variety of reasons that range from vanity to an excess of cut & paste), you can tell it which name server to send the request to, as in line 2 of the example.

Line 3 is an unconditional add request. We’re asking for the name blogposttest.lem.click to be created, pointing to the IP address 127.0.0.1, with a 10 seconds time to live. Multiple requests can be added and will go together. This is how you would add prerequisites, which would execute transactionally (either the prerequisites are met and the updates completed, or the updates are not attempted).

When your update request is complete – and ours is, because it’s a very simple single-record add – you issue the send command in line 4, which causes nsupdate to pack the request and send it to the server. If everything goes well, then there’s no output.

1
2
3
4
5
nsupdate -k ~/mykey.conf
> server ns1.libertad.link
> update add blogposttest.lem.click 10 in a 127.0.0.1
> send
> ^D

Sometimes things can go wrong, in which case an error will be reported as in line 5 below. Can you tell what went wrong in this case?

1
2
3
4
5
nsupdate -k ~/mykey.conf
> server 8.8.8.8
> update add lookabovethisline.lem.click 5 in a 127.0.0.1
> send
dns_request_getresponse: expected a TSIG or SIG(0)

You can use nsupdate in your own scripts as it will report success or failure as any other well behaved shell command. By putting the update in a file and requesting it from within a script, you can manage your DNS zones automatically.

Removing records with nsupdate

Removal of DNS records is actually very simple. Line 3 on the example below shows how to remove the record we created earlier.

1
2
3
4
5
nsupdate -k ~/mykey.conf
> server ns1.libertad.link
> update delete blogposttest.lem.click
> send
> ^D

Manual zone updates

As your zones can now be updated at any time, there are some things that will be slightly different. As Bind processes the updates, it’ll change the zone files.

To safely edit your zone files, you need to use the rndc freeze command. This tells Bind that you’ll be changing the zone file, so dynamic updates will not clobber your work. After you’re done done with your changes, you’ll naturally thaw the zone. This example makes it more clear:

1
2
3
rndc freeze lem.click
# ⋯ go crazy editing your zone
rndc thaw lem.click

After a zone is thawed, Bind will reload it and dynamic updates will resume.

Do not waste time making the zone pretty as you edit, because Bind will surely have a differing opinion on what a pretty zone file means.