<!doctype linuxdoc system>

<article>

<!-- Title information -->

<title>Chroot-BIND HOWTO
<author>Scott Wunsch, <tt>scott at wunsch.org</>
<date>v1.5, 1 December 2001
<abstract>
This document describes installing the BIND 9 nameserver to run in a chroot
jail and as a non-root user, to provide added security and minimise the
potential effects of a security compromise.  Note that this document has
been updated for BIND 9; if you still run BIND 8, you want the Chroot-BIND8
HOWTO instead.
</abstract>

<!-- Table of contents -->
<toc>

<!-- Begin the document -->

<sect>Introduction

<p>
This is the Chroot-BIND HOWTO; see <ref id="where" Name="Where?"> for the
master site, which contains the latest copy.  It is assumed that you already
know how to configure and use BIND (the Berkeley Internet Name Domain).  If
not, I would recommend that you read the DNS HOWTO first.  It is also assumed
that you have a basic familiarity with compiling and installing software on
your UNIX-like system.

<sect1>What?

<p>
This document describes some extra security precautions that you can take when
you install BIND.  It explains how to configure BIND so that it resides in a
``chroot jail,'' meaning that it cannot see or access files outside its own
little directory tree.  We shall also configure it to run as a non-root user.

The idea behind chroot is fairly simple.  When you run BIND (or any other
process) in a chroot jail, the process is simply unable to see any part of the
filesystem outside the jail.  For example, in this document, we'll set BIND up
to run chrooted to the directory <tt>/chroot/named</>.  Well, to BIND, the
contents of this directory will appear to be <tt>/</>, the root directory.
Nothing outside this directory will be accessible to it.  You've probably
encounted a chroot jail before, if you've ever used <tt>ftp</> to log into a
public system.

Because the chroot process is much simpler with BIND 9, I have started to expand
this document slightly, to include more general tips about securing a BIND
installation.  Nevertheless, this document is not (and is not intended to be) a
complete reference for securing BIND.  If you do only what is outlined in this
document, you're not finished securing your nameserver!

<sect1>Why?

<p>
The idea behind running BIND in a chroot jail is to limit the amount of access
any malicious individual could gain by exploiting vulnerabilities in BIND.  It
is for the same reason that we run BIND as a non-root user.

This should be considered as a supplement to the normal security precautions
(running the latest version, using access control, etc.), certainly not as a
replacement for
them.

If you're interested in DNS security, you might also be interested in a few
other products.  Building BIND with <url
url="http://www.immunix.org/products.html#stackguard" name="StackGuard"> would
probably be a good idea for even more protection.  Using it is easy; it's
just like using ordinary gcc.  Also, <url
url="http://cr.yp.to/dnscache.html" name="DNScache"> is a secure replacement
for BIND, written by Dan Bernstein.  Dan is the author of qmail, and DNScache
appears to follow a similar philosophy.

<sect1>Where?<label id="where">

<p>
The latest version of this document is always available from the web site of the
Linux/Open Source Users of Regina, Sask., at <url
url="http://www.losurs.org/docs/howto/Chroot-BIND.html">.

There is now a Japanese translation of this document, maintained by Nakano
Takeo <tt>nakano at apm.seikei.ac.jp</>.  This is available at <url
url="http://www.linux.or.jp/JF/JFdocs/Chroot-BIND-HOWTO.html">.

BIND is available from <url url="http://www.isc.org/" name="the Internet
Software Consortium"> at <url url="http://www.isc.org/bind.html">.  As of this
writing, the current version of BIND 9 is 9.2.0.  BIND 9 has been out for some
time now, and many people are using it in production.  Nevertheless, some more
conservative sorts still prefer to remain with BIND 8.  If you are such a
person, please see my Chroot-BIND8 HOWTO (available from the same location)
for details on chrooting it, but be warned that BIND 8 is much messier to
chroot.

Keep in mind that there are <bf>known</> security holes in many earlier
versions of BIND, so make very sure that you're running the latest version!

<sect1>How?

<p>
I wrote this document based on my experiences in setting BIND up in a chroot
environment.  In my case, I already had an existing BIND installation in the
form of a package that came with my Linux distribution.  I'll assume that most
of you are probably in the same situation, and will simply be transferring over
and modifying the configuration files from your existing BIND installation, and
then removing the package before installing the new one.  Don't remove the
package yet, though; we may want some files from it first.

If this is not the case for you, you should still be able to follow this
document.  The only difference is that, where I refer to copying an existing
file, you first have to create it yourself.  The DNS HOWTO may be helpful for
this.

<sect1>Disclaimer

<p>
These steps worked for me, on my system; your mileage may vary.  This is but
one way to approach this; there are other ways to set the same thing up
(although the general approach will be the same).  It just happens that this
was the first way that I tried that worked, so I wrote it down.

My BIND experience to date has been installing on Linux servers.  However, most
of the instructions in this document should be easily applicable to other
flavours of UNIX as well, and I shall try to point out differences of which I am
aware.  I've also received suggestions from people using other distributions
and other platforms, and I've tried to incorporate their comments where
possible.

If you run Linux, you need to make sure that you're running a 2.4 kernel before
attempting this.  The <tt>-u</> switch (to run as a non-root user) requires
this newer kernel.

<sect>Preparing the Jail

<sect1>Creating a User

<p>
As mentioned in the introduction, it's not a good idea to run BIND as root.  So,
before we begin, let's create a separate user for BIND.  Note that you should
never use an existing generic user like <tt>nobody</> for this purpose.
However, some distributions, such as SuSE and Linux Mandrake have started
providing a specific user (generally called <tt>named</>); you can simply adapt
this user for our purposes, if you like.

This requires adding a line something like the following to <tt>/etc/passwd</>:
<tscreen><verb>
named:x:200:200:Nameserver:/chroot/named:/bin/false
</verb></tscreen>
And one like this to <tt>/etc/group</>:
<tscreen><verb>
named:x:200:
</verb></tscreen>
This creates a user and group called <tt>named</> for BIND.  Make sure that the
UID and GID (both 200 in this example) are unique on your system.  The shell is
set to <tt>/bin/false</> because this user will never need to log in.

<sect1>Directory Structure

<p>
Now, we must set up the directory structure that we will use for the chroot jail
in which BIND will live.  This can be anywhere on your filesystem; the truly
paranoid may even want to put it on a separate volume.  I shall assume that you
will use <tt>/chroot/named</>.  Let's start by creating the following directory
structure:

<tscreen><verb>
/chroot
  +-- named
       +-- dev
       +-- etc
       |    +-- namedb
       |         +-- slave
       +-- var
            +-- run
</verb></tscreen>

If you use GNU <tt>mkdir</> (such as on a Linux system), you can create this
directory structure like this:

<tscreen><verb>
# mkdir -p /chroot/named
# cd /chroot/named
# mkdir -p dev etc/namedb/slave var/run
</verb></tscreen>

<sect1>Placing the BIND Data

<p>
Assuming that you have already done a conventional installation of BIND and are
using it, you will already have an existing <tt>named.conf</> and zone files.
These files must now be moved (or copied, to be safe) into the chroot jail, so
that BIND can get at them.  <tt>named.conf</> goes in <tt>/chroot/named/etc</>,
and the zone files can go in <tt>/chroot/named/etc/namedb</>.  For example:
<tscreen><verb>
# cp -p /etc/named.conf /chroot/named/etc/

# cp -a /var/named/* /chroot/named/etc/namedb/
</verb></tscreen>

BIND would normally need to write to the <tt>namedb</> directory, but in the
interests of tightening security, we will not allow it to do this.  If your
nameserver serves as a slave for any zones, it will need to update these zone
files, which means we'll have to store them in a separate directory, to which
BIND does have write access.
<tscreen><verb>
# chown -R named:named /chroot/named/etc/namedb/slave
</verb></tscreen>

Keep in mind that'll you have to move any slave zones you have into this
directory, and update your <tt>named.conf</> accordingly.

BIND will also need to write to the <tt>/var/run</> directory, to put its
pidfile and statistical information there, so let's allow it to do so:
<tscreen><verb>
# chown named:named /chroot/named/var/run
</verb></tscreen>

<sect1>System Support Files

<p>
Once BIND is running in the chroot jail, it will not be able to access files
outside the jail <bf>at all</>.  However, it needs to access a few key files,
although not nearly as many as BIND 8 did.

One file that BIND will need inside its jail is good ol' <tt>/dev/null</>.
Note that the exact command necessary to create this device node may vary from
system to system; check your <tt>/dev/MAKEDEV</> script to be sure.  Some
systems may also require <tt>/dev/zero</>, which can created similarly.  It's
reported that the BIND 9.2.0 release candidates now require <tt>/dev/random</>
as well.  For most Linux systems, we can use the following commands:
<tscreen><verb>
# mknod /chroot/named/dev/null c 1 3
# mknod /chroot/named/dev/random c 1 8
# chmod 666 /chroot/named/dev/{null,random}
</verb></tscreen>

For FreeBSD 4.3, this is:
<tscreen><verb>
# mknod /chroot/named/dev/null c 2 2
# mknod /chroot/named/dev/random c 2 3
# chmod 666 /chroot/named/dev/{null,random}
</verb></tscreen>

You also need another file in the <tt>/etc</> directory inside the jail.  You
must copy <tt>/etc/localtime</> (this is sometimes known as
<tt>/usr/lib/zoneinfo/localtime</> on some systems) in there so that BIND logs
things with the right time on them.  The following command will take care
of this:
<tscreen><verb>
# cp /etc/localtime /chroot/named/etc/
</verb></tscreen>

<sect1>Logging<label id="logging">

<p>
Unlike a conventional jailbird, BIND can't just scribble its log entries on the
walls :-).  Normally, BIND logs through <tt>syslogd</>, the system logging daemon.
However, this type of logging is performed by sending the log entries to the
special socket <tt>/dev/log</>.  Since this is outside the jail, BIND can't use
it any more.  Fortuantely, there are a couple options to work around this.

<sect2>The Ideal Solution

<p>
The ideal solution to this dilemma requires a reasonably recent version of
<tt>syslogd</> which supports the <tt>-a</> switch introduced by OpenBSD.
Check the manpage for your <tt>syslogd(8)</> to see if you have such a
version.

If you do, all you have to do is add the switch ``<tt>-a
/chroot/named/dev/log</>'' to the command line when you launch <tt>syslogd</>.
On systems which use a full SysV-init (which includes most Linux distributions),
this is typically done in the file <tt>/etc/rc.d/init.d/syslog</>.  For example,
on my Red Hat Linux system, I changed the line
<tscreen><verb>
daemon syslogd -m 0
</verb></tscreen>
to
<tscreen><verb>
daemon syslogd -m 0 -a /chroot/named/dev/log
</verb></tscreen>

Interestingly, as of Red Hat 7.2, Red Hat has apparently made this process
even easier.  There is now a file called <tt>/etc/sysconfig/syslog</> in which
extra parameters for syslogd can be defined.

On Caldera OpenLinux systems, they use a daemon launcher called <tt>ssd</>,
which reads configuration from <tt>/etc/sysconfig/daemons/syslog</>.  You
simply need to modify the options line to look like this:
<tscreen><verb>
OPTIONS_SYSLOGD="-m 0 -a /chroot/named/dev/log"
</verb></tscreen>

Similarly, on SuSE systems, I'm told that the best place to add this switch
is in the <tt>/etc/rc.config</> file.  Changing the line
<tscreen><verb>
SYSLOGD_PARAMS=""
</verb></tscreen>
to read
<tscreen><verb>
SYSLOGD_PARAMS="-a /chroot/named/dev/log"
</verb></tscreen>
should do the trick.

And, last but not least, for FreeBSD 4.3 you can apparently just edit the
<tt>rc.conf</> file and put in the following:
<tscreen><verb>
syslogd_flags="-s -l /chroot/named/dev/log"
</verb></tscreen>
The <tt>-s</> is for security reasons, and is part of the default settings.
The <tt>-l</> is a local path on which to put another logging node.

Once you've figured out how to make this change for your system, simply
restart <tt>syslogd</>, either by killing it and launching it again (with
the extra parameters), or by using the SysV-init script to do it for you:
<tscreen><verb>
# /etc/rc.d/init.d/syslog stop
# /etc/rc.d/init.d/syslog start
</verb></tscreen>

Once it's been restarted, you should see a ``file'' in <tt>/chroot/named/dev</>
called <tt>log</>, that looks something like this:

<verb>srw-rw-rw-   1 root     root            0 Mar 13 20:58 log</verb>

<sect2>The Other Solutions

<p>
If you have an older <tt>syslogd</>, then you'll have to find another way to do
your logging.  There are a couple programs out there, such as <tt>holelogd</>,
which are designed to help by acting as a ``proxy'' and accepting log entries
from the chrooted BIND and passing them out to the regular <tt>/dev/log</>
socket.

Alteratively, you can simply configure BIND to log to files instead of going
through syslog.  See the BIND documentation for more details if you choose to go
this route.

<sect1>Tightening Permissions<label id="perm">

<p>
First of all, feel free to restrict access to the whole <tt>/chroot</>
directory to the <tt>root</> user.  Of course, not everybody may want to do
this, especially if you have other software installed in that tree that
doesn't appreciate it.

<tscreen><verb>
# chown root /chroot
# chmod 700 /chroot
</verb></tscreen>

You can also safely restrict access to <tt>/chroot/named</> to the <tt>named</>
user.

<tscreen><verb>
# chown named:named /chroot/named
# chmod 700 /chroot/named
</verb></tscreen>

For even more tightening, on Linux systems we can make a few of the files and
directories immutable, using the <tt>chattr</> tool on ext2 filesystems.

<tscreen><verb>
# cd /chroot/named
# chattr +i etc etc/localtime var
</verb></tscreen>

Equivalently, on FreeBSD 4.3, you want to look into <tt>chflags</> if you
wish to make things immutable.  As an example, the following should change
everything in the <tt>/chroot/named/etc</> directory to immutable:
<tscreen><verb>
# chflags schg /chroot/named/etc/*(*).
</verb></tscreen>

It would be nice to do this for the <tt>dev</> directory too, but unfortunately
that would prevent <tt>syslogd</> from creating its <tt>dev/log</> socket.
You may also choose to set the immutable bit on other files in the jail as
well, such as your primary zone files, if they aren't expected to change.

<sect>Compiling and Installing Your Shiny New BIND<label id="compiling">

<p>
<sect1>Doing the Compile

<p>
Compiling BIND 9 for use in a chroot jail should be a much more pleasant
experience than BIND 8 was.  In fact, you don't have to do anything special;
the standard <tt>./configure && make</> should suffice.

Keep in mind that if you want to enable IPv6 support in BIND
(<tt>--enable-ipv6</>) on Linux systems, you need matching versions of kernel
and glibc.  If you have kernel 2.2, you need glibc 2.1, and if you have kernel
2.4, you need glibc 2.2.  BIND is quite picky about this.

<sect>Installing Your Shiny New BIND<label id="installing">

<p>
I should mention that if you have an existing installation of BIND, such as from
an RPM, you should probably remove it before installing the new one.  On Red Hat
systems, this probably means removing the packages <tt>bind</> and
<tt>bind-utils</>, and possibly <tt>bind-devel</> and <tt>caching-nameserver</>,
if you have them.

You may want to save a copy of the init script (e.g.,
<tt>/etc/rc.d/init.d/named</>), if any, before doing so; it'll be useful later
on.

If you are upgrading from an older version of BIND, such as BIND 8, you will
want to read the migration documentation in the file <tt>doc/misc/migration</>
in the BIND source package.  I don't deal with any migration issues in this
document; I simply assume that you are replacing an existing, working
installation of BIND 9.

<sect1>Installing the Binaries

<p>
This is the easy part :-).  Just run <tt>make install</> and let it take care of
it for you.  Really, that's it!

<sect1>Setting up the Init Script

<p>
If you have an existing init script from your distribution, it would probably
be best simply to modify it to run the new binary, with the appropriate
switches.  The switches are... <it>(drumroll please...)</>
<itemize>
<item><tt>-u named</>, which tells BIND to run as the user <tt>named</>, rather
than <tt>root</>.
<item><tt>-t /chroot/named</>, which tells BIND to chroot itself to the jail
that we've set up.
<item><tt>-c /etc/named.conf</>, which tells BIND where to find its
configuration file within the jail.
</itemize>

The following is the init script I use with my Red Hat 6.0 system.  As you can
see, it is almost exactly the same as the way it shipped from Red Hat.  I
haven't tried the <tt>rndc</> commands yet, but I can't see any reason why
they shouldn't work.
<tscreen><code>
#!/bin/sh
#
# named           This shell script takes care of starting and stopping
#                 named (BIND DNS server).
#
# chkconfig: 345 55 45
# description: named (BIND) is a Domain Name Server (DNS) \
# that is used to resolve host names to IP addresses.
# probe: true

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0

[ -f /usr/local/sbin/named ] || exit 0

[ -f /chroot/named/etc/named.conf ] || exit 0

# See how we were called.
case "$1" in
  start)
        # Start daemons.
        echo -n "Starting named: "
        daemon /usr/local/sbin/named -u named -t /chroot/named -c /etc/named.conf
        echo
        touch /var/lock/subsys/named
        ;;
  stop)
        # Stop daemons.
        echo -n "Shutting down named: "
        killproc named
        rm -f /var/lock/subsys/named
        echo
        ;;
  status)
        status named
        exit $?
        ;;
  restart)
        $0 stop
        $0 start
        exit $?
        ;;
  reload)
        /usr/local/sbin/rndc reload
        exit $?
        ;; 
  probe)
        # named knows how to reload intelligently; we don't want linuxconf
        # to offer to restart every time
        /usr/local/sbin/rndc reload >/dev/null 2>&1 || echo start
        exit 0
        ;;
  
  *)
        echo "Usage: named {start|stop|status|restart|reload}"
        exit 1
esac

exit 0
</code></tscreen>

As with syslogd, as of Red Hat 7.2 this process is now even easier.  There is
a file called <tt>/etc/sysconfig/named</> in which extra parameters for syslogd
can be defined.  The default <tt>/etc/rc.d/init.d/named</> on Red Hat 7.2,
however, will check for the existance of <tt>/etc/named.conf</> before
starting.  You will need to correct this path.

On Caldera OpenLinux systems, you simply need to modify the variables defined
at the top, and it will apparently take care of the rest for you:
<tscreen><verb>
NAME=named
DAEMON=/usr/local/sbin/$NAME
OPTIONS="-t /chroot/named -u named -c /etc/named.conf"
</verb></tscreen>

And for FreeBSD 4.3, you can edit the <tt>rc.conf</> file and put in the
following:
<tscreen><verb>
named_enable="YES"
named_program="chroot/named/bin/named"
named_flags="-u named -t /chroot/named -c /etc/namedb/named.conf"
</verb></tscreen>

<sect1>Configuration Changes

<p>
You will also have to add or change a few options in your <tt>named.conf</> to
keep the various directories straight.  In particular, you should add (or
change, if you already have them) the following directives in the <tt>options</>
section:
<tscreen><verb>
directory "/etc/namedb";
pid-file "/var/run/named.pid";
statistics-file "/var/run/named.stats";
</verb></tscreen>
Since this file is being read by the <tt>named</> daemon, all the paths are of
course relative to the chroot jail.  As of this writing, BIND 9 does not support
many of the statistics and dump files that previous versions did.  Presumably
later versions will; if you are running such a version, you may have to add
additional entries to cause BIND to write them to the <tt>/var/run</> directory
as well.

<sect>The End

<sect1>Launching BIND

<p>
Everything should be set up, and you should be ready to put your new, more
secure BIND into action.  Assuming you set up a SysV-style init script, you can
simply launch it as:
<tscreen><verb>
# /etc/rc.d/init.d/named start
</verb></tscreen>
Make sure you kill any old versions of BIND still running before doing this.

<sect1>That's It!

<p>
You can go take a nap now ;-).

<sect>Appendix - Upgrading BIND Later<label id="upgrading">

<p>So, you had BIND 9.1.2 all nicely chrooted and tweaked to your taste...  and
then you hear this nasty rumour that BIND 9.1.3 is finally out, and you just
have to give it a try right away.  Do you have to go through this whole
long process to install this new version?

Nope.  In fact, you really just need to compile the new BIND and install it
over top of the old one.  Just don't forget to kill the old version and restart
BIND, or it'll still be the old version running!

<sect>Appendix - Thanks<label id="thanks">

<p>
I'd like to thank the following people for their assistance in the creation
of this HOWTO:

<itemize>

<item>Lonny Selinger <tt>&lt;lonny at abyss.za.org&gt;</> for "testing" the
first version of this HOWTO and making sure that I didn't miss any steps.

<item>Chirik <tt>&lt;chirik at CastleFur.COM&gt;</>, Dwayne Litzenberger
<tt>&lt;dlitz at dlitz.net&gt;</>, Phil Bambridge <tt>&lt;phil.b at
cableinet.co.uk&gt;</>, Robert Cole <tt>&lt;rcole at metrum-datatape.com&gt;</>,
Colin MacDonald <tt>&lt;colinm at telus.net&gt;</>, and others for pointing out
errors, omissions, and providing other useful advice to make this HOWTO even
better.

<item>Erik Wallin <tt>&lt;erikw at sec.se&gt;</> and Brian Cervenka
<tt>&lt;brian at zerobelow.org&gt;</> for providing good suggestions for
further tightening the jail.

<item>Robert Dalton <tt>&lt;support at accesswest.com&gt;</> for suggesting
a couple more example commands, and pointing out BIND 9.2.0's need of
<tt>/dev/random</>.

<item>Eric McCormick <tt>&lt;hostmaster at cybertime.net&gt;</> for the FreeBSD
4.3 information.

<item>Tan Zheng Da <tt>&lt;tzd at pobox.com&gt;</> for the details about the
changes in Red Hat 7.2 that make this a little easier.

</itemize>

And last but certainly not least, I'd like to thank Nakano Takeo <tt>&lt;nakano
at apm.seikei.ac.jp&gt;</> for translating the Chroot-BIND HOWTO into
Japanese.  You can find his translation at <url
url="http://www.linux.or.jp/JF/JFdocs/Chroot-BIND-HOWTO.html">.

<sect>Appendix - Document Distribution Policy<label id="legal">

<p>
Copyright &copy; Scott Wunsch, 2000-2001.  This document may be distributed only
subject to the terms set forth in the LDP licence at <url
url="http://metalab.unc.edu/LDP/COPYRIGHT.html">.

This HOWTO is free documentation; you can redistribute it and/or modify it under
the terms of the LDP licence.  It is distributed in the hope that it will be
useful, but <bf>without any warranty</>; without even the impled warranty of
merchantability or fitness for a particular purpose.  See the LDP licence for
more details.

</article>
