This document outlines how we setup our DNS data. We use cvs(1) and make(1) to ensure reliable operation while allowing for multiple hostmasters and automated tools.
The scheme relies on rather rigid rules for naming of zone files etc, but thanks to a simple script, converting from your old DNS setup to this method is quite painless.
The advantages of this setup are:
Once setup, CVS is very simple to use. The hostmaster might use
$ cd $ cvs checkout namedto obtain a private copy of the DNS data for editing. Making changes there after is simply a case of:
$ cd ~/named $ cvs update # pick up changes made by others $ vi hosts/crufty.net.db # make changes as desired $ cvs diff -cb # see what we have changed $ cvs commit -m"log comment" # check in the changes.If the log comment is omitted then the user is dropped into an editor ($EDITOR or vi) to make a log entry. If multiple files have been changed, they can be committed separately or as a group by adding file names to the commit command.
See here for more detail on using CVS.
For those on systems other than NetBSD or similar, http://www.crufty.net/pub/sjg/bmake.tar.gz is an autoconf'd version of the latest NetBSD make and will compile on many different UNIX systems.
Since make(1) is most conveniently driven by filename suffixes, we use the convention that the SOA file has an extension of .soa and that the included zone file has an extension of .db
An example always helps.
# make depend dnsdeps -N named.boot # touch ns.list # make updsoa hosts/crufty.net.soa updsoa rev/203.12.250.soa bouncednsIn the above, we ran make depend which uses dnsdeps(1) to ensure that all the dependencies of all the (primary) zone files referenced by named.boot are recorded. We then simply touch a file that some zones are dependent on and run make, which runs updsoa to update the serial number of zones that were dependent on ns.list.
Note, we used to have make(1) perform a cvs update automagically, but since any update will not be reflected in the dependency graph, we don't do this any more. See upddns below.
# rm hosts/crufty.net.soa # make updsoa hosts/crufty.net.soa bouncednsIn the above example, we remove one of the .soa files - to simulate an accident or perhaps a new .db file. When we then run make the .soa file is created automagically.
On systems that do not have perl(1) and cannot install it, we have shell scripts that provide the same functionality though at lower speed. The performance really only matters though, when generating PTR records for a large domain.
hosts/crufty.net.soa: \ hosts/crufty.net.db \ mx/crufty.net \ ns.list rev/203.12.250.soa: \ rev/203.12.250.db \ ns.list .zones: \ hosts/crufty.net.soa \ named.local \ rev/203.12.250.soa
Updrev works with data gleaned from a named_dump.db file and respects any existing PTR records provided that a matching A record exists. This makes updrev(8) more suitable for interaction with human hostmasters than our old mkrev.pl(8) tool. That is, updrev can be used to initially generate the reverse maps, and a human can then edit them to override the tool's choices and this intervention need not be repeated (unlike mkrev.pl). For sites that object to using perl(1) updrev.sh is a shell version of the same logic.
The tool is reasonably efficient, updrev.pl(8) can generate or update reverse maps at about 10,000 A records per minute.
Note that updrev only supports the DNS arrangement described in this document.
For this reason the bouncedns command above, simply touches a flag file to indicate that a DNS restart is needed. The same command is then run regularly from cron(8) such that if the flag file exists, named is restarted.
Note that it is worthwhile coordinating the cron jobs on secondary servers such that the bouncedns jobs do not all run at the same time.
As long as administrators are aware of the issue, the .nocvs can be removed and automated updates allowed. When an extensive set of changes are to be performed, .nocvs should be created in the live tree to ensure no automated updates will occur until the commits are complete.
Truely rigid sites might only allow updates of the live tree to be done manually and under change management.
For many sites the cronjob modules included with DNS Magic, should prove quite useful.
Eventually we may provide a self contained DNSMagic archive.
$ mkdir /tmp/named $ cd /tmp/named $ dns_convert.sh $ ls makefile hosts/ mx/ ns.list db.auth named.boot named.ca rev/ secondary/ $ cvs import -m"original data" named NAMED NAMED_0 $ su # cd /var # mv named named.old # cvs checkout named ... # cd named # make dnsdeps -N named.boot updsoa hosts/crufty.net.soa bouncedns ... # cd /etc # mv named.boot named.boot.old # ln -s /var/named/named.boot . # /etc/rc_d/bouncedns -f Stopping named Restarting named # exit $ cd $ cvs checkout namedThere after, changes you make in ~/named can be cvs committed and a cvs update and make in /var/named will sort it out.
Simply add a line like:
^named/ /usr/local/share/dns/regressto $CVSROOT/CVSROOT/commitinfo, and that command will be run when ever a commit is made to $CVSROOT/named. Most systems support starting named(8) with an alternate port and bootfile. This allows named to be started and given a chance to verify its input, without interfering with normal DNS service.
Note that if a large number of files have been updated, CVS may fail to invoke the regression suite due to too many args or rather to long a command line. This then causes the commit to fail. The only work around is to commit the files in several batches. The exact number of files which is too many is system dependent.
An alternative is to modify CVS such that the pre-commit filter is fed its args via stdin rather than the command line. We have a patch which does this if the filter command begins with xargs or its full path. For sites with more than 200 in-addr zone files this is a good option.
The basic modules are (most of these do nothing if NO_NAMED is set in the environment):
As mentioned above, if the variable NO_NAMED is set in the environment, then the above tests do very little. Presumably other tests will check the validity of the data in this case. Note that if a group of changes are to be committed individually, then loading up named each time is over-kill. This is the main reason for the variable NO_NAMED, it is set by regress.sh if it detects that it is not the first child of a CVS process and that the original did not fail.
Note that BIND-9 versions prior to 9.3 are not supported.
For BIND-8 or BIND-9 dns/regress.d/S10regress.sh knows to use named.conf.test and to look for the named_dump.db in named's working directory.
;; this zone file has a number of problems that dns/regress ;; will vomit on. See how many tries it takes to fix them all ;; --sjg @ IN SOA ns.crufty.net. hostmaster.crufty.net. ( 1.2 ; Last changed by - sjg 7200 ; Refresh 2 hour 1800 ; Retry 1/2 hour 14400 ) ; Minimum $INCLUDE n.list cool IN A 192.168.168.42 IN MX 100 cool foo IN A 192.168.168.1 IN A 192.168.168.2 IN A 192.168.168. IN MX foo fool IN CNAME foo IN MX fooA first run though regress produces:
Jul 21 12:36:27 gate named[10296]: /tmp/tnamed/named.boot.test: line 16: zone "test." has trailing dot Jul 21 12:36:27 gate named[10648]: n.list: No such file or directory Jul 21 12:36:27 gate named[10296]: test.db:5: decimal serial number interpreted as 10002 Jul 21 12:36:27 gate named[10296]: test.db: line 21: unexpected EOF Jul 21 12:36:27 gate named[10296]: test.db: line 21: database format error (ns.crufty.net.) Jul 21 12:36:27 gate named[10296]: primary zone "test" rejected due to errors (serial 10002)After fixing those...
;; this zone file has a number of problems that dns/regress ;; will vomit on. See how many tries it takes to fix them all ;; --sjg @ IN SOA ns.crufty.net. hostmaster.crufty.net. ( 1997072100 ; Last changed by - sjg 7200 ; Refresh 2 hour 1800 ; Retry 1/2 hour 3600000 ; Expire 1000 hours 14400 ) ; Minimum $INCLUDE ns.list cool IN A 192.168.168.42 IN MX 100 cool foo IN A 192.168.168.1 IN A 192.168.168.2 IN A 192.168.168. IN MX foo fool IN CNAME foo IN MX fooRegress says:
Jul 21 12:43:29 gate named[10472]: test.db: line 17: database format error (192.168.168.) Jul 21 12:43:29 gate named[10472]: test.db: line 18: database format error (foo) Jul 21 12:43:29 gate named[10472]: test.db: line 21: database format error (foo) Jul 21 12:43:29 gate named[10472]: primary zone "test" rejected due to errors (serial 1997072100)Ok, this time for sure...
;; this zone file has a number of problems that dns/regress ;; will vomit on. See how many tries it takes to fix them all ;; --sjg @ IN SOA ns.crufty.net. hostmaster.crufty.net. ( 1997072100 ; Last changed by - sjg 7200 ; Refresh 2 hour 1800 ; Retry 1/2 hour 3600000 ; Expire 1000 hours 14400 ) ; Minimum $INCLUDE ns.list cool IN A 192.168.168.42 IN MX 100 cool foo IN A 192.168.168.1 IN A 192.168.168.2 IN A 192.168.168.3 IN MX 10 foo fool IN CNAME foo IN MX 10 fooOops:
Jul 21 12:45:32 gate named[10529]: fool.test has CNAME and other data (invalid)Well at least now named is happy.
We put the following into named.conf:
logging { // we want to know about all problems // so that the regression suite will pick them up // we only need this on the master. category cname { default_syslog; }; category lame-servers { default_syslog; }; category insist { default_syslog; }; // we may also want some of these category xfer-out { default_syslog; }; category statistics { default_syslog; }; category update { default_syslog; }; category security { default_syslog; }; category os { default_syslog; }; category notify { default_syslog; }; category response-checks { default_syslog; }; category maintenance { default_syslog; }; };
It is possible to run both BIND-8 and BIND-9. We used to run BIND-9 on our gateway NS, and BIND-8 on internal NS's. In particular the NS where cvs commits are done ran BIND-8. On the gateway NS, we use two views:
// master for our external view view external { match-clients { ! internals; any; }; zone "." { type hint; file "named.ca"; }; zone "127.in-addr.arpa" { type master; file "named.local"; }; include "primary.zones"; include "secondary.zones"; }; // a slave server for our internal view view internal { match-clients { internals; }; zone "127.in-addr.arpa" { type master; file "named.local"; }; include "internal-slave.zones"; };It requires some kludges in Makefile.inc to convert the BIND-9 named.conf into a named.conf.test that is acceptible to BIND-8 so that the regression suite can still be used, but it works.
Upgrading to BIND 9.3 is recommended.