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:
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.
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?
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.
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:
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.