This chapter was commissioned by SAMS Publishing for "Red Hat Linux
7.2 Unleashed" in 2001. They graciously allowed me to reproduce the
text here (with minor edits).
Introduction
It is often convenient to refer to networked computers by name rather
than by IP address, and various translation mechanisms have been devised
to make this possible. The DNS (Domain Name System) is one such method,
now used almost universally on the Internet; this chapter introduces DNS
concepts and practice using BIND (Berkeley Internet Name Domain), the de
facto standard DNS software for UNIX.
Hostnames are merely a convenience for users. Communication with other
computers still requires knowledge of their IP addresses, and to allow
hosts to be referred to by name, it must be possible to translate a name
into an equivalent IP address. This process is called name resolution,
and is usually performed by software known as a resolver. Since it is a
very common operation, whatever translation method we use must be very
fast and reliable.
Hostname to address mappings were once maintained by the SRI (Stanford
Research Institute) in the hosts.txt file, each line of which contained
the name and address of a host. Anyone could obtain a copy of this file
via FTP and let their resolver use it locally. This scheme worked well
when there were only a few machines, but it quickly grew impractical as
more and more people began connecting to the Internet.
A lot of bandwidth was wasted in keeping the ever-growing hosts.txt file
synchronised between the increasing number of hosts. Name resolution was
progressively slowed down, since the resolver took longer to search the
list of hosts each time. Changes to the database took forever to make
and propagate, since the SRI was inundated by requests for additions and
changes.
DNS is designed to address these problems, and to provide a consistent,
portable namespace for network resources. Its database is maintained in
a distributed fashion, to accommodate its size and the need for frequent
updates. Performance and bandwidth utilisation are improved by the
extensive use of local caches. Authority over portions of the database
is delegated to people who are able and willing to maintain them in a
timely manner, so that updates are no longer constrained by the
schedules of a central authority.
DNS is a simple but delicate system which is vital to today's Internet.
Errors may manifest themselves in far from obvious ways, long after the
changes which caused them were made, often leading to unacceptable and
embarrassing service disruptions. An understanding of the concepts and
processes involved will help to make sure that your experiences as a DNS
admin are pleasant ones.
DNS concepts
We begin with a look at the ideas behind DNS, independent of the details
of the software used to implement it. An understanding at this level is
invaluable in avoiding the majority of problems, and in diagnosing and
quickly solving the ones which do occur. In the following overview, I
avoid several small details in the protocol, because they are not very
relevant to the everyday tasks of a DNS administrator. If you need more
information, consult the DNS standards, especially RFC 1034. (The RFCs
related to DNS are distributed with BIND. Red Hat 7.2 installs them in
/usr/share/doc/bind-9.1.3/rfc/.)
The domain namespace is structured as a tree. Each domain is a node in
the tree, and has a name. For every node, there are "Resource Records"
(RRs), each of which stores a single fact about the domain (Who owns it?
What is its IP address?). Domains may have any number of children, or
subdomains. The root of the tree is a domain named "." (like the "/"
root directory in a filesystem).
The resource records belonging to a domain each store a different type
of information. For example, "A" (Address) records store the IP address
associated with a name. NS (Name Server) records name an authoritative
name server for a domain. Some other common RRs are MX (Mail Exchanger),
SOA (Start of Authority), and PTR (Pointer). They are discussed later.
Every node has a unique name which specifies its position in the tree,
just as every file has a unique path from the root directory to itself.
That is, one starts with the root domain ".", and prepends to it each
name in the path, using a dot to separate the names. The root domain has
children named "com.", "org.", "net.", "de.", and so on. They, in turn,
have children named "ibm.com", "wiw.org.", and "gmx.de". In general, a
fully-qualified domain name (FQDN) like "foo.example.com." is like the
path "/com/example/foo". (Notice how the trailing dot in an FQDN is
often omitted.)
Information about the structure of the tree, and the associated resource
records, is stored by programs called name servers. Every domain, has an
authoritative name server which holds a complete local copy of the data
for the domain (and its administrators are responsible for maintaining
the data). A name server may also cache information about parts of the
tree for which they have no authority. For administrative convenience,
name servers may delegate authority over certain subdomains to other,
independently maintained, name servers.
The authoritative name server for a zone knows about the name servers to
which authority over subdomains has been delegated. It may refer queries
about the delegated zones to those name servers. So, we can always find
authoritative data for a domain by following the chain of delegations of
authority from "." (the root domain) until we reach an authoritative
name server for the domain. This is what gives DNS its distributed tree
structure.
Users of DNS need not be aware of these details. To them, the namespace
is just a single tree, any part of which they can request information
about. The task of finding the requested RRs from the resource set for a
domain is left to programs called resolvers. Resolvers are aware of the
distributed structure of the database. They know how to contact the root
name servers (which are authoritative for the root domain), and how to
follow the chain of delegations until they find an authoritative name
server which can give them the information they are looking for.
At the risk of stretching the analogy too far, one can think of domains
as directories in a filesystem, and resource records as files in these
directories. The delegation of authority over subdomains is like having
an NFS filesystem mounted under a subdirectory: requests for files under
that directory would go to the NFS server, rather than this filesystem.
The resolver's job is to start from the root directory and walk down the
directory tree (following mount points) until they reach the directory
which contains the files they are interested in. (For efficiency, they
can then cache the information they find for some time.) This process is
examined in detail below.
In practice, there are several authoritative name servers for a domain.
One of them is the master (or primary) name server, where the domain's
data is held. The others are known as slave (or secondary) name servers,
and they hold automatically updated copies of the master data. Both the
master and the slaves serve the same information, so it doesn't matter
which one a resolver asks. The distinction between master and slave is
made purely for reasons of reliability, to ensure that the failure of a
single name server does not result in the loss of authoritative data for
the domain. As a bonus, this redundancy also distributes the network
load between several hosts, so that no one name server is overwhelmed
with requests for authoritative information.
(As a DNS administrator, it is your responsibility to ensure that your
name servers provide sufficient redundancy for your zones. Your slaves
should be far away from the master, so that power failures, network
outages, and other catastrophes do not affect your name service.)
Despite these precautions, the load on DNS servers would be crushing,
without the extensive use of local caches. As mentioned before, name
servers are allowed to cache the results of queries and intermediate
referrals for some time, so that they can serve repeated requests for
data without referring to the source each time. If they didn't do this,
root name servers, and the name servers for other popular zones, would
be contacted by clients all over the world for every name lookup,
wasting a huge amount of resources.
Name resolution in practice
Let us see what happens behind the scenes when a Web browser issues a
request for the IP address of www.ibm.com. Usually, the request is sent
to a local name server, which resolves the name, stores the result in
its cache, and returns the IP address. We will mimic the actions of our
resolver by using the incredibly useful dig utility to follow the chain
of delegations between zones until we find the A record we are looking
for. (Since most name servers would follow the delegations for us, we
use the "+norec" dig parameter to turn off recursion. That is, if the
name server does not know how to answer our query, it will not issue
further queries on its own.)
We randomly select one of the thirteen root name servers (ranging from
a.root-servers.net to m.root-servers.net; we picked e), and ask what it
knows about an A record for www.ibm.com:
$ dig @e.root-servers.net www.ibm.com A +norec
; <<>> DiG 9.1.3 <<>> @e.root-servers.net www.ibm.com A +norec
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52356
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 13
;; QUESTION SECTION:
;www.ibm.com. IN A
;; AUTHORITY SECTION:
com. 172800 IN NS A.GTLD-SERVERS.NET.
com. 172800 IN NS G.GTLD-SERVERS.NET.
com. 172800 IN NS H.GTLD-SERVERS.NET.
com. 172800 IN NS C.GTLD-SERVERS.NET.
com. 172800 IN NS I.GTLD-SERVERS.NET.
com. 172800 IN NS B.GTLD-SERVERS.NET.
com. 172800 IN NS D.GTLD-SERVERS.NET.
com. 172800 IN NS L.GTLD-SERVERS.NET.
com. 172800 IN NS F.GTLD-SERVERS.NET.
com. 172800 IN NS J.GTLD-SERVERS.NET.
com. 172800 IN NS K.GTLD-SERVERS.NET.
com. 172800 IN NS E.GTLD-SERVERS.NET.
com. 172800 IN NS M.GTLD-SERVERS.NET.
;; ADDITIONAL SECTION:
A.GTLD-SERVERS.NET. 172800 IN A 192.5.6.30
G.GTLD-SERVERS.NET. 172800 IN A 192.42.93.30
H.GTLD-SERVERS.NET. 172800 IN A 192.54.112.30
C.GTLD-SERVERS.NET. 172800 IN A 192.26.92.30
I.GTLD-SERVERS.NET. 172800 IN A 192.36.144.133
B.GTLD-SERVERS.NET. 172800 IN A 192.33.14.30
D.GTLD-SERVERS.NET. 172800 IN A 192.31.80.30
L.GTLD-SERVERS.NET. 172800 IN A 192.41.162.30
F.GTLD-SERVERS.NET. 172800 IN A 192.35.51.30
J.GTLD-SERVERS.NET. 172800 IN A 210.132.100.101
K.GTLD-SERVERS.NET. 172800 IN A 213.177.194.5
E.GTLD-SERVERS.NET. 172800 IN A 192.12.94.30
M.GTLD-SERVERS.NET. 172800 IN A 202.153.114.101
;; Query time: 819 msec
;; SERVER: 192.203.230.10#53(e.root-servers.net)
;; WHEN: Wed Sep 26 10:05:08 2001
;; MSG SIZE rcvd: 461
The "QUERY: 1, ANSWER: 0" in the response means that e.root-servers.net
did not know the answer to our question. It does know the authoritative
name servers for the "com" TLD, and it refers our query to them in the
"AUTHORITY" section. (Not too long ago, all the root-servers.net name
servers were themselves authoritative for the com TLD, but additional
delegations were recently introduced.)
The resolver's next step would be to select one of these listed servers
at random, to use the IP addresses mentioned in the "ADDITIONAL" section
of the response to connect to the server, and repeat the question. This
is what we do now, having chosen i.gtld-servers.net:
; <<>> DiG 9.1.3 <<>> @i.gtld-servers.net www.ibm.com A +norec
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61562
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 5, ADDITIONAL: 5
;; QUESTION SECTION:
;www.ibm.com. IN A
;; AUTHORITY SECTION:
ibm.com. 172800 IN NS INTERNET-SERVER.ZURICH.ibm.com.
ibm.com. 172800 IN NS NS.WATSON.ibm.com.
ibm.com. 172800 IN NS NS.ERS.ibm.com.
ibm.com. 172800 IN NS NS.ALMADEN.ibm.com.
ibm.com. 172800 IN NS NS.AUSTIN.ibm.com.
;; ADDITIONAL SECTION:
INTERNET-SERVER.ZURICH.ibm.com. 172800 IN A 195.212.119.252
NS.WATSON.ibm.com. 172800 IN A 198.81.209.2
NS.ERS.ibm.com. 172800 IN A 204.146.173.35
NS.ALMADEN.ibm.com. 172800 IN A 198.4.83.35
NS.AUSTIN.ibm.com. 172800 IN A 192.35.232.34
;; Query time: 8337 msec
;; SERVER: 192.36.144.133#53(i.gtld-servers.net)
;; WHEN: Wed Sep 26 10:06:46 2001
;; MSG SIZE rcvd: 240
Still 0 ANSWERs, but we are clearly getting closer. The response lists
the names and IP addresses of five authoritative name servers for the
"ibm.com" domain. (Notice the abnormally large query time. This tells us
that our choice of i.gtld-servers.net was, for some reason, a poor one.
Intelligent resolvers remember this fact, and would pick a different
server in future. BIND only does this for the root servers, though.)
We choose NS.WATSON.ibm.com, and repeat our question:
$ dig @NS.WATSON.ibm.com www.ibm.com A +norec
; <<>> DiG 9.1.3 <<>> @NS.WATSON.ibm.com www.ibm.com A +norec
;; global options: printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32287
;; flags: qr aa ra; QUERY: 1, ANSWER: 4, AUTHORITY: 5, ADDITIONAL: 5
;; QUESTION SECTION:
;www.ibm.com. IN A
;; ANSWER SECTION:
www.ibm.com. 1800 IN A 129.42.18.99
www.ibm.com. 1800 IN A 129.42.19.99
www.ibm.com. 1800 IN A 129.42.16.99
www.ibm.com. 1800 IN A 129.42.17.99
;; AUTHORITY SECTION:
ibm.com. 600 IN NS ns.watson.ibm.com.
ibm.com. 600 IN NS ns.austin.ibm.com.
ibm.com. 600 IN NS ns.almaden.ibm.com.
ibm.com. 600 IN NS ns.ers.ibm.com.
ibm.com. 600 IN NS internet-server.zurich.ibm.com.
;; ADDITIONAL SECTION:
ns.watson.ibm.com. 600 IN A 198.81.209.2
ns.austin.ibm.com. 86400 IN A 192.35.232.34
ns.almaden.ibm.com. 86400 IN A 198.4.83.35
ns.ers.ibm.com. 259200 IN A 204.146.173.35
internet-server.zurich.ibm.com. 1800 IN A 195.212.119.252
;; Query time: 441 msec
;; SERVER: 198.81.209.2#53(NS.WATSON.ibm.com)
;; WHEN: Wed Sep 26 10:08:21 2001
;; MSG SIZE rcvd: 304
NS.WATSON.ibm.com knew the answer to our question, and for the first
time, the response contains an "ANSWER" section which lists four A
records for www.ibm.com. Most resolvers pick one at random and return it
to the program which initiated the name resolution. This concludes our
search.
Reverse resolution
Given an IP address, it is often necessary to find the name associated
with it (while writing web server logs, for example). This process is
known as reverse resolution, and is accomplished with the help of an
elegant subterfuge. The problem is that IP addresses (like filenames)
are "backwards" from the DNS point of view. Since we can only associate
RRs with DNS names, we must find a way to write an IP address, with its
left-to-right hierarchy (129.42.18.99 belongs to 129.*), as a DNS name,
with a right-to-left hierarchy (ibm.com belongs to com).
We do this by reversing the order of the octets in the address, and then
appending ".in-addr.arpa" (a domain used exclusively to support reverse
lookups) to the result. For example, 129.42.18.99 would be written as
99.18.32.129.in-addr.arpa. PTR (Pointer) records associated with this
special name would then tell us the real name of the host to which the
IP belongs.
We can look for PTR records in the usual fashion, by following a chain
of delegations from a root server. We examine this process briefly by
resolving 203.200.109.66 (which is one of the dialup IP addresses which
my ISP assigns to its customers). We ask a root name server about
66.109.200.203.in-addr.arpa:
$ dig @a.root-servers.net 66.109.200.203.in-addr.arpa PTR +norec
; <<>> DiG 8.2 <<>> @a.root-servers.net 66.109.200.203.in-addr.arpa PTR +norec
; (1 server found)
;; res options: init defnam dnsrch
;; got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17284
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 3
;; QUERY SECTION:
;; 66.109.200.203.in-addr.arpa, type = PTR, class = IN
;; AUTHORITY SECTION:
203.in-addr.arpa. 1D IN NS SVC00.APNIC.NET.
203.in-addr.arpa. 1D IN NS NS.APNIC.NET.
203.in-addr.arpa. 1D IN NS NS.TELSTRA.NET.
203.in-addr.arpa. 1D IN NS NS.RIPE.NET.
;; ADDITIONAL SECTION:
SVC00.APNIC.NET. 2D IN A 202.12.28.131
NS.APNIC.NET. 2D IN A 203.37.255.97
NS.RIPE.NET. 2D IN A 193.0.0.193
;; Total query time: 432 msec
;; FROM: lustre to SERVER: a.root-servers.net 198.41.0.4
;; WHEN: Sun Sep 23 02:10:19 2001
;; MSG SIZE sent: 45 rcvd: 186
Notice that this output is from an old version of dig (one shipped with
BIND 8) which I happened to have installed. There are minor differences
in output format, notably that the "QUESTION" section is missing, and
that time intervals are specified in days and hours rather than seconds.
Continuing with NS.TELSTRA.NET, we have:
$ dig @NS.TELSTRA.NET 66.109.200.203.in-addr.arpa PTR +norec
; <<>> DiG 8.2 <<>> @NS.TELSTRA.NET 66.109.200.203.in-addr.arpa PTR
+norec
; (1 server found)
;; res options: init defnam dnsrch
;; got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11519
;; flags: qr ra; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 2
;; QUERY SECTION:
;; 66.109.200.203.in-addr.arpa, type = PTR, class = IN
;; AUTHORITY SECTION:
200.203.in-addr.arpa. 4D IN NS ns3.vsnl.com.
200.203.in-addr.arpa. 4D IN NS dns.vsnl.net.in.
;; ADDITIONAL SECTION:
ns3.vsnl.com. 19h48m53s IN A 203.197.12.42
dns.vsnl.net.in. 3h30m22s IN A 202.54.1.30
;; Total query time: 723 msec
;; FROM: lustre to SERVER: NS.TELSTRA.NET 203.50.0.137
;; WHEN: Sun Sep 23 02:11:51 2001
;; MSG SIZE sent: 45 rcvd: 132
And then:
$ dig @ns3.vsnl.com 66.109.200.203.in-addr.arpa PTR +norec
; <<>> DiG 8.2 <<>> @ns3.vsnl.com 66.109.200.203.in-addr.arpa PTR +norec
; (1 server found)
;; res options: init defnam dnsrch
;; got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 65340
;; flags: qr aa ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
;; QUERY SECTION:
;; 66.109.200.203.in-addr.arpa, type = PTR, class = IN
;; AUTHORITY SECTION:
200.203.in-addr.arpa. 1D IN SOA dns.vsnl.net.in.
helpdesk.giasbm01.vsnl.net.in. (
200001053 ; serial
1D ; refresh
2H ; retry
4w2d ; expiry
4D ) ; minimum
;; Total query time: 233 msec
;; FROM: lustre to SERVER: ns3.vsnl.com 203.197.12.42
;; WHEN: Sun Sep 23 02:15:07 2001
;; MSG SIZE sent: 45 rcvd: 114
What happened here?
If you've been reading the responses carefully so far, you would have
noticed that the status has always been "NOERROR", but this one has a
status of "NXDOMAIN" (Nonexistent Domain), and the "flags" section has
the "aa" (Authoritative Answer) flag. This means that the name we are
looking for is known not to exist. Notice the difference between this
and earlier responses. The response from the root name servers did not
have the aa flag set, and said "We don't know about this name, ask
somebody else". The authoritative answer from ns3.vsnl.com says "I know
that this name does not exist, and you might as well stop looking."
The administrators at vsnl.net.in have clearly not bothered to set up
PTR records for their dialup IP address pool. If they had, the response
would have looked like the ones we've seen before, and included a PTR
record. As it is, the response lists the SOA record for zone (including
the email address of the domain contact, helpdesk@giasbm01.vsnl.net.in,
should we choose to complain about broken reverse resolution).
What did the resolver learn?
We have a list of the names and addresses of the authoritative name
servers for the com TLD. In future, if we are asked to resolve, say
www.mcp.com, we can direct our query to gtld-servers.net from the start,
instead of wasting one query on a root server. However, the NS records
have an expiry time of "2D", so we must throw away the information once
it is older than two days.
Similarly, we know the authoritative name servers for ibm.com, for use
in queries involving ibm.com during the next two days. For instance, a
web browser might ask for the IP address of commerce.ibm.com when a user
clicks on a link on the IBM web page. We can save two queries by asking
NS.WATSON.ibm.com again. Of course, we also have the four A records for
www.ibm.com in our cache, and we can return one of them if the browser
asks us to resolve the name again.
All of this information (including that gleaned from the reverse
resolution process) can be cached until expiry and used to speed up
further queries.
We have also learned some things which a resolver cannot remember or
make use of. We can guess from the names, for instance, that the DNS
administrators at IBM have, as recommended, delegated their DNS service
to servers which are distant both geographically and network-wise. We
can see that IBM runs four web servers on the same network, perhaps to
gracefully handle the load. We know that the DNS administrators at VSNL
(a large ISP) aren't as conscientious as they could be, since they have
only two name servers for their entire domain, and don't have correct
reverse mappings.
DNS is endlessly fascinating, and a few hours of playing with dig is
well rewarded, both in terms of learning interesting things, and because
familiarity with dig queries and responses is very useful in debugging
problems with your own DNS setup.
BIND
BIND is the de facto standard DNS software suite for UNIX. It contains a
nameserver daemon (named) which answers DNS queries, a resolver library
which allows programs to make such queries, and some utility programs.
It is maintained by the ISC (Internet Software Consortium), whose web
site for it is at
http://www.isc.org/bind/.
There are three major versions of BIND in common use today: 4, 8, and 9.
The use of BIND 4 is now strongly discouraged (due to numerous security
vulnerabilities and other bugs), and will not be discussed here. BIND 8,
with many new features and bugfixes, is now quite widely deployed. It is
actively maintained, but still vulnerable to a variety of attacks.
This chapter discusses the use of BIND 9, which is now shipped with Red
Hat Linux. BIND 9 was rewritten from scratch, in an attempt to make the
code more robust and leave behind the problems inherent in the old code.
It is compliant with new DNS standards, and is claimed to represent a
substantial improvement in features, performance, and security.
BIND has often been criticised for its recurrent security problems, but
few alternatives exist. djbdns
is the only one which seems ready for production at the moment. Its use
is not discussed here.
At the time of writing, BIND 9.1.3 is the latest version. Installing it
is just a matter of installing the bind and bind-utils RPMs shipped with
the Red Hat 7.2 distribution. The former contains named and a wealth of
BIND documentation, while the latter contains, among other things, the
invaluable dig(1) utility. Of course, you may choose to compile BIND
yourself, in which case you can download the source distribution from
the ISC's web site and follow the build instructions therein.
Once you install the RPMs, the following directories are of special
interest. (If you installed from source, the files will be in the
locations you specified at configure time, with the default being
directories under /usr/local/.)
/etc/ Configuration files.
/usr/bin/ dig, host, nslookup, nsupdate.
/usr/sbin/ named, rndc, and various support programs.
/usr/share/doc/bind-9.1.3/ BIND documentation.
/usr/share/man/ Manual pages.
/var/named/* Zone files.
Basic configuration
We develop a minimal name server configuration below, and then expand it
as necessary to provide useful DNS service. The components which must be
configured are named (the nameserver daemon) and rndc (a control utility
which permits various interactions with a running named); often, it is
also necessary to configure the resolver software, as discussed later.
rndc.conf
rndc uses a TCP connection (on port 953) to communicate with named; for
authentication, it uses cryptographic keys to digitally sign commands
before sending them over the network to named. The configuration file,
/etc/rndc.conf by default, must specify a server to talk to, and the
corresponding key (which must be recognised by named) to use while
talking to it.
The only authentication mechanism currently supported is the use of a
secret, encrypted with the HMAC-MD5 algorithm, and shared between rndc
and named. The easiest way to generate a key is to use the dnssec-keygen
utility. Here, we are asking it to generate a 128-bit HMAC-MD5 user key
named rndc:
$ dnssec-keygen -a hmac-md5 -b 128 -n user rndc
Krndc.+157+14529
$ cat Krndc.+157+14529.private
Private-key-format: v1.2
Algorithm: 157 (HMAC_MD5)
Key: mKKd2FiHMFe1JqXl/z4cfw==
Two files are created, with .key and .private extensions respectively.
The "Key: " line in the .private file tells us the secret that rndc and
named need to share ("mKKd2FiHMFe1JqXl/z4cfw=="). Once we have this, we
can proceed to set up the very simple /etc/rndc.conf:
# Use the key named "rndc" when talking to the name server "localhost."
server localhost {
key rndc;
};
# Here are the details about the key named "rndc."
key rndc {
algorithm HMAC-MD5;
secret "mKKd2FiHMFe1JqXl/z4cfw==";
};
# Defaults.
options {
default-server localhost;
default-key rndc;
};
The file needs to have three sections: a "server" section which defines
a name server ("localhost") and specifies a key ("rndc") to be used while
communicating with it. The corresponding "key" section contains some
details about the key, as generated by dnssec-keygen. When we set up
named, we need to tell it the same information about the key, so that it
can authenticate requests made by rndc. The third section, "options,"
just sets up reasonable defaults (because the file may list multiple
servers and keys). Should you need it, the rndc(8) and rndc.conf(5)
manual pages contain more information.
named.conf
Our next task is to configure named itself. Its single configuration
file (/etc/named.conf) has syntax very similar to rndc.conf, but only a
small subset of its many available configuration directives, essential
to the configuration of a functional name server, are described here.
For a more exhaustive reference, consult the BIND 9 ARM (Administrator
Reference Manual; it is distributed with BIND and Red Hat 7.2 installs
it under /usr/share/doc/bind-9.1.3/arm/).
Only two sections are absolutely necessary: the "options" section must
tell named where the zone files are kept, and named must know where to
find the root zone ("."). We also set up a "controls" section to allow
suitably authenticated commands from rndc to be accepted. Since clients
(notably nslookup) often depend on resolving the name server's IP, we
set up the 0.0.127.in-addr.arpa reverse zone as well.
We start with a configuration file like this:
options {
# This is where zone files are kept.
directory "/var/named";
};
# Allow rndc running on localhost to send us commands.
controls {
inet 127.0.0.1
allow { 127.0.0.1; }
keys { rndc; };
};
# The same information as specified in rndc.conf
key rndc {
algorithm hmac-md5;
secret "mKKd2FiHMFe1JqXl/z4cfw==";
};
# Information about the root zone.
zone "." {
type hint;
file "root.hints";
};
# Lots of software depends on being able to resolve 127.0.0.1
zone "0.0.127.in-addr.arpa" {
type master;
file "rev/127.0.0";
};
The "options" section is where most of the interesting directives go.
For now, we will content ourselves with specifying the directory in
which named should look for zone files (as named in other sections of
the file). Other options will be introduced as required along the way.
Next, we instruct named to accepts commands from an authenticated rndc.
We add a "key" section, identical to the one from rndc.conf, and the
"controls" section saying that rndc will be connecting from localhost
and using the specified key. (You can specify more than one IP address
in the allow list, or use an access control list as described in the
Security section).
The "." zone tells named about the root name servers, whose names and
addresses are contained in the root.hints file. This information is used
to decide which root name server to consult initially (the decision is
frequently revised based on the server's response time). Although the
hints file can be obtained via FTP, the recommended, network-friendly
way to keep it synchronised is to use dig. We ask a root name server (it
doesn't matter which one) for the NS records of ".", and use the dig
output directly to create /var/named/root.hints:
# dig @j.root-servers.net. . ns > /var/named/root.hints
# cat /var/named/root.hints
; <<>> DiG 8.2 <<>> @j.root-servers.net . ns
; (1 server found)
;; res options: init recurs defnam dnsrch
;; got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6
;; flags: qr aa rd; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 13
;; QUERY SECTION:
;; ., type = NS, class = IN
;; ANSWER SECTION:
. 6D IN NS H.ROOT-SERVERS.NET.
. 6D IN NS C.ROOT-SERVERS.NET.
. 6D IN NS G.ROOT-SERVERS.NET.
. 6D IN NS F.ROOT-SERVERS.NET.
. 6D IN NS B.ROOT-SERVERS.NET.
. 6D IN NS J.ROOT-SERVERS.NET.
. 6D IN NS K.ROOT-SERVERS.NET.
. 6D IN NS L.ROOT-SERVERS.NET.
. 6D IN NS M.ROOT-SERVERS.NET.
. 6D IN NS I.ROOT-SERVERS.NET.
. 6D IN NS E.ROOT-SERVERS.NET.
. 6D IN NS D.ROOT-SERVERS.NET.
. 6D IN NS A.ROOT-SERVERS.NET.
;; ADDITIONAL SECTION:
H.ROOT-SERVERS.NET. 5w6d16h IN A 128.63.2.53
C.ROOT-SERVERS.NET. 5w6d16h IN A 192.33.4.12
G.ROOT-SERVERS.NET. 5w6d16h IN A 192.112.36.4
F.ROOT-SERVERS.NET. 5w6d16h IN A 192.5.5.241
B.ROOT-SERVERS.NET. 5w6d16h IN A 128.9.0.107
J.ROOT-SERVERS.NET. 5w6d16h IN A 198.41.0.10
K.ROOT-SERVERS.NET. 5w6d16h IN A 193.0.14.129
L.ROOT-SERVERS.NET. 5w6d16h IN A 198.32.64.12
M.ROOT-SERVERS.NET. 5w6d16h IN A 202.12.27.33
I.ROOT-SERVERS.NET. 5w6d16h IN A 192.36.148.17
E.ROOT-SERVERS.NET. 5w6d16h IN A 192.203.230.10
D.ROOT-SERVERS.NET. 5w6d16h IN A 128.8.10.90
A.ROOT-SERVERS.NET. 5w6d16h IN A 198.41.0.4
;; Total query time: 4489 msec
;; FROM: lustre to SERVER: j.root-servers.net 198.41.0.10
;; WHEN: Mon Sep 10 04:18:26 2001
;; MSG SIZE sent: 17 rcvd: 436
The zone file
The "zone 0.0.127.in-addr.arpa" section in named.conf says that we are a
master name server for that zone, and that the zone data is in the file
127.0.0. Before we examine our first real zone file in detail, let us
look at the general format of a resource record specification:
name TTL class type data
Here, "name" is the DNS name with which this record is associated. In a
zone file, names ending with a "." are fully qualified, while others are
relative to the name of the zone. In the zone example.com, "foo" refers
to the fully-qualified name "foo.example.com.". The special name "@" is
a short form for the name of the zone itself. If the name is omitted,
the last specified name is used again.
The TTL (Time To Live) field is a number which specifies the time for
which the record may be cached. This is explained in greater detail in
the discussion of the SOA record below. If it is omitted, the default
TTL for the zone is assumed. TTL values are usually in seconds, but
(since BIND 8) you can append an "m" for minutes, "h" for hours, or "d"
for days.
BIND supports different record classes, but for all practical purposes,
the only important class is IN, for Internet. If no class is explicitly
specified, a default value of IN is assumed; to save a little typing, we
don't mention the class in any of the zone files we write here.
The "type" field is mandatory, and names the RR in use, such as A, NS,
MX, or SOA. (We will only use a few of the existing RRs here. Consult
the DNS standards for a complete list.) The "data" field (or fields)
contains data specific to this type of record. The appropriate syntax
will be introduced as we examine the use of each RR in turn.
Here is the zone file (/var/named/rev/127.0.0) for the
0.0.127.in-addr.arpa zone:
$TTL 2D
@ SOA localhost. hostmaster.example.com. (
2001090101 ; Serial
24h ; Refresh
2h ; Retry
3600000 ; Expire (1000h)
1h) ; Minimum TTL
NS localhost.
1 PTR localhost.
The $TTL directive, which should begin every zone file, sets the default
minimum time-to-live for the zone to 2 days. (This is discussed further
in the section describing the SOA record).
The SOA record
The second line uses the special "@" name that we saw earlier. Here, it
stands for "0.0.127.in-addr.arpa", to which the SOA (Start of Authority)
record belongs. The rest of the fields (continued on the next few lines,
until the closing parenthesis) contain SOA-specific data.
The first data field in the SOA record is the fully-qualified name of
the master name server for the domain. The second field is the email
address of the contact person for the zone. It is written as a DNS name
by replacing the "@" sign with a "."; "foo@example.com" would be written
as "foo.example.com." (Note the trailing ".") Don't use an address like
"a.b@example.com", since it is written as a.b.example.com., and will
later be misinterpreted as a@b.example.com. It is important to ensure
that mail to this address is frequently read, for it is used to report
DNS setup problems and other potentially useful information.
The next several numeric fields specify various characteristics of this
zone. It is important to configure these values correctly, and hence to
understand them all. As shown in the comments (note that zone file
comments aren't the same syntax as named.conf comments), the fields are:
serial number, refresh interval, retry time, expire period, and minimum
TTL.
Serial numbers are 32-bit quantities which can hold values between 0 and
4,294,967,295 (2^32-1). Every time the zone data is changed, the serial
number must be incremented. This change serves as a signal to slaves
that they need to transfer the contents of the zone again. It is
conventional to assign serial numbers in the format YYYYMMDDnn, i.e. the
date of the change and a two-digit revision number (e.g. 2001090101).
For changes made on the same day, you increment only the revision (this
assumes, reasonably, that you don't make more than 99 changes to a zone
in one day); for changes on the next, the date is changed and the
revision number starts from 01 again.
The refresh interval specifies how often a slave server should check if
the master data has been updated. It has been set to 24 hours here, but
if the zone changes often, the value should be lower. (Slaves may reload
the zone much sooner if they and the master both support the DNS NOTIFY
mechanism. Most DNS software does.) The retry time is relevant only when
a slave fails to contact the master after the refresh time has elapsed.
It specifies how long it should wait before trying again (it is set to 2
hours here).
If the slave is consistently unable to contact the master for the length
of the expire period (usually due to some catastrophic failure), it
discards the zone data it already has and stops answering queries for
the zone. Thus, the expire period should be long enough to allow for the
recovery of the master name server. It has repeatedly been shown that a
value of one or two weeks is too short. 1000 hours (about 6 weeks) is
accepted as a good default.
The TTL (Time to Live) fields deserve more explanation. Every RR has a
TTL, which specifies how long it may be cached before the origin of the
data must be consulted again. If the RR definition does not specify a
TTL explicitly, the default TTL (set by the $TTL directive) is used
instead. This allows individual RRs to override the default TTL as
required.
The SOA TTL, the last numeric field in the SOA record, is now used to
determine how long negative responses (NXDOMAIN) should be cached (i.e.,
if a query results in an NXDOMAIN response, that fact is cached for as
long as indicated by the SOA TTL). Older versions of BIND used the SOA
minimum TTL to set the default TTL, but BIND 9 no longer does so. The
default TTL of 2 days and SOA TTL of 1 hour is recommended for cache
friendliness.
The values used above are good defaults for zones which do not change
often. You may have to adjust them a bit for zones with very different
requirements, in which case
http://www.ripe.net/docs/ripe-203.html
is recommended reading.
Other records
The next two lines in the zone file create NS and a PTR records. The NS
record has no explicit name specified, so it uses the last one, which is
the @ of the SOA record. Thus, the name server for 0.0.127.in-addr.arpa
is defined to be localhost. The PTR record has the name "1" which, when
qualified, becomes "1.0.0.127.in-addr.arpa" (which is how you write the
address 127.0.0.1 as a DNS name), and the name "localhost." (We shall
see some of the numerous other RR types when we later configure our name
server to be authoritative for a real domain.)
Logging
We now have all the elements of a minimal functioning DNS server, but
before we experiment further, some extra logging will allow us to see
exactly what named is doing. Log options are configured in a "logging"
section in named.conf, and the various options are described in detail
in the BIND 9 ARM.
All log messages go to one or more channels, each of which can write
messages to the syslog, to an ordinary file, to stderr, or to "null"
(that is, discard the message). There are categories of messages, such
as those generated while parsing configuration files, those caused by OS
errors, and so on. Your logging statement must define some channels, and
associate them with the categories of messages that you wish to see.
BIND logging is very flexible, but complicated, so we'll only examine a
simple log configuration here. If you need anything significantly more
complicated, refer to the ARM. The following addition to your named.conf
will set up a channel called custom which writes timestamped messages to
a file, and send messages in the listed categories to it.
logging {
channel custom {
file "/tmp/named.log"; # Where to send messages.
print-time yes; # Print timestamps?
print-category yes; # Print message category?
};
category config { custom; }; # Configuration files
category notify { custom; }; # NOTIFY messages
category dnssec { custom; }; # TSIG messages
category general { custom; }; # Miscellaneous
category security { custom; }; # Security messages
category xfer-out { custom; }; # Zone transfers
category lame-servers { custom; };
};
Keeping track of logs is especially important because syntax errors
often cause BIND to reject a zone and not answer queries for it, causing
your server to become "lame" (i.e., not authoritative for a zone for
which it is supposed to be).
Resolver configuration
The last thing we need to do before running BIND is to set up the local
resolver software. This involves configuring three files: /etc/hosts,
/etc/resolv.conf, and /etc/nsswitch.conf.
To avoid gratuitous network traffic, most UNIX resolvers still use a
hosts.txt-like file named /etc/hosts to store the names and addresses of
commonly used hosts. Each line in this file contains an IP address and a
list of names for the host. Add entries to this file for any hosts you
wish to be able to resolve independent of DNS.
/etc/resolv.conf specifies the addresses of preferred name servers, and
a list of domains relative to which unqualified names will be resolved.
A name server is specified with a line of the form "nameserver 1.2.3.4"
(where 1.2.3.4 is the address of the name server). You can use multiple
nameserver lines (usually up to 3). You can also use a "search" line to
specify a list of domains to search for unqualified names. A search line
like "search example.com example.net" would cause the resolver to
attempt to resolve the unqualified name "xyz", first as xyz.example.com,
and, if that fails, as xyz.example.net. Don't use too many domains in
the search list, because it slows down resolution.
A "hosts: files dns" line in /etc/nsswitch.conf will cause the resolver
to consult /etc/hosts before using the DNS during the course of a name
lookup. This allows you to override the DNS by making temporary changes
to /etc/hosts, which is especially useful during network testing. (Older
resolvers may require an "order hosts, bind" line in the /etc/host.conf
file instead.)
Running named
Finally! You can now start named with "/etc/rc.d/init.d/named start".
You should see messages similar to the ones below in the syslog (or
according to the logging configuration you have set up).
Sep 26 09:47:12 ns1 named[30507]: starting BIND 9.1.3
Sep 26 09:47:12 ns1 named[30507]: using 1 CPU
Sep 26 09:47:12 ns1 named[30509]: loading configuration from '/etc/named.conf'
Sep 26 09:47:12 ns1 named[30509]: listening on IPv4 interface lo, 127.0.0.1#53
Sep 26 09:47:12 ns1 named[30509]: command channel listening on 127.0.0.1#953
Sep 26 09:47:12 ns1 named[30509]: running
You can interact with this instance of named with rndc. Running rndc
without arguments displays a list of available commands, including the
ability to reload or refresh zones, dump statistics and the database to
disk, toggle query logging, and stop the server. (Unfortunately, rndc
does not yet implement all the commands which were supported by ndc, the
control program shipped with earlier versions of BIND.)
You should now be able to resolve 1.0.0.127.in-addr.arpa locally (try
"dig @localhost 1.0.0.127.in-addr.arpa PTR +norec"), and other names via
recursive resolution. If you cannot, there is something wrong, and you
should read the Troubleshooting section below to diagnose and correct
your problem before proceeding further. Remember to read the logs!
A real domain
Let us expand this minimal configuration into one which performs useful
name service for a real domain. Suppose that we own (i.e., our ISP has
assigned to us) the IP addresses in the 192.0.2.0/29 range (which has 6
usable addresses: 192.0.2.1-6), and that we want to serve authoritative
data for the domain example.com. A friend has agreed to configure her
name server (192.0.2.96), to be a slave for the domain, as well as a
backup mail server. In return, she wants the foo.example.com subdomain
delegated to her own name servers.
Forward zone
First, we must introduce the zone to named.conf:
zone "example.com" {
type master;
file "example.com";
};
And create the zone file:
$TTL 2D
@ SOA ns1.example.com. hostmaster.example.com. (
2001090101 ; Serial
24h ; Refresh
2h ; Retry
3600000 ; Expire (1000h)
1h) ; Minimum TTL
NS ns1.example.com.
NS ns2.example.com.
MX 5 mx1.example.com.
MX 10 mx2.example.com.
A 192.0.2.1
; Addresses
ns1 A 192.0.2.1 ; Name servers
ns2 A 192.0.2.96
mx1 A 192.0.2.2 ; Mail servers
mx2 A 192.0.2.96
www A 192.0.2.3 ; Web servers
dev A 192.0.2.4
work A 192.0.2.5 ; Workstations
play A 192.0.2.6
; Delegations
foo NS dns1.foo.example.com.
foo NS dns2.foo.example.com.
dns1.foo A 192.0.2.96
dns2.foo A 192.0.2.1
The SOA record is similar to the one we saw before. (Note that the next
five records all use the implicit name "@", short for "example.com".)
The two NS records define ns1.example.com (our own server, 192.0.2.1)
and ns2.example.com (our friend's server, 192.0.2.96) as authoritative
name servers for example.com.
The MX (Mail Exchanger) records each specify a mail server for the zone.
An MX RR takes two arguments: a priority number, and the name of a host.
In delivering mail addressed to example.com, the listed MXes are tried
in increasing order of priority. In this case, mx1.example.com (our own
machine, 192.0.2.2) has the lowest priority, and is always tried first.
If the attempt to deliver mail to mx1 fails (for whatever reason), the
next listed MX, mx2.example.com (our friend's server) is tried.
The A record says that the address of example.com is 192.0.2.1, and the
next few lines specify addresses for other hosts in the zone: our name
servers ns1 and ns2, mail servers mx1 and mx2, two web servers, and two
workstations.
Next, we add NS records to delegate authority over the foo.example.com
domain to dns1 and dns2.foo.example.com. The A records for dns1 and dns2
are known as glue records, and they enable resolvers to find the address
of the authoritative name servers, so that they can continue the query.
(If we were using dig, the NS records for dns1 and dns2 would be listed
in the AUTHORITY section of the response, while the ADDITIONAL section
would contain their addresses.)
Notice that dns2.foo.example.com is 192.0.2.1, our own name server. We
are acting as a slave for the foo.example.com zone, and must configure
named accordingly. We introduce the zone as a slave in named.conf, and
specify the address of the master name server:
zone "foo.example.com" {
type slave;
file "foo.example.com";
masters {
192.0.2.96;
};
};
Similarly, our friend must configure 192.0.2.96, which is a master for
foo.example.com, and a slave for example.com. (She must also configure
her server to accept mail addressed to example.com. Usually, mx2 would
just queue the mail until it could be delivered to mx1.)
Reverse zone
Let us pretend that we live in a perfect world, and that our highly
competent ISP has successfully delegated authority of our reverse zone
to us, and we must set up named to handle reverse resolution as well.
There is nothing particularly different between this and the reverse
zone we set up for 0.0.127.in-addr.arpa. There is, however, one new
issue we must address. What is our zone named?
DNS can only delegate authority at the "." in domain names. This means
that one can set up reverse zones for the whole of a class A, B, or C
network, since they are divided at octet boundaries in the IP address.
This is clearly unsuitable for classless subnets like ours, since the
divisions are not at octet boundaries, but in the middle of an octet.
In other words, our network can't be described as x.* (class A), x.y.*
(class B), or x.y.z.* (class C). The latter comes closest, but includes
several addresses (like 192.0.2.22) which do not belong to our tiny
192.0.2.0/29 network. To set up a reverse zone for our network, we must
resort to the use of classless delegation (described in RFC 2317).
The ISP, which is authoritative for the 2.0.192.in-addr.arpa zone, must
either maintain your reverse zone for you, or add the following records
into its zone file:
1 CNAME 1.1-6
2 CNAME 2.1-6
3 CNAME 3.1-6
4 CNAME 4.1-6
5 CNAME 5.1-6
6 CNAME 6.1-6
1-6 NS 192.0.2.1
1-6 NS 192.0.2.96
The first CNAME record says that 1.2.0.192.in-addr.arpa is an alias for
1.1-6.2.0.192.in-addr.arpa. (The others are similar. We don't have CNAME
records for network and broadcast addresses 0 and 7, because they don't
need to resolve.) Resolvers already know how to follow CNAME aliases
while resolving names. When they ask about the 1-6 domain, they find the
NS records defined above, and continue with their query by asking our
name server about 1.1-6.2.0.192.in-addr.arpa.
So we must set up a zone file for 1-6.2.0.192.in-addr.arpa. Apart from
the peculiar name, this zone file is similar in every respect to the
reverse zone we set up earlier, and should contain 6 PTR records (apart
from the SOA and NS records). Note that we make 192.0.2.96 (ns2) a slave
for the reverse zone as well, so the administrator must add a suitable
zone statement to named.conf for it.
In the real world, however, you may have to wait for months for your ISP
to get the reverse delegation right, and your reverse zone will remain
broken until it does.
Registering the domain
You now have a working DNS setup, but external resolvers cannot see it,
because there is no chain of delegations from the root name servers to
yours. You need to create this chain by "registering" the domain; that
is, by paying the appropriate registration fees to an authority known as
a registrar, who then delegates authority over the chosen zone to your
name servers.
There is nothing magical about what a registrar does. It has authority
over a certain portion of the DNS database (say, the "com." TLD), and,
for a fee, it delegates authority over a subdomain ("example.com") to
you. This delegation is accomplished by the same mechanisms that were
explained above in our delegation of foo.example.com.
http://www.iana.org/domain-names.htm
contains a list of all the TLDs and the corresponding registrars (of
which there are now several). The procedure and fees for registering a
domain vary wildly between them. Visit the web site of the registrar in
question and follow the procedures outlined there. After wading through
the required amounts of red tape, your domain should be visible to the
rest of the world.
Congratulations. Your job as a DNS administrator has just begun.
Troubleshooting
There is a lot of good material about finding and fixing DNS errors. The
DNSRD Tricks and Tips
and the
RFC 1912, entitled
"Common DNS Operational and Configuration Errors," which discusses a
number of these problems at length.
Delegation problems
Your zone must be delegated to the name servers authoritative for them,
either by the root name servers, or the parents of the zone in question.
The lack of proper delegation can lead to problems ranging from the name
service for your domain being entirely dysfunctional, to some networks
being unable to use it. These are usually a problem only in the initial
stages of setting up a domain, when the delegations have not propagated
widely yet. As discussed at length earlier, you can use dig to follow
delegation chains and find the point at which problems occur. Tools like
dnswalk may also be useful.
The opposite situation is also problematic. When a name server is listed
as being authoritative for a zone, but is in fact not authoritative (it
has not been configured to be a master for the zone), it is called a
lame server, and the situation is a lame delegation. Unfortunately, lame
delegations are very common on the Internet, either temporarily because
of moving domains around, or (especially in the case of reverse zones),
more permanent configuration errors which are never detected because of
a lack of attention to detail.
If your registrar's bills for your domain aren't promptly paid, they may
discontinue the delegation of authority for your zone. If this happens
(and the whois record for your domain will usually mention this), the
best thing to do is quickly pay the registrar and ask them to renew the
delegation. It's better to not let it happen, though, since such changes
can take a relatively long time to make and propagate.
Reverse lookup problems
Reverse lookup problems are often very hard to diagnose, because they
manifest themselves as failures in systems other than DNS. Many security
sensitive services perform reverse lookups on the originating host for
all incoming connections, and deny the connection if the query fails.
Even if reverse resolution succeeds, many servers may reject connections
from your host if your A and PTR records do not match (that is, the PTR
record for a particular IP address refers to a name, and the A record
for that name refers to a different IP address). They perform a double
lookup to verify that the PTR and A records match to eliminate spoofing
attacks. Maintain your reverse zones carefully at all times.
Delegation problems are a frequent source of woe. Unfortunately, many
ISPs appear unable to understand, configure or delegate reverse zones.
In such cases, you often have little choice but to try and tell them
what to do to fix the problem, and if they refuse to listen, find a new
ISP (or live with broken DNS).
Another typical symptom of failing reverse lookups is an abnormally long
delay on connection attempts. This happens when the server's query for a
PTR record is not answered and times out (often due to network problems,
or the name server being down). This can be baffling to diagnose, but
you should suspect DNS problems whenever you hear questions like "Hey!
Why is telnet taking so long to connect?"
Serial numbers
Serial numbers are very important to the correct operation of slave
servers. An increase in the serial number of a zone causes slaves to
reload the zone and update their local cache. A very common mistake is
forgetting to increment the serial number after a change to the zone
data. Secondary name servers will not reload the zone, and will continue
to serve old data. If you suspect that the data on the master and slave
servers are out of sync, you can use dig to view the SOA record for the
zone on each server ("dig @master domain SOA" and "dig @slave domain
SOA") and compare the serial numbers in the responses.
Another common problem is setting the serial number to an incorrect
value, either too small or too large. A too-small serial number causes
slaves to think that they possess a more up-to-date copy of the zone
data, but this is easily corrected by increasing the serial number as
necessary. A too-large serial number is more problematic, and needs more
elaborate measures to fix.
Serial number comparisons are defined in such a way that if a serial
number, when subtracted from another with no overflow correction,
results in a positive number, the second number is "newer" than the
first, and a zone transfer is required. (See RFC 1982 "Serial Number
Arithmetic" for details.) You can exploit this property by temporarily
setting the serial number to 2^32 (4,294,967,296), waiting for all the
slaves to reload the zone, and then setting it to the correct number.
Zone files
The commonest error in zone data is to forget that names in a zone file
are relative, not to the root, but to the origin of the zone. Writing
"www.example.com" in the zone file for example.com, and expecting it to
be fully qualified, causes names like "www.example.com.example.com" to
show up in the DNS. You should either write "www", which is qualified to
the correct "www.example.com", or write "www.example.com." with the
trailing period, to indicate that the name is fully qualified.
The SOA record should be treated with care. It should contain (as the
first field) the domain name of the master server (not a CNAME), and a
contact address (with the @ replaced by a ".") to report problems to.
Mail sent to this address should be read frequently. The other fields
should contain sensible values for your zone, and the serial number
should be correctly incremented after each change.
As discussed earlier, A and PTR records should always match, that is,
the A record pointed to by a PTR should point back to the address of the
PTR record. Remember to quote the two arguments of HINFO records if they
contain any whitespace. Avoid the use of CNAME records for MX, NS, and
SOA records.
In general, after making changes to zone data, it is a good idea to
reload named and examine the logs for any errors which cause named to
complain or reject the zone. Even better, you could use one of the
verification tools like dnswalk, discussed briefly below.
Tools
http://www.dns.net/dnsrd/tools.html
maintains an extensive list of programs which can help you debug and
prevent DNS problems. BIND itself includes the always-useful dig
program, as well as named-checkconf (to check /etc/named.conf for syntax
errors) and named-checkzone (to do the same for zone files).
I also specially recommend dnswalk and nslint. dnswalk is a Perl script
which scans the DNS setup of a given domain for problems. It should be
used in conjunction with RFC 1912, which explains most of the problems
it detects. nslint, like the analogous lint utility for C programs,
searches for common BIND and zone file configuration errors.
The occasional use of these programs, especially after non-trivial zone
changes, helps to keep your DNS configuration healthy and trouble-free.
Security
Security considerations are of vital importance to DNS administrators,
because DNS is itself not a very secure protocol, and because a number
of successful attacks against BIND have been found over the years. The
most important defence is to keep abreast of developments in security
circles and act on them promptly. The BugTraq mailing list, hosted by
Securityfocus, and
the SANS Institute are good places to
start.
DNS is especially vulnerable to attacks known as poisoning and spoofing.
Poisoning refers to the placing of incorrect data into the DNS database,
which then spreads to clients and caches across the world, potentially
causing hundreds of thousands of people to unwittingly use the bad data.
Although DNS poisoning can occur because of carelessness, it has serious
implications when performed deliberately. What if someone set up a clone
of a common web site, redirected users to it by DNS poisoning, and then
asked them for their credit card numbers? Spoofing is the practice of
forging network packets, and making name servers believe that they are
receiving a valid answer to a query is one of the ways malicious
poisoning can be performed.
BIND has often been criticised as being very insecure, and though the
recent versions are greatly improved in this regard, DNS administrators
today must take several precautions to ensure that its use is adequately
protected from attacks. Of course, it is important to always run the
latest recommended version of BIND.
UNIX security considerations
The very first thing to do is to configure the environment BIND runs in
to use all the security mechanisms available to it through the operating
system to its advantage. named should run with as few privileges as it
needs to function. Even if an attacker manages to exploit a security
hole in BIND, the effects of the break-in can be minimised if named is
running as nobody rather than root. Of course, named needs to be started
as root (because it needs to bind to port 53), but it can be instructed
to switch to a given user and group with the -u and -g command line
options.
Starting named with a command like "named -u nobody -g nogroup" is
highly recommended. Remember, however, that if you run multiple services
as nobody, the risks of a compromise are no longer negligible. In such a
situation, it is best to create separate accounts for each service, and
use them for nothing else.
You can also use the chroot feature of UNIX to isolate named into its
own part of the filesystem. If correctly configured, such a jail will
restrict attackers -- if they manage to break in -- to a part of the
filesystem which contains little of value. It is important to remember
than a chroot jail is not a panacea, and does not eliminate the need for
other defensive measures. (Programs which use chroot but do not take
other precautions as well have been shown to be insecure. BIND does,
however, take such precautions.)
In order for a chroot environment to work properly, you need to set up a
directory which contains everything which BIND needs to run. It is
recommended that you start with a working configuration of BIND, and
then create a directory, say /usr/local/bind, and copy over the files it
needs into subdirectories under that one. For instance, you will need to
copy the binaries, some system libraries, the configuration files, and
so on. Consult the BIND documentation for details about exactly which
files you need.
Once your chroot environment is set up, you can start named with the "-t
/usr/local/bind" option (combined with the -u and -g options) to
instruct it to chroot to the directory you have set up.
You may also want to keep track of resource usage. named manages a cache
of DNS data which can potentially grow very large; it will also happily
hog CPU and bandwidth, making your server unusable. This is something
which can be exploited by clever attackers, but you can configure BIND
to set resource limits. Several such options in the named.conf file are
available, including "datasize", which limits the maximum size of the
data segment (and thus the cache). One downside of this approach is that
named might be killed by the kernel if it exceeds these limits, which
means you have to run it in a loop which restarts it if it dies (or run
it from /etc/inittab).
DNS security considerations
There are several configuration options for named which can make it more
resistant to various potential attacks. The most common ones are briefly
described below. For more detailed discussions of the syntax and use of
these options, refer to the BIND 9 documentation.
ACLs (Access Control Lists)
Specifying network and IP addresses multiple times in a configuration
file is tedious and error-prone. BIND allows you to define ACLs (Access
Control Lists), which are named collections of network and IP addresses,
to ease the task of assigning permissions.
Four predefined ACLs exist: "any", which matches anything; "none", which
matches nothing; "localhost", which matches all the network interfaces
local to your name server, and "localnets", which matches any network
directly attached to a local interface. In addition, you can define your
own lists in named.conf, containing as many network and IP addresses as
you wish, using the "acl" command as shown:
acl trusted {
192.0.2.0/29; // Our own network is OK.
localhost; // And so is localhost.
!192.0.2.33/29; // But not this range.
};
As shown, you can use a "!" to negate members in an ACL. Once defined,
you can use these ACLs in allow-query, allow-transfer, allow-recursion
and similar options, as discussed below.
Queries
As mentioned before, most name servers will perform recursive resolution
for any queries it receives unless it is specifically configured not to
do so (which behaviour we suppressed by using the "dig +norec"). It so
happens that explicitly denying recursion is a good idea because, by
repeatedly fetching data from a number of unknown and untrusted name
servers, it makes your installation vulnerable to DNS poisoning.
Recursive queries can be disabled by adding a "recursion no" statement
to the "options" section of named.conf. It may still be desirable to
allow recursive queries from some trusted hosts, however, and this can
be accomplished by the use of an "allow-recursion" statement. This
excerpt would would configure named to disallow recursion for all but
the listed hosts:
options {
...
recursion no;
allow-recursion {
192.0.2.0/29;
localnets; // Trust our local networks.
localhost; // And ourselves.
};
};
You can choose to be still more restrictive and allow only selected
hosts to query your name server at all, by using the allow-query
statement (with syntax similar to allow-recursion, as described above).
Of course, this is not desirable if your server is authoritative for a
zone. In that case, you will have to explicitly "allow-query { all; }"
in the configuration section of each zone for which you wish to serve
authoritative data.
Allow only known slave servers to perform zone transfers from your
server. Not only do zone transfers consume a lot of resources (they
require a named-xfer process to be forked each time) and provide an
avenue for denial of service attacks, there have been remote exploits
via buffer overflows in named-xfer which allow attackers to gain root
privileges on the compromised system. To prevent this, add sections like
the following to all your zone definitions:
zone "example.com" {
...
allow-transfer {
192.0.2.96; // Known slave.
localhost; // Often required for testing.
};
};
Despite all this, it may be necessary to single out a few troublesome
hosts for special treatment. The "server" and "blackhole" statements in
named.conf can be used to tell named about known sources of poisoned
information or attack attempts. For instance, if the host 203.122.154.1
is repeatedly trying to attack the server, the following addition to the
"options" section of named.conf will cause our server to ignore traffic
from that address. Of course, one can specify multiple addresses and
networks in the blackhole list.
options {
...
blackhole {
203.122.154.1;
};
};
For a known source of bad data, one can do something like the following
to cause your name server to simply stop asking the listed server any
questions. This is different from adding a host to the blackhole list. A
server marked as bogus will never be sent queries, but it can still ask
us questions. A blackholed host is simply ignored altogether.
server bogus.info.example.com {
bogus yes;
};
The AUS-CERT advisory
AL-1999.004,
which discusses denial of service attacks against DNS servers, discusses
various ways of restricting access to name servers, and is a highly
recommended read. Among other things, it recommends the most restrictive
configuration possible, and the permanent blackholing of some addresses
known to be popular sources of spoofed requests and answers. It is a
good idea to add the ACL below to the blackhole list of all your
servers.
/* These are known fake source addresses. */
acl "bogon" {
0.0.0.0/8; # Null address
1.0.0.0/8; # IANA reserved, popular fakes
2.0.0.0/8;
192.0.2.0/24; # Test address
224.0.0.0/3; # Multicast addresses
/* RFC 1918 addresses may be fake too. Don't list these if you
use them internally. */
10.0.0.0/8;
172.16.0.0/12;
192.168.0.0/16;
};
DNSSEC
DNSSEC, a set of security extensions to the DNS protocol, provides data
integrity and authentication by using cryptographic digital signatures.
It provides for the storage of public keys in the DNS and their use for
verifying transactions. DNSSEC is still not widely deployed, but BIND 9
does support it for inter-server transactions (zone transfers, NOTIFY,
recursive queries, dynamic updates). It is worth configuring the TSIG
(Transaction Signature) mechanism if your slaves also run BIND 9. I
briefly discuss using TSIG for authenticated zone transfers here.
The first thing to do is to use dnssec-keygen, as we did with rndc, to
generate a shared secret key. This key is stored on both the master and
slave servers. As before, we extract the "Key:" data from the ".private"
file. The following command creates a 512-bit host key named transfer:
$ dnssec-keygen -a hmac-md5 -b 512 -n host transfer
Next, we set up matching key statements in the named.conf for both the
master and slave servers (exactly as we did for rndc and named earlier).
Remember not to transfer the secret key from one machine to the other
over an insecure channel. Use ssh, secure FTP, or something similar.
Remember also that the shared secrets should not be stored in world
readable files. The statements, identical on both machines, would look
something like this:
key transfer {
algorithm "hmac-md5";
secret "..."; # Key from .private file
};
Finally, we set up a server statement on the master, to instruct it to
use the key we just created when communicating with the slave, and
enable authenticated zone transfers with the appropriate allow-transfer
directives:
server 192.0.2.96 {
key { transfer; };
};
The BIND 9 ARM contains more information on TSIG configuration and
DNSSEC support in BIND.
Split DNS
BIND is often run on firewalls, both to act as a proxy for resolvers
inside the network, and to serve authoritative data for some zones. In
such situations, many people prefer to avoid exposing more details of
their private network configuration via DNS than is unavoidable (though
there is some debate about whether or not this is actually useful). The
outside world should only see information they are explicitly allowed
access to, while internal hosts are allowed access to other data. This
kind of setup is called "split DNS."
The best way to do this used to be to run two separately configured
instances of named on internal and external interfaces, and forward
selected traffic from the former to the latter. BIND 9 provides an
elegant and convenient solution to the problem with its support for
views. Suppose you have a set of zones which you wish to expose to the
outside world, and another set which you wish to allow hosts on your
network to see. You can accomplish this with a configuration like the
following:
acl private {
localhost;
192.168.0.0/24; # Define your internal network suitably.
};
view private_zones {
match { private; };
recursion yes; # Recursive resolution for internal hosts.
zone internal.zone {
# Zone statements;
};
# More internal zones.
};
view public_zones {
match { any; }
recursion no;
zone external.zone {
# Zone statements;
};
# More external zones.
};
On a somewhat related note, you may wish to configure internal hosts
running named to forward all queries to the firewall, and never try to
resolve queries themselves. The "forward only" and "forwarders" options
in named.conf do this (forwarders specifies a list of IP addresses of
the name servers to forward queries to).
The BIND 9 ARM discusses several details of running BIND in a secure
split-DNS configuration.
References
I recommend the book "The Concise Guide to DNS and BIND", by Nicolai
Langfeldt, for in-depth discussion of both theoretical and operational
aspects of DNS administration.