You know the drill: you provision a server with a master zone, and then you have to hop over to all secondary servers and add the slave zone to their configuration. You probably do that with some form of automation, or you use something slightly convoluted like what we’ve discussed previously in automatic provisioning of slave DNS servers. If your master and slave servers are BIND, you’re in luck: catalog zones will automate this for you within the BIND code itself: there’ll no longer be a need for “hacking” this to accomplish automatic provisioning of slave BIND servers.
Catalog zones are scheduled for release in BIND 9.11, but Evan Hunt graciously let me have a peek at a preview of the code, and I must warn you: first, this is a work in progres and hasn’t been released yet, and second (and more importantly) what I’m going to show you is the little I know of catalog zones. Be warned on both accounts! Update: Catalog zones are now in BIND 9.11.0a3.
Catalog zones are based on an Internet-Draft called draft-muks-dnsop-dns-catalog-zones-01, updated in draft-toorop-dnsop-dns-catalog-zones-01, which describes a method for automatic synchronization of zones among primary and secondary servers. The way this is works is that a zone contains the names (and optional metadata) describing which zones are to be used on slave (secondary) servers; these zone names in a catalog are called member zones. The secondary server obtains a copy of this catalog (via zone transfer (AXFR/IXFR)) and uses it to update the internal catalog of zones it ought to have; once it detects a change, it adds and slaves the member zone or deletes it, depending on whether the member zone was added or removed.
More concisely: we add one or more member zones to the catalog zone which is transferred to its slaves where it triggers the creation (or removal) of the member zones.
Let’s see how this works in practice with a preliminary copy of BIND which supports this.
Primary and secondary servers
On my primary test server, I add the following to named.conf
:
options {
directory "/etc/namedb";
...
server-id "authoritative";
allow-new-zones yes;
};
zone "catalog.example.com" {
type master;
file "catalog.example.com.db";
also-notify { 192.168.1.189; };
notify explicit;
};
Pay attention to the allow-new-zones directive. The catalog zone proper is mostly empty: mname, rname and NS are irrelevant, and the relative version
label specifies the catalog zone format version (currently “2”):
$TTL 3600
@ IN SOA . . 1 86400 3600 86400 3600
@ IN NS nop.
version IN TXT "2"
On a secondary (192.168.1.189) I have the following configuration:
options {
directory "/etc/namedb";
...
masterfile-format text;
zone-statistics yes;
server-id "slave";
allow-new-zones yes;
catalog-zones {
zone "catalog.example.com"
zone-directory "cat-zones"
in-memory no
default-masters { 192.168.1.188; };
};
};
zone "catalog.example.com" {
type slave;
file "catalog.example.com.db";
masters { 192.168.1.188; };
};
Here we also add allow-new-zones
so that the secondary can create member zones on the fly, and we use the new catalog-zones
stanza to define the primary server which holds our catalog. The default-masters
statement in the catalog-zones
stanza defines the default masters for its member zones.
The optional zone-directory
in “catalog-zones” allows master files for slaves provisioned by catalog zones to be stored in a directory other than the server’s working directory.
If in-memory
is set to yes
, local copies of master files on slaves will be stored in memory only and not on the file system.
Adding a new member zone
On the primary server I add a new zone, either dynamically with rndc addzone
or manually; I’ll use the former:
$ rndc addzone example.org '{type master; file "example.org";};'
$ dig +short @127.0.0.1 example.org soa
localhost. root.localhost. 22 10800 3600 604800 3600
From this point onwards, my primary server is ready to serve this master zone, but I want the secondary to serve the zone as well. Here’s what I do:
- I create a hash of a value. Basically any value should do, but it must currently be a valid BIND “nzf” hash. (The pre-alpha code I have does a little oops if it isn’t – I’m reporting that as we speak.) ISC is considering loosening this rule to allow me to use any label I wish to, but for interoperability with possible future implementations which comply with the draft, the hash is preferable.
- I then add a record to the catalog zone in which I name the member zone, bump its serial and reload the zone.
#!/usr/bin/env python
import dns.name # pip install dnspython
import hashlib
import sys
print(hashlib.sha1(dns.name.from_text(sys.argv[1]).to_wire()).hexdigest())
I run the above (thanks Witold!) to create the hash:
$ ./nzf.py example.org
47ac1a4d93b61fffdb4762c18c9e7d1a6b046d33
Another way of creating the hash (thanks, Peter) is:
$ printf '\7example\3org\0' | openssl sha1
47ac1a4d93b61fffdb4762c18c9e7d1a6b046d33
I’ve dropped a slightly more useful Python program here.
I now use that hash for step 2 and add a record containing the member zone:
@ 3600 SOA . . 2 86400 3600 86400 3600
@ 3600 IN NS nop.
47ac1a4d93b61fffdb4762c18c9e7d1a6b046d33.zones PTR example.org.
When I reload named on the primary (rndc reload
), I watch the transfer logs; they look very promising:
xfer-out: info: client 192.168.1.189#33213 (catalog.example.com): transfer of 'catalog.example.com/IN': AXFR-style IXFR started (serial 2)
xfer-out: info: client 192.168.1.189#33213 (catalog.example.com): transfer of 'catalog.example.com/IN': AXFR-style IXFR ended
xfer-out: info: client 192.168.1.189#45095 (example.org): transfer of 'example.org/IN': AXFR started (serial 22)
xfer-out: info: client 192.168.1.189#45095 (example.org): transfer of 'example.org/IN': AXFR ended
Note how first the catalog zone was transferred, and then our new example.org
zone.
So, what’s on the secondary?
$ ls -l /etc/namedb/cat-zones
-rw-r--r-- 1 root root 316 Jun 2 13:33 __catz__catalog.example.com_example.org.db
The __catz__*
file contains a copy of the slaved zone.
If I delete a member zone record from the catalog the process is reversed, and the secondaries remove the zone completely:
catz: updating catalog zone 'catalog.example.com' with serial 3
catz: deleting zone 'example.org' from catalog 'catalog.example.com' - success
catz: catz_delzone_taskaction: zone 'example.org' deleted
Metadata
BIND can use metadata on a zone which is transferred with the catalog: for example different master servers for a particular zone. This allows us to add a member zone to the catalog, which is configured with different master servers. So, on my primary server, I can add the following to the catalog: the first record specifies a member zone, and the second record the master(s) for that zone.
45414dc99ae20a84507692de578c0dd9d2522134.zones IN PTR ww.mens.de.
masters.45414dc99ae20a84507692de578c0dd9d2522134.zones IN A 192.168.1.130
When the secondaries get the catalog, this member zone is created and configured to obtain a copy of its data from the specified master server.
Another interesting feature is the possibility of setting ACLs on particular member zones within the catalog. This is done using Address Prefix Lists (RFC 3123):
allow-transfer.45414dc99ae20a84507692de578c0dd9d2522134.zones IN APL 1:192.168.1.189/32 1:192.168.1.206/32
The above example would allow transfers from the two addresses only.
Catalog zones may also carry TSIG signatures for member zones; this is described in the BIND ARM.
Closing
It goes without saying (so why am I?) that the catalog zone can be dynamically updated of course; this makes provisioning easy and ensures the SOA serial number is bumped.
We should probably all wish for other Open Source DNS server implementations to embrace catalog zones and add support for them in order to increase interoperability between DNS server software brands.
Updates:
- Peter van Dijk has created a proof of concept implementation which supports catalog zones in PowerDNS.
-
In a short introduction to Catalog Zones ISC talks about catz and demonstrates two small Python programs to add and delete zones programatically.