I l@ve RuBoard Previous Section Next Section

Hack 77 Views in BIND 9

figs/moderate.giffigs/hack77.gif

Change the results that your DNS server returns depending on where the requests are coming from

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.

77.1 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 such as "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.

The match-clients substatement takes an address match list as an argument. Only quarriers 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 it doesn't include any substatements besides match-clients, which would change the configuration of this view from the default configuration.

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

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.

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

77.3 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 { 192.168.0.254; };
};
};

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.

    I l@ve RuBoard Previous Section Next Section