DNS Zone Compare

Having performed hundreds of DNS migrations of all sorts of size and shape, I can't overemphasize the importance of performing post-migration zone-by-zone resource record validation and verification. Customers used to be amazed at the results of such a detailed check. Now, they simply demand it and expect it. I used to perform these zone-by-zone checks using personally developed scripts written in Perl, Ruby, Python, and even Java. These scripts have served me well over time. Nowadays, the zones are bigger, and there are more zones to check than ever. It also seems that I have less time to actually perform these checks. That's the point of this quick blog entry - How to compare zones faster and possibly more accurate.

First, why not continue with Perl, Ruby, Python, or Java? They're slower than some of the new C-compiled tools that are now available that make the job easier. I've morphed my scripts over time, starting out with scripts that performed DNS "playback", i.e. the tool would issue actual DNS queries for each record, and compare the response, with the source data. This was REALLY SLOW. Cool to watch - like watching grass grow. Next, the scripts were improved to load both zones that I was comparing and used an iterator comparing A to B and B to A. Still slow by today's standards.

Enter LDNS and some support tools that ship with ISC Bind. The rest of this blog entry is how I've approached this more recently. First, I should talk about the platform. I performed DNS Zone comparisons on a modern Linux System, RHEL/CENTOS/FEDORA. Start by installing LDNS (maintained by NLnet::Labs)

yum install ldns ldns-utils
Ensure this same system has ISC Bind Utilities installed as well. This can be done by:
yum bind-utils

Once these two packages are installed, we can start to compare two different copies of the same zone to find any differences. At first, I started off using ldns-compare-zones right away but quickly ran into odd unparsable issues with some of the DB files. To get around this, I found that both named-checkzone and named-compilezone can perform what's called "canonicalization" of the data. Basically the scripts parse, reformat and sort the data in these zone files, making them easier to compare. These are some of the things that these CLIs do:

  • Canonicalize @ or ORIGIN, making all domain names dot-terminated FQDNs
  • Insert CLASS
  • Format all RRs w/ TTLs
  • Format whitespace
  • Sort RR by name

To canonicalize the db file for zone.com in the pre/ subdirectory, perform the following:

named-checkzone -D -o db.zone.com zone.com pre/db.zone.com
The above command reads in the zone file for zone.com in pre/db.zone.com, parses the data and outputs a new canonicalized zone file, db.zone.com, in our current working directory. For my last migration, I really needed to do this for both the pre- and post-migration zone file. I ended up doing something like so:
named-checkzone -D -o prec/db.zone.com zone.com pre/db.zone.com named-checkzone -D -o postc/db.zone.com zone.com post/db.zone.com

The above command wrote new canonicalized output files into prec or pre-canonical AND postc or post-canonical directories. I now had pre- and post-migration normalized data that was ready to compare. To perform the compare, I chose to use the LDNS CLI called ldns-compare-zones. The following command can be used to compare zones:

ldns-compare-zones prec/db.zone.com postc/db.zone.com zone.com +0 -0 ~0

The above command and its output, show that both zones are equivalent. No records are missing from them, and no records are different. To interpret the output, let's discuss the columns:

  • zone.com - the name of the zone that was compared
  • +0 - this means that there are no new additional records in the postc/db.zone.com present in prec/db.zone.com
  • -0 - this means that no records were removed from postc/db.zone.com when compared with prec/db.zone.com
  • ~0 - this means there were no records that had different values between the two
Now, to put it all together, here's what I did:
#!/bin/bash 

PRE=/var/dns/pre 
POST=/var/dns/post 
PREC=/var/dns/prec 
POSTC=/var/dns/postc 

for zone in `cat zonefile.txt` 
do 
   named-checkzone -D -o ${PREC}/db.${zone} ${zone} ${PRE}/db.${zone} > /dev/null 2&>1 
   named-checkzone -D -o ${POSTC}/db.${zone} ${zone} ${POST}/db.${zone} > /dev/null 2>&1 
   ldns-compare-zones ${PREC}/db.${zone} ${POSTC}/db.${zone} | tee /var/log/dns-compare.log 
done

There's nothing fancy about this... It's accurate, reliable, and reasonably fast. You may have to tweak the output of the ldns-compare-zones script depending on what your needs are. One of the features of ldns-compare-zones is that you can pass the -a flag and get all the various RRs assuming you have discrepancies. You could tweak the above script to perform two (2) passes on zones that exhibit differences, passing the -a on the 2nd pass to get the details. I think this will be my new approach from now on. Let me know what you think...

Add comment


Security code
Refresh