We'll start by discussing measures you should take on all name servers for which security is important. Then we'll describe a model in which your name servers are split into two communities, one for serving only resolvers and one for answering other name servers' queries.
But don't stop there: new attacks are being thought up all the time, so you'll have to do your best to keep abreast of BIND's vulnerabilities and the latest "safe" version of BIND. One good way to do that is to read the comp.protocols.dns.bind newsgroup regularly.
There's another aspect of BIND's version relevant to security: if a hacker can easily find out which version of BIND you're running, he may be able to tailor his attacks to that version of BIND. And, wouldn't you know it, since about BIND 4.9, BIND name servers have replied to a certain query with their version. If you look up TXT records in the CHAOSNET class attached to the domain name version.bind, BIND graciously returns something like this:
To address this, BIND Versions 8.2 and later let you tailor your name server's response to the version.bind query:% dig txt chaos version.bind. ; <<>> DiG 9.1.0 <<>> txt chaos version.bind. ;; global options: printcmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34772 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;version.bind. CH TXT ;; ANSWER SECTION: version.bind. 0 CH TXT "9.1.0"
Of course, receiving a response like "None of your business" will tip off the alert hacker to the fact that you're likely running BIND 8.2 or better, but that still leaves a number of possibilities.options { version "None of your business"; };
The neighborhood is not such a friendly place anymore, though. In particular, people who run Internet firewalls may have a legitimate need to hide certain parts of their namespace from most of the world while making it available to a limited audience.
The BIND 8 and 9 allow-query substatement lets you apply an IP address-based access control list to queries. The access control list can apply to queries for data in a particular zone or to any queries received by the name server. In particular, the access control list specifies which IP addresses are allowed to send queries to the server.
So to restrict our name server to answering queries from the three main Movie U. networks, we'd use:options { allow-query { address_match_list; }; };
options { allow-query { 192.249.249/24; 192.253.253/24; 192.253.254/24; }; };
Any kind of authoritative name server, master or slave, can apply an access control list to the zone. Zone-specific access control lists take precedence over a global ACL for queries in that zone. The zone-specific access control list may even be more permissive than the global ACL. If there's no zone-specific access control list defined, any global ACL will apply.acl "HP-NET" { 15/8; }; zone "hp.com" { type slave; file "bak.hp.com"; masters { 15.255.152.2; }; allow-query { "HP-NET"; }; };
In BIND 4.9, this functionality is provided by the secure_zone record. Not only does it limit queries for individual resource records, it limits zone transfers, too. (In BIND 8 and 9, restricting zone transfers is done separately.) However, BIND 4.9 name servers have no mechanism for restricting who can send your server queries for data in zones your server is not authoritative for; the secure_zone mechanism works only with authoritative zones.
To use secure_zone, include one or more special TXT records in your zone data on the primary master name server. Conveniently, these records are transferred to the zone's slave servers automatically. Of course, only BIND 4.9 slaves will understand them.
The TXT records are special because they're attached to the pseudo-domain name secure_zone, and the resource record-specific data has a special format, too:
or:address:mask
In the first form, address is the dotted-octet form of the IP network to which you want to allow access to the data in this zone. The mask is the netmask for that network. If you want to allow all of 15/8 access to your zone data, use 15.0.0.0:255.0.0.0. If you want to allow only the range of IP addresses from 15.254.0.0 to 15.255.255.255 access to your zone data, use 15.254.0.0:255.254.0.0.address:H
The second form specifies the address of a particular host you'd like to allow access to your zone data. The H is equivalent to the mask 255.255.255.255; in other words, each bit in the 32-bit address is checked. Therefore, 15.255.152.4:H gives the host with the IP address 15.255.152.4 the ability to look up data in the zone.
If we want to restrict queries for information in movie.edu to hosts on Movie U.'s networks, but our name servers run BIND 4.9 instead of BIND 8 or 9, we could add the following lines to db.movie.edu on the movie.edu primary master:
Notice that we included the loopback address (127.0.0.1) in our access control list. That's so a resolver running on the same host as a name server can query the local name server.secure_zone IN TXT "192.249.249.0:255.255.255.0" secure_zone IN TXT "192.253.253.0:255.255.255.0" secure_zone IN TXT "192.253.254.0:255.255.255.0" secure_zone IN TXT "127.0.0.1:H"
If you forget the :H, you'll see the following syslog message:
Also, note that the secure_zone records here apply only to the zone they're in -- that is, movie.edu. If you wanted to prevent unauthorized queries for data in other zones on this server, you'd have to add secure_zone records to those zones on their primary master name servers, too.Aug 17 20:58:22 terminator named[2509]: build_secure_netlist (movie.edu): addr (127.0.0.1) is not in mask (0xff000000)
BIND 8 and 9's allow-transfer substatement and 4.9's xfrnets directive let administrators apply an access control list to zone transfers. allow-transfer restricts transfers of a particular zone when used as a zone substatement, and restricts all zone transfers when used as an options substatement. It takes an address match list as an argument.
The slave servers for our movie.edu zone have the IP addresses 192.249.249.1 and 192.253.253.1 (wormhole.movie.edu) and 192.249.249.9 and 192.253.253.9 (zardoz.movie.edu). The following zone statement:
allows only those slaves to transfer movie.edu from the primary master name server. Note that because the default for BIND 8 or 9 is to allow zone transfer requests from any IP address, and because hackers can just as easily transfer the zone from your slaves, you should probably also have a zone statement like this on your slaves:zone "movie.edu" { type master; file "db.movie.edu"; allow-transfer { 192.249.249.1; 192.253.253.1; 192.249.249.9; 192.253.253.9; }; };
BIND 8 and 9 also let you apply a global access control list to zone transfers. This applies to any zones that don't have their own explicit access control lists defined as zone substatements. For example, we might want to limit all zone transfers to our internal IP addresses:zone "movie.edu" { type slave; masters { 192.249.249.3; }; file "bak.movie.edu"; allow-transfer { none; }; };
The BIND 4.9 xfrnets directive also applies an access control list to all zone transfers. xfrnets takes as its arguments the networks or IP addresses you'd like to allow to transfer zones from your name server. Networks are specified by the dotted-octet form of the network number. For example:options { allow-transfer { 192.249.249/24; 192.253.253/24; 192.253.254/24; }; };
allows only hosts on the network 15/8 or the network 128.32/16 to transfer zones from this name server. Unlike the secure_zone TXT record, this restriction applies to any zones the server is authoritative for.xfrnets 15.0.0.0 128.32.0.0
If you want to specify just a part of a network, down to a single IP address, you can add a network mask. network&netmask is the syntax for including a network mask. Note that spaces aren't allowed between the network and the ampersand or between the ampersand and the netmask.
To pare down the addresses allowed to transfer zones in the previous example to just the IP address 15.255.152.4 and the subnet 128.32.1/24, use the xfrnets directive:
Finally, as we mentioned earlier in the chapter, those newfangled BIND 8.2 and later name servers let you restrict zone transfers to slave name servers that include a correct transaction signature with their request. On the master name server, you need to define the key in a key statement and then specify the key in the address match list:xfrnets 15.255.152.4&255.255.255.255 128.32.1.0&255.255.255.0
On the slave's end, you need to configure the slave to sign zone transfer requests with the same key:key terminator-wormhole. { algorithm hmac-md5; secret "UNd5xYLjz0FPkoqWRymtgI+paxW927LU/gTrDyulJRI="; }; zone "movie.edu" { type master; file "db.movie.edu"; allow-transfer { key terminator-wormhole.; }; };
For a primary master name server accessible from the Internet, you probably want to limit zone transfers to just your slave name servers. You probably don't need to worry about name servers inside your firewall, unless you're worried about your own employees listing your zone data.key terminator-wormhole. { algorithm hmac-md5; secret "UNd5xYLjz0FPkoqWRymtgI+paxW927LU/gTrDyulJRI="; }; server 192.249.249.3 { keys { terminator-wormhole.; }; // sign all requests to 192.249.249.3 // with this key }; zone "movie.edu" { type slave; masters { 192.249.249.3; }; file "bak.movie.edu"; };
BIND 8.1.2 and later include code that allows you to change the user and group the name server runs as. This allows you to run the name server with what's known as least privilege : the minimal set of rights it needs to do its job. That way, if someone breaks into your host through the name server, at least that person won't have root privileges.
BIND 8.1.2 and later also include an option that allows you to chroot( ) the name server: to change its view of the filesystem so that its root directory is actually a particular directory on your host's filesystem. This effectively traps your name server in this directory, along with any attackers who successfully compromise your name server's security.
The command-line options that implement these features are:
If your name server is configured to log to files (instead of to syslog), make sure that those files exist and are writable by the name server before starting the server.
The -t option takes a little more specialized configuration. In particular, you need to make sure that all the files named uses are present in the directory you're restricting the server to. Here's a procedure to follow to set up your chroot ed environment, which we'll assume lives under /var/named :[83]
[83]This procedure is based on Red Hat Linux 6.2, so if you use a different operating system, your mileage may vary.
# mkdir /var/named # cd /var/named # mkdir -p dev etc lib usr/sbin var/named var/run
# cp /etc/named.conf etc
Alternately, you can put it wherever you like under /var/named and use the named-xfer substatement to tell named where to find it. Just remember to strip /var/named off of the pathname, since when named reads named.conf, /var/named will look like the root of the filesystem. (If you're running BIND 9, skip this step because BIND 9 doesn't use named-xfer.)# cp /usr/sbin/named-xfer usr/sbin
# mknod dev/null c 1 3
The pathnames may vary on your operating system. BIND 9 name servers are self-contained.# cp /lib/libc.so.6 /lib/ld-2.1.3.so lib # ln -s lib/ld-2.1.3.so lib/ld-linux.so.2
If your syslogd doesn't support the -a option, use the logging statement described in Chapter 7, "Maintaining BIND", to log to files in the chroot ed directory.
Then add the entries to the system's /etc/passwd and /etc/group files. If you're running BIND 9, you can just add the entries to the system's /etc/passwd and /etc/group files, since BIND 9 name servers read the information they need before calling chroot( ).# echo "named:x:42:42:named:/:" > etc/passwd # echo "named::42" > etc/group
rndc will continue to work as before with your BIND 9 name server since it just talks to the server via port 953.# ndc -c /var/named/var/run/ndc reload
There are special measures you can take to secure your delegated name servers. But first, you should make sure that these name servers don't receive any recursive queries (that is, you don't have any resolvers configured to use these servers and no name servers use them as forwarders). Some of the precautions we'll take -- like making the server respond nonrecursively even to recursive queries -- preclude your resolvers from using these servers. If you do have resolvers using your delegated name servers, consider establishing another class of name servers to serve just your resolvers or using the Section 11.2.6, "Two Name Servers in One" configuration, both described later in this chapter.
Once you know your name server only answers queries from other name servers, you can turn off recursion. This eliminates a major vector of attack: the most common spoofing attacks involve inducing the target name server to query name servers under the hacker's control by sending the target a recursive query for a domain name in a zone served by the hacker's servers. To turn off recursion, use the following statement on a BIND 8 or 9 name server:
or, on a BIND 4.9 server, use:options { recursion no; };
You should also restrict zone transfers of your zones to known slave servers, as described in Section 11.2.3, "Preventing Unauthorized Zone Transfers" earlier in this chapter. Finally, you might also want to turn off glue fetching. Some name servers will automatically try to resolve the domain names of any name servers in NS records; to prevent this from happening and keep your name server from sending any queries of its own, use this on a BIND 8 name server (BIND 9 name servers have glue fetching turned off by default):options no-recursion
or, on a BIND 4.9 server, use:options { fetch-glue no; };
options no-fetch-glue
Only BIND 8 and 9 allow us to restrict which IP addresses can send our name server arbitrary queries. (BIND 4.9 name servers let us restrict which IP addresses can send the server queries in authoritative zones, via the secure_zone TXT record, but we're actually more worried about recursive queries in others' zones.) This allow-query substatement restricts queries to just our internal network:
With this configuration, the only resolvers that can send our name server recursive queries and induce them to query other name servers are our own internal resolvers, which are presumably relatively benevolent.options { allow-query { 192.249.249/24; 192.253.253/24; 192.253.254/24; }; };
There's one other option we can use to make our resolving name server a little more secure -- use-id-pool:
use-id-pool was introduced in BIND 8.2. It tells our name server to take special care to use random message IDs in queries. Normally, the message IDs aren't random enough to prevent brute-force attacks that try to guess the IDs our name server has outstanding in order to spoof a response.options { use-id-pool yes; };
The ID pool code became a standard part of BIND 9, so you don't need to specify it on a BIND 9 name server.
Here's a named.conf file to do that:
Here, the more permissive zone-specific access control lists apply to queries in the name server's authoritative zones, but the more restrictive global access control list applies to all other queries.acl "internal" { 192.249.249/24; 192.253.253/24; 192.253.254/24; }; acl "slaves" { 192.249.249.1; 192.253.253.1; 192.249.249.9; 192.253.253.9; }; options { directory "/var/named"; allow-query { "internal"; }; use-id-pool yes; }; zone "movie.edu" { type master; file "db.movie.edu"; allow-query { any; }; allow-transfer { "slaves"; }; }; zone "249.249.192.in-addr.arpa" { type master; file "db.192.249.249"; allow-query { any; }; allow-transfer { "slaves"; }; };
If we were running BIND 8.2.1 or newer, we could simplify this configuration somewhat using the allow-recursion substatement:
We don't need the allow-query substatements anymore: although the name server may receive queries from outside our internal network, it'll treat those queries as nonrecursive, regardless of whether they are or not. Consequently, external queries won't induce our name server to send any queries. This configuration also doesn't suffer from a gotcha the previous setup is susceptible to: if your name server is authoritative for a parent zone, it may receive queries from remote name servers resolving domain names in a subdomain of the zone. The allow-query solution will refuse those legitimatequeries, but the allow-recursion solution won't.acl "internal" { 192.249.249/24; 192.253.253/24; 192.253.254/24; }; acl "slaves" { 192.249.249.1; 192.253.253.1; 192.249.249.9; 192.253.253.9; }; options { directory "/var/named"; allow-recursion { "internal"; }; use-id-pool yes; }; zone "movie.edu" { type master; file "db.movie.edu"; allow-transfer { "slaves"; }; }; zone "249.249.192.in-addr.arpa" { type master; file "db.192.249.249"; allow-transfer { "slaves"; }; };
Another option is to run two named processes on a single host. One is configured as a delegated name server, another as a resolving name server. Since we have no way of telling remote servers or configuring resolvers to query one of our name servers on a port other than 53, the default DNS port, we have to run these servers on different IP addresses.
Of course, if your host already has more than one network interface, that's no problem. Even if it has only one, the operating system may support IP address aliases. These allow you to attach more than one IP address to a single network interface. One named process can listen on each. Finally, if the operating system doesn't support IP aliases, you can still bind one named against the network interface's IP address and one against the loopback address. Only the local host will be able to send queries to the instance of named listening on the loopback address, but that's fine if the local host's resolver is the only one you need to serve.
First, here's the named.conf file for the delegated name server, listening on the network interface's IP address:
Next, here's the named.conf file for the resolving name server, listening on the loopback address:acl "slaves" { 192.249.249.1; 192.253.253.1; 192.249.249.9; 192;253.253.9; }; }; options { directory "/var/named-delegated"; recursion no; fetch-glue no; listen-on { 192.249.249.3; }; pid-file "/var/run/named.delegated.pid"; }; zone "movie.edu" { type master; file "db.movie.edu"; allow-transfer { "slaves"; }; }; zone "249.249.192.in-addr.arpa" { type master; file "db.192.249.249"; allow-transfer { "slaves"; }; }; zone "." { type hint; file "db.cache"; };
Note that we didn't need an access control list for the resolving name server, since it's only listening on the loopback address and can't receive queries from other hosts. (If our resolving name server were listening on an IP alias or a second network interface, we could use allow-query to prevent others from using our name server.) We turn recursion off on the delegated name server, but we must leave it on on the resolving name server. We also give each name server its own PID file and its own directory so that the servers don't try to use the same default filename for their PID files, debug files, and statistics files.options { directory "/var/named-resolving"; listen-on { 127.0.0.1; }; pid-file "/var/run/named.resolving.pid"; use-id-pool yes; }; zone "." { type hint; file "db.cache"; };
To use the resolving name server listening on the loopback address, the local host's resolv.conf file must include the following:
as the first nameserver directive.nameserver 127.0.0.1
If you're running BIND 9, you can even consolidate the two name server configurations into one using views:
It's a fairly simple configuration: two views, internal and external. The internal view, which applies only to our internal network, has recursion on. The external view, which applies to everyone else, has recursion off. The zones movie.edu and 249.249.192.in-addr.arpa are defined identically in both zones. You could do a lot more with it -- define different versions of the zones internally and externally, for example -- but we'll hold off on that until the next section.options { directory "/var/named"; }; acl "internal" { 192.249.249/24; 192.253.253/24; 192.253.254/24; }; view "internal" { match-clients { "internal"; }; recursion yes; zone "movie.edu" { type master; file "db.movie.edu"; }; zone "249.249.192.in-addr.arpa" { type master; file "db.192.249.249"; }; zone "." { type hint; file "db.cache"; }; }; view "external" { match-clients { any; }; recursion no; zone "movie.edu" { type master; file "db.movie.edu"; }; zone "249.249.192.in-addr.arpa" { type master; file "db.192.249.249"; }; zone "." { type hint; file "db.cache"; }; };