DNS & BIND

DNS & BINDSearch this book
Previous: 10.10 Avoiding a Bogus Name ServerChapter 10
Advanced Features and Security
Next: 10.12 Load Sharing Between Mirrored Servers
 

10.11 Securing Your Name Server

BIND 4.9 introduced several important security features that help you protect your name server. BIND 8 continued the tradition by introducing several more. These features are particularly important if your name server is running on the Internet, but they may also be useful on purely internal name servers.

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.

10.11.1 BIND Version

One of the most important ways you can enhance the security of your name server is to run the latest version of BIND. All versions of BIND before 4.9.7 are susceptible to at least a few known attacks.[4] BIND 8.1.2 is an even better bet, because of the many new security mechanisms it introduced.

[4] In fact, part of the reason there is a BIND 4.9.7, and the BIND 4 release stream didn't stop with 4.9.6, was 4.9.6's vulnerability to a particular attack.

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 newsgroup comp.protocols.dns.bind regularly.

10.11.2 Restricting Queries

Up until BIND 4.9, domain administrators had no way to control who could look up data on their name servers. That makes a certain amount of sense; the original idea behind DNS was to make information easily available all over the Internet.

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 name space from most of the world but to make it available to a limited audience.

The BIND 8 allow-query substatement allows you to place an IP address-based access list on queries. The access list can apply to a particular zone, or to any queries received by the server. In particular, the access list specifies which IP addresses are allowed to send queries to the server.

10.11.2.1 Restricting all queries

The global form of the allow-query substatement looks like this:

options {
                allow-query { address_match_list; };
};

So to restrict our name server to answering queries from the two main Movie U. networks, we'd use:

options {
                allow-query { 192.249.249/24; 192.253.253/24; };
};

10.11.2.2 Restricting queries in a particular zone

BIND 8 also allows you to apply an access list to a particular zone. In this case, just use allow-query as a substatement of the zone statement for the zone you want to protect:

zone "hp.com" {
                type slave;
                file "db.hp";
                masters { 15.255.152.2; };
                allow-query { "HP-NET"; };
};

Any kind of authoritative name server, master or slave, can apply an access list to the zone. Zone-specific access lists take precedence over global access lists for that zone. The zone-specific access list may even be more permissive than the global access list. If there's no zone-specific access list defined, any global access list will apply.

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, restricting zone transfers is done separately.) However, 4.9 servers have no mechanism for restricting who can send your server queries for data in zones your server isn't authoritative for; the secure zones mechanism only works with authoritative zones.

To use secure zones, you include one or more special TXT (text) records in your zone data on the primary master name server. The records are conveniently 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, either:

address:mask

or:

address:H

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 address. If you want to allow all of net 15 access to your zone data, use 15.0.0.0:255.0.0.0. If you only want to allow 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.

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 wanted to restrict queries for information in movie.edu to hosts on Movie U.'s networks, we could add the following lines to db.movie on the movie.edu primary:

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"

Notice that we included the address 127.0.0.1 in our access list. That's so a resolver can query its local name server. If you forget the :H, you'll see the following syslog message:

Aug 17 20:58:22 terminator named[2509]: build_secure_netlist
       (movie.edu): addr (127.0.0.1) is not in mask (0xff000000)

Also, note that the secure zones 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 zones records to that zone on its primary master name server, too.

10.11.3 Preventing Unauthorized Zone Transfers

Even more important than controlling who can query your name server is ensuring that only your real slave name servers can transfer zones from your name server. Users on remote hosts that can query your name server's zone data can only look up data (e.g., addresses) for hosts whose domain names they already know, one at a time. Users who can start zone transfers from your server can list all of the hosts in your zones. It's the difference between letting random folks call your company's switchboard and ask for John Q. Cubicle's phone number and sending them a copy of your corporate phone directory.

BIND 8's allow-transfer substatement and 4.9's xfrnets directive let administrators apply an access list to zone transfers. allow-transfer can restrict transfers of a particular zone as a zone substatement or can restrict all zone transfers as an options substatement. It takes an address match list as an argument.

Say the slave servers for your acmebw.com zone have the IP addresses 192.168.0.1 and 192.168.1.1. The zone statement:

zone "acmebw.com" {
                type master;
                file "db.acmebw";
                allow-transfer { 192.168.0.1; 192.168.1.1; };
};

will allow only those slaves to transfer acmebw.com from the primary master name server. Note that since BIND 8's default is to allow any IP address to transfer zones, 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 "acmebw.com" {
                type slave;
                masters { 192.168.0.4; };
                allow-transfer { none; };
};

BIND 8 will also let you establish a global access list on zone transfers. This applies to any zones that don't have their own, explicit access lists defined as zone substatements. For example, you might want to limit all zone transfers to your internal IP addresses:

options {
                allow-transfer { 192.168/16; };
};

The BIND 4.9 xfrnets directive 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:

xfrnets 15.0.0.0 128.32.0.0

allows only hosts on the Class A network 15 or the Class B network 128.32 to transfer zones from this name server. Unlike secure zones, this restriction applies to any zones the server is authoritative for.

If you want to specify just a part of the network, down to a single IP address, you can add a network mask. The syntax for including a network mask is network&netmask. Note that there are no spaces either between the network and the ampersand or between the ampersand and the netmask: they're not allowed.

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.0, you could use the xfrnets directive:

xfrnets 15.255.152.4&255.255.255.255 128.32.1.0&255.255.255.0

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 use xfrnets on name servers inside your firewall, unless you're worried about your own employees listing your zone data.

10.11.4 Running BIND as a Non-Root User

Running a network server such as BIND as the root user can be dangerous - and BIND normally runs as root. If a hacker finds a vulnerability in the server through which he can read or write files, he'll have root access to the filesystem. If he can exploit a flaw that allows him to execute commands, he'll execute them as root.

BIND 8.1.2 includes experimental 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 is able to break into your host through the name server, at least they won't have root privileges.

BIND 8.1.2 also includes 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:

-u

Specifies the username or user ID the name server changes to after starting, e.g., named -u bin.

-g

Specifies the group or group ID the name server changes to after starting, e.g., named -g other. If -u is specified without -g, the name server will use the user's primary group.

-t

Specifies the directory for the name server to chroot to.

If you opt to use the -u and -g options, you'll have to decide what user and group to use. Your best bet is to create a new user and group for the name server to run as, such as named. Since the name server reads named.conf before giving up root privileges, you don't have to change that file's permissions. However, you may have to change the permissions and ownership of your zone data files so that the user the name server runs as can read them. If you use dynamic update, you'll have to make the zone data files for dynamically-updated zones writable by the name server.

The name server may have trouble writing the named.pid file, too, since it occasionally rewrites it after startup (and after giving up root privileges). On most versions of UNIX, /var/run (the directory in which the name server saves named.pid) isn't writable by non-root users. The easiest way to deal with this is to use the pid-file substatement to move named.pid to another directory:

options {
                pid-file "/var/named/named.pid";
};

Then make sure that file is writable by the user named runs as.

If your name server is configured to log to files (as opposed to syslog), make sure those files exist and are writable by the name server before starting the server.

The -t option takes a little more special 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. That includes the following:

named-xfer

Most nameds expect to find the named-xfer executable in /etc/named-xfer. If you chroot the server to /var/named, that becomes /var/named/etc/named-xfer.

Shared libraries

If you build the name server with shared libraries, you'll need to put copies of those shared libraries in whatever directory your operating system expects them in. For example, if your OS looks for shared libraries in /lib, you'll need copies in /var/named/lib. The alternative is to build your name server as a statically-linked executable.

/dev/null

The name server needs a /dev/null in the chrooted directory. You can find out how to make one by reading /dev/MKDEV, or the manual page for mknod.

Finally, the name server may not be able to syslog in a chrooted environment. If that's the case, use the logging statement described in Chapter 7, Maintaining BIND, to log to files in the chrooted directory.

With any of these options, you'll have to remember to either edit ndc to specify the options upon restart or start, or to start and restart named manually.

10.11.5 "Delegated" Name Server Configuration

Some of your name servers answer nonrecursive queries from other name servers on the Internet, because your name servers appear in NS records delegating your zones to them. We'll call these name servers "delegated" name servers.

There are special measures you can take to secure your delegated name servers. But first, you should make sure that these 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, as described in the next section.

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, you'd use:

options {
                recursion no;
};

on a BIND 8 server, and:

options no-recursion

on a BIND 4.9 server.

You should also restrict zone transfers of your zones to known slave servers, as described in Section 10.11.3, "Preventing Unauthorized Zone Transfers," earlier in this chapter. Finally, you might also want to turn off glue fetching. The name server will automatically try to resolve the names of any name servers in the RDATA of NS records; to prevent this from happening, and keep your name server from sending any queries of its own, use:

options {
                fetch-glue no;
};

on a BIND 8 server, and:

options no-fetch-glue

on BIND 4.9.

10.11.6 Resolving Name Server Configuration

We'll call a name server that serves one or more resolvers, or is configured as another name server's forwarder, a "resolving" name server. Unlike a delegated name server, a "resolving" name server can't refuse recursive queries. Consequently, we have to configure it a little differently to secure it. Since we know our name server should only receive queries from our own resolvers, we can configure it to deny queries from any but our resolvers' IP addresses.

Only BIND 8 will allow us to restrict which IP addresses can send our name server arbitrary queries. (BIND 4.9 servers will 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 will restrict queries to just our internal network:

options {
                allow-query { 192.168/16; };
};

With this configuration, the only resolvers that can send your server recursive queries, and induce your name servers to query other name servers, are your internal resolvers, which are presumably relatively benevolent.

10.11.7 Two Name Servers in One

What if you only have one name server to advertise your zone and serve your resolvers, and you can't afford the additional expense of buying another computer to run another name server on? There are still a couple of options open to you. One is a single-server solution that takes advantage of BIND 8's flexibility. This configuration allows anyone to query the name server for information in your delegated zones, but only your internal resolvers can query the name server for other information. While this doesn't prevent remote resolvers from sending your name server recursive queries, those queries have to be in your authoritative zones, so they won't induce your name server to send additional queries.

Here's a named.conf file to do that:

options {
                allow-query { 192.168/16; };
};

zone "acmebw.com" {
                type master;
                file "db.acmebw";
                allow-query { any };
                allow-transfer { 192.168.0.1; 192.168.1.1; };
};

zone "168.192.in-addr.arpa" {
                type master;
                file "db.192.168";
                allow-query { any; };
                allow-transfer { 192.168.0.1; 192.168.1.1; };
};

Here, the more permissive zone-specific access lists apply to queries in the server's authoritative zones, but the more restrictive global access list applies to all other queries.

Another option is to run two named processes on a single host. One is configured as the delegated server, another as the 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 only has 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 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:

options {
                directory "/usr/local/named";
                recursion no;
                listen-on { 192.168.0.4; };
                pid-file "delegated.pid";
};

zone "acmebw.com" {
                type master;
                file "db.acmebw";
                allow-query { any };
                allow-transfer { 192.168.0.1; 192.168.1.1; };
};

zone "168.192.in-addr.arpa" {
                type master;
                file "db.192.168";
                allow-query { any; };
                allow-transfer { 192.168.0.1; 192.168.1.1; };
};

zone "." {
                type hint;
                file "db.cache";
};

Next, here's the named.conf file for the resolving name server, listening on the loopback address:

options {
                directory "/usr/local/named1";
                listen-on { 127.0.0.1; };
                pid-file "resolving.pid";
};

zone "." {
                type hint;
                file "db.cache";
};

The listen-on substatement tells the server reading the conf file to listen only on network interfaces whose IP addresses match the address match list. We've specified the IP address explicitly, so that there's no chance of mistakes. You can also use listen-on to have named listen on a port other than 53; for that, use the syntax:

options {
                listen-on port <n> { address_match_list };
};

Note that we turn recursion off on the delegated name server, but 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 they don't try to use the same default filename for their PID files, debug files, and statistics files.

To use the resolving name server, listening on the loopback address, the local host's resolv.conf file must include:

nameserver 127.0.0.1

as the first nameserver directive.


Previous: 10.10 Avoiding a Bogus Name ServerDNS & BINDNext: 10.12 Load Sharing Between Mirrored Servers
10.10 Avoiding a Bogus Name ServerBook Index10.12 Load Sharing Between Mirrored Servers