Hack 77 Views in BIND 9
 
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.
|