I’m in an environment in which a number of outward-facing authoritative DNS servers are fed by hidden primary servers; nothing special. What is perhaps a bit special is that all these servers are hand-crafted: zone definitions and BIND configurations are lovingly hand-edited, and as such, there is little control over which zones are actually valid, which of the servers are correctly configured, etc.
What I need on the “outside” DNS servers is a list of zones which have been configured on the hidden primaries, so that I can automate verification: are all zones configured, are any missing, etc. But there’s a problem here: I’m not the copy-and-paste type of person: I want delivery of this zone list automated.
What makes this a bit tricky is that the only communications channel available to me for transporting data between the outward-facing servers and the internal hidden primaries or vice versa, is the DNS ports: port 53 UDP and TCP, so I thought I’d just zone-transfer a list of zones out in its own zone. Crazy but easy. :-) I’m going to show you how I’m solving the problem. First of all, let me show you what I’m talking about.
On the left of the following diagram is a hidden primary BIND name server. On the right, is a set of firewalls (the thick red line) and then come the outward-facing name servers. (Their brand is irrelevant; it suffices that they are capable of transferring zones from the left-hand-side name server via the DNS zone transfer (AXFR) protocol.)
What I need is a list of zones configured on the hidden primary servers (seen on the left in
the diagram above). I could use the usual Unix toolbox to parse
and its included files, but I thought I’d rather query BIND itself, through
its statistics server.
You may know that BIND 9.5 introduced a built-in
statistics server you enable when building the server. The statistics
server provides a large variety of data in XML format you can retrieve
directly via HTTP. The host and port on which the statistics server listens
and access controls to it are configured in
named.conf. For example, the
tell BIND to accept HTTP connections to its statistics server at any
of the system interfaces on port 8053 for the addresses in the “
In fact if I point my Web browser at the host:port combination configured
for BIND (which for me would be
http://127.0.0.1:8053/) I’ll see a nicely
(oh, my eyes) formatted display of the data. This is a tiny excerpt showing
some of my configured zones:
(The formatted output you see above in the
browser is created by an XSL style sheet which is also provided by the
BIND name server – look closely at the second line of what is output in the
example below.) The plain XML looks different of course; the first few lines
of what I get when I retrieve the XML with an invocation of
http://127.0.0.1:8053/ look like this:
What I need from the XML is a list of zones, which is easier said than done. In BIND zones may be contained in views, and I may have the same zone in two (or more) different views. So, what I really want is a list which contains the zone name, the view name, the class and the zone’s current SOA serial number.
I first tried parsing the XML produced by the BIND statistics server with Perl, but that was much too slow for me, so I created bzl, a small and very fast libxml2-enabled C program that grabs the XML statistics directly via HTTP from BIND, uses Xpath to extract the zones and prints those out one per line. An example output is:
Note how BIND’s internal automatically configured zones usually have
an SOA serial number of 0. Also note, the zone
foo.bar has a dash for the
serial number – the zone can’t be loaded because it’s an SDB zone and
it’s back-end isn’t with me in the hotel (although it should be).
Each zone name retrieved by bzl becomes an unqualified domain name.
The value of the TXT resource record is the
zone’s SOA serial retrieved from the XML out of the statistics server; it is
simply for informational purposes, but I could even use it for monitoring. An
additional resource record called
bzl-nzones is added to the zone file: it
contains the number of zones listed in this zone file.
And now? I said: the rest is trivial. I configure my hidden primary server to serve this zone to particular hosts only, of course:
And I configure the zone as a slave zone on the outward-facing name servers, taking particular care to protect the zone’s content. (Make sure you restrict in a view or via an ACL which clients may query the zone!) Let me show you how the zone will be transferred onto a slave:
A cron entry on the hidden primary creates the zone file and reloads the name server. If the zone has changed, BIND will notify its slave(s) which then re-transfer the zone.
Thanks to the BIND statistics server I have an easy way of listing zones configured in a BIND name server, and wrapping a few bits around that data, solves a problem I would have had difficulty solving otherwise.