Views in BIND 9

Views in BIND 9

The view that computers on the Internet see is usually considerably stripped down from the internal version and only contains information about resources available on the Internet, including Web servers and mail servers. The internal view normally contains everything: all those hosts accessible from the Internet plus all those on the internal network.

Until recently, presenting one view of a zone to one community of hosts and another view to others has entailed running multiple sets of name servers or multiple name server processes on a single host in a tricky configuration. Nobody ever said being two-faced is easy.

BIND 9 introduced a new feature called "views" that makes delivering different versions of a zone, and even different name server configurations, easy. In this article, I'll describe how to use views to configure several two-faced name servers, each a little more complex than the last.

Basic Syntax

The key to configuring views is the new BIND 9 configuration statement, view. view takes a view name as an argument and has one required substatement, match-clients. The view name is simply a convenient mnemonic for identifying the view; I usually use names like "internal" and "external" or "Internet." If you use a name that conflicts with a reserved word in named.conf, be sure to quote it. I quote all of my view names because I can't remember which words are reserved.


he match-clients substatement takes an address match list as an argument. Only queriers whose IP addresses match this address match list will see the configuration specified in the enclosing view. If a querier's IP address matches multiple view statement's match-clients substatements, the first view statement is the one that applies.

Here's an example of a simple view statement:

view "global" {
match-clients { any; };
};
This view statement doesn't do anything useful, because it applies to all queries (in the absence of other view statements) and because it doesn't include any substatements besides match-clients, which would change the configuration of this view from the default configuration.

There's a list of substatements supported within a view statement in the file doc/misc/options in the BIND 9 distribution, which you can get from the Internet Software Consortium. The list grows with each successive release of BIND 9, and it already includes most options substatements.

If you don't specify a particular substatement within a view, the view inherits the global setting for that substatement. So, for example, if you don't turn recursion off within a view, that view would inherit the recursion setting from your options statement (specified by the recursion substatement) or, if you don't have one, would inherit the global default, which is "recursion yes."

Here's an example of turning recursion off within a view:

view "external" {
match-clients { any; };
recursion no;
};
Here's a complete named.conf file that includes the previous view statement:

/*
 * BIND 9 name server allowing recursive queries from
 * localhost, disallowing from anywhere else
*/

options {
directory "/var/named";
};

view "localhost" {
match-clients { localhost; };
        recursion yes; /* this is the default */
};

view "external" {
match-clients { any; };
        recursion no;
};
This name server allows recursive queries only if they come from the local host. (The localhost access control list [ACL] is predefined to be all of the IP addresses of the host that runs the name server, plus the loopback address.) Queries from all other addresses are treated as non-recursive.

Here's a similar configuration, this one of a name server that allows recursive queries from our internal network but not from the Internet:

/*
 * Same name server serving an internal network and the  
 * Internet
 */

options {
directory "/var/named";
};

view "internal" {
match-clients { localnets; };
        recursion yes; /* this is the default */
};

view "external" {
match-clients { any; };
        recursion no;
};
This configuration takes advantage of the built-in ACL localnets, which is predefined as all of the networks to which our name server is directly connected.


Defining Zones in Views

In order to present a version of a zone to only some queriers, you need to define the zone within a view. You simply use a zone statement as a view substatement. Otherwise, its syntax is the same as if it were a top-level statement. Note that if you define even one zone within a view, all of your zones need to be defined inside views.

Here's an example:

/*
 * A name server showing different zone data to different
 * networks
 */

options {
        directory "/var/named";
};

view "internal" {
        match-clients { localnets; };
        recursion yes; /* this is the default */

        zone "oreilly.com" {
                type master;
                file "db.oreilly.com.internal";
                allow-transfer { any; };
};
};

view "external" {
match-clients { any; };
recursion no;

zone "oreilly.com" {
type master;
file "db.oreilly.com.external";
       allow-transfer { none; };
};
};
Note that the zone oreilly.com is defined in both the internal and the external view, but the zone data file for oreilly.com is db.oreilly.com.internal in the internal view and db.oreilly.com.external in the external view. Presumably, the contents of the zone data files are different.

If you prefer to use different subdirectories for the internal and external zone data, you can do that, too. For example, the file substatement for oreilly.com in the internal view could be:

file "internal/db.oreilly.com";

and the external view could use:

file "external/db.oreilly.com";

This way, you can keep all of your internal zone data files in the /var/named/internal directory and your external zone data files in the /var/named/external directory.

Views in Slave Name Servers

One minor wrinkle in views is the configuration of slave name servers. Many people configure a primary master name server with multiple views, then want to configure a slave with the same views. Unfortunately, when the slave tries to transfer the zones (or the two versions of the same zone) from the primary master name server, it only sees one version of the zone, the one defined in the view the slave's IP address can see.

The solution to this problem is to configure an IP address alias on the slave name server, giving it two IP addresses from which to initiate zone transfers. Then configure the primary master to present one view to one of the slave's IP addresses and the other view to the slave's other IP address. Finally, force the slave to initiate zone transfers from the appropriate IP address within each view.

Here's an example. Our slave has two IP addresses, 192.168.0.2 and 192.168.0.254. Our primary master has just one, 192.168.0.1. First, here's the slave's named.conf file:

options {
directory "/var/named";
};

view "internal" {
match-clients { localnets; };
        recursion yes;

        zone "oreilly.com" {
       type slave;
       masters { 192.168.0.1; };
       transfer-source 192.168.0.2;
                file "internal/bak.oreilly.com ";
                allow-transfer { any; };
};
};

view "external" {
match-clients { any; };
recursion no;

zone "oreilly.com" {
type slave;
masters { 192.168.0.1; };
transfer-source 192.168.0.254;
file "external/bak.oreilly.com ";
       allow-transfer { none; };
};
};
Notice that the slave is configured to initiate transfers of oreilly.com within the internal view from the IP address 192.168.0.2 and within the external view from 192.168.0.254.

Now, here's the primary master's named.conf file:

options {
directory "/var/named";
};

view "internal" {
match-clients { !192.168.0.254; localnets; };
        recursion yes;

        zone "oreilly.com" {
       type master;
       file "internal/db.oreilly.com ";
                allow-transfer { any; };
};
};

view "external" {
match-clients { any; };
recursion no;

zone "oreilly.com" {
type master;
file "external/db.oreilly.com ";
       allow-transfer { none; };
};
};
Notice that the internal view's match-clients substatement explicitly places 192.168.0.254 into the external view by negating it in the address match list. Any time the slave initiates a zone transfer from that IP address, it'll get the version of oreilly.com described by the zone data file /var/named/external/db.oreilly.com.

Summary

Though serving different versions of a single zone was possible in previous versions of BIND, views makes the configuration much simpler. You no longer need multiple named.conf files and multiple named processes, each with its own working directory.

Now if someone could just make the Internet friendlier.

No comments: