DekGenius.com
I l@ve RuBoard Previous Section Next Section

11.2 Securing Your Name Server

BIND 4.9 introduced several important security features that help you protect your name server. BIND 8 and 9 continued the tradition by adding several more. These features are particularly important if your name server is running on the Internet, but they're also 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.

11.2.1 BIND Version

One of the most important ways you can enhance the security of your name server is to run a recent version of BIND. All versions of BIND before 8.2.3 are susceptible to at least a few known attacks. Check the ISC's list of vulnerabilities in various BIND versions at http://www.isc.org/products/BIND/bind-security.html for updates.

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:

% 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"

To address this, BIND Versions 8.2 and later let you tailor your name server's response to the version.bind query:

options {
	version "None of your business";
};

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.

11.2.2 Restricting Queries

Before BIND 4.9, administrators had no way to control who could look up names 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 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.

11.2.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 three main Movie U. networks, we'd use:

options {
      allow-query { 192.249.249/24; 192.253.253/24; 192.253.254/24; };
};
11.2.2.2 Restricting queries in a particular zone

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

acl "HP-NET" { 15/8; };

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

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.

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:

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 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.

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:

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 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.

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_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.

11.2.3 Preventing Unauthorized Zone Transfers

Arguably 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 records (e.g., addresses) for domain names they already know, one at a time. Users who can start zone transfers from your server can list all of the records 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 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:

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; };
};

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 slave;
      masters { 192.249.249.3; };
      file "bak.movie.edu";
      allow-transfer { none; };
};

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:

options {
      allow-transfer { 192.249.249/24; 192.253.253/24; 192.253.254/24; };
};

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:

xfrnets 15.0.0.0 128.32.0.0

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.

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:

xfrnets 15.255.152.4&255.255.255.255 128.32.1.0&255.255.255.0

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:

key terminator-wormhole. {
	algorithm hmac-md5;
	secret "UNd5xYLjz0FPkoqWRymtgI+paxW927LU/gTrDyulJRI=";
};

zone "movie.edu" {
	type master;
	file "db.movie.edu";
	allow-transfer { key terminator-wormhole.; };
};

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=";
};

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";
};

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.

11.2.4 Running BIND with Least Privilege

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 name server through which he can read or write files, he'll have unfettered 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 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:

-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 you specify -u without -g, the name server uses the user's primary group. BIND 9 name servers always change to the user's primary group, so they don't support -g.

-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.

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 :[3]

[3] This procedure is based on Red Hat Linux 6.2, so if you use a different operating system, your mileage may vary.

  1. Create the /var/named directory, if it doesn't exist. Create dev, etc, lib, usr, and var subdirectories. Within usr, create an sbin subdirectory. Within var, create subdirectories named named and run:

    # mkdir /var/named
    # cd /var/named
    # mkdir -p dev etc lib usr/sbin var/named var/run
  2. Copy named.conf to /var/named/etc/named.conf:

    # cp /etc/named.conf etc
  3. If you're running BIND 8, copy the named-xfer binary to the usr/sbin/ or etc subdirectory (depending on whether you found it in /usr/sbin or /etc).

    # cp /usr/sbin/named-xfer usr/sbin

    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.)

  4. Create dev/null in the chroot ed environment:

    # mknod dev/null c 1 3
  5. If you're running BIND 8, copy the standard, shared C library and the loader to the lib subdirectory:

    # 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

    The pathnames may vary on your operating system. BIND 9 name servers are self-contained.

  6. Edit your startup files to start syslogd with an additional option and option argument: -a /var/named/dev/log. On many modern versions of Unix, syslogd is started from /etc/rc.d/init.d/syslog. When syslogd restarts next, it will create /var/named/dev/log, and named will log to it.

    If your syslogd doesn't support the -a option, use the logging statement described in Chapter 7, to log to files in the chroot ed directory.

  7. If you're running BIND 8 and use the -u or -g options, create passwd and group files in the etc subdirectory to map the arguments of -u and -g to their numeric values (or just use numeric values as arguments):

    # echo "named:x:42:42:named:/:" >
    etc/passwd # echo "named::42" > etc/group

    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( ).

  8. Finally, edit your startup files to start named with the -t option and option argument: -t /var/named. Similarly to syslogd, many modern versions of Unix start named from /etc/rc.d/init.d/named.

If you're hooked on using ndc to control your BIND 8 name server, you can continue to do so as long as you specify the pathname to the Unix domain socket as the argument to ndc 's -c option:

# ndc -c /var/named/var/run/ndc reload

rndc will continue to work as before with your BIND 9 name server since it just talks to the server via port 953.

11.2.5 Split-Function Name Servers

Name servers really have two major roles: answering iterative queries from remote name servers and answering recursive queries from local resolvers. If we separate these roles, dedicating one set of name servers to answering iterative queries and another to answering recursive queries, we can more effectively secure those name servers.

11.2.5.1 "Delegated" name server configuration

Some of your name servers answer nonrecursive queries from other name servers on the Internet because these 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 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 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:

options {
     recursion no;
};

or, on a BIND 4.9 server, use:

options no-recursion

You should also restrict zone transfers of your zones to known slave servers, as described in Section 11.2.3 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 {
      fetch-glue no;
};

or, on a BIND 4.9 server, use:

options no-fetch-glue
11.2.5.2 "Resolving" name server configuration

We'll call a name server that serves one or more resolvers or that 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 receive queries only from our own resolvers, we can configure it to deny queries from any but our resolvers' IP addresses.

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:

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

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.

There's one other option we can use to make our resolving name server a little more secure— use-id-pool:

options {
	use-id-pool yes;
};

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.

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.

11.2.6 Two Name Servers in One

What if you have only one name server to advertise your zones and serve your resolvers, and you can't afford the additional expense of buying another computer to run a second name server on? There are still a few options open to you. Two are single-server solutions that take advantage of the flexibility of BIND 8 and 9. One of these configurations allows anyone to query the name server for information in zones it's authoritative for, but only our internal resolvers can query the name server for other information. While this doesn't prevent remote resolvers from sending our name server recursive queries, those queries have to be in its authoritative zones so they won't induce our name server to send additional queries.

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

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"; };
};

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.

If we were running BIND 8.2.1 or newer, we could simplify this configuration somewhat using the allow-recursion substatement:

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"; };
};

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.

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:

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";
};

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

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";
};

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.

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

nameserver 127.0.0.1

as the first nameserver directive.

If you're running BIND 9, you can even consolidate the two name server configurations into one using views:

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";
	};
};

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.

    I l@ve RuBoard Previous Section Next Section