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 nobody for this purpose. However, some
distributions, such as SuSE and Linux Mandrake have started
providing a specific user (generally called named);
you can simply adapt this user for our purposes, if you like.
This requires adding a line something like the following to
/etc/passwd:
And one like this tonamed:x:200:200:Nameserver:/chroot/named:/bin/false
/etc/group:
This creates a user and group callednamed:x:200:
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
/bin/false because this user will never need to log
in.
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
/chroot/named. Let's start by creating the following
directory structure:
/chroot +-- named +-- dev +-- etc | +-- namedb | +-- slave +-- var +-- run
If you use GNU mkdir (such as on a Linux system),
you can create this directory structure like this:
# mkdir -p /chroot/named # cd /chroot/named # mkdir -p dev etc/namedb/slave var/run
Assuming that you have already done a conventional installation
of BIND and are using it, you will already have an existing
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. named.conf goes in
/chroot/named/etc, and the zone files can go in
/chroot/named/etc/namedb. For example:
# cp -p /etc/named.conf /chroot/named/etc/ # cp -a /var/named/* /chroot/named/etc/namedb/
BIND would normally need to write to the 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.
# chown -R named:named /chroot/named/etc/namedb/slave
Keep in mind that'll you have to move any slave zones you have
into this directory, and update your named.conf
accordingly.
BIND will also need to write to the /var/run
directory, to put its pidfile and statistical information there,
so let's allow it to do so:
# chown named:named /chroot/named/var/run
Once BIND is running in the chroot jail, it will not be able to access files outside the jail 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'
/dev/null. Note that the exact command necessary to
create this device node may vary from system to system; check
your /dev/MAKEDEV script to be sure. Some systems
may also require /dev/zero, which can created
similarly. It's reported that the BIND 9.2.0 release candidates
now require /dev/random as well. For most Linux
systems, we can use the following commands:
# mknod /chroot/named/dev/null c 1 3 # mknod /chroot/named/dev/random c 1 8 # chmod 666 /chroot/named/dev/{null,random}
For FreeBSD 4.3, this is:
# mknod /chroot/named/dev/null c 2 2 # mknod /chroot/named/dev/random c 2 3 # chmod 666 /chroot/named/dev/{null,random}
You also need another file in the /etc directory
inside the jail. You must copy /etc/localtime (this
is sometimes known as /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:
# cp /etc/localtime /chroot/named/etc/
Unlike a conventional jailbird, BIND can't just scribble its log
entries on the walls :-). Normally, BIND logs through
syslogd, the system logging daemon. However, this
type of logging is performed by sending the log entries to the
special socket /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.
The ideal solution to this dilemma requires a reasonably recent
version of syslogd which supports the
-a switch introduced by OpenBSD. Check the manpage
for your syslogd(8) to see if you have such a
version.
If you do, all you have to do is add the switch ``-a
/chroot/named/dev/log'' to the command line when you
launch syslogd. On systems which use a full
SysV-init (which includes most Linux distributions), this is
typically done in the file /etc/rc.d/init.d/syslog.
For example, on my Red Hat Linux system, I changed the line
todaemon syslogd -m 0
daemon syslogd -m 0 -a /chroot/named/dev/log
Interestingly, as of Red Hat 7.2, Red Hat has apparently made
this process even easier. There is now a file called
/etc/sysconfig/syslog in which extra parameters for
syslogd can be defined.
On Caldera OpenLinux systems, they use a daemon launcher called
ssd, which reads configuration from
/etc/sysconfig/daemons/syslog. You simply need to
modify the options line to look like this:
OPTIONS_SYSLOGD="-m 0 -a /chroot/named/dev/log"
Similarly, on SuSE systems, I'm told that the best place to add
this switch is in the /etc/rc.config file. Changing
the line
to readSYSLOGD_PARAMS=""
should do the trick.SYSLOGD_PARAMS="-a /chroot/named/dev/log"
And, last but not least, for FreeBSD 4.3 you can apparently just
edit the rc.conf file and put in the following:
Thesyslogd_flags="-s -l /chroot/named/dev/log"
-s is for security reasons, and is
part of the default settings. The -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 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:
# /etc/rc.d/init.d/syslog stop # /etc/rc.d/init.d/syslog start
Once it's been restarted, you should see a ``file'' in
/chroot/named/dev called log, that
looks something like this:
srw-rw-rw- 1 root root 0 Mar 13 20:58 log
If you have an older syslogd, then you'll have to
find another way to do your logging. There are a couple programs
out there, such as 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
/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.
First of all, feel free to restrict access to the whole
/chroot directory to the 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.
# chown root /chroot # chmod 700 /chroot
You can also safely restrict access to /chroot/named
to the named user.
# chown named:named /chroot/named # chmod 700 /chroot/named
For even more tightening, on Linux systems we can make a few of
the files and directories immutable, using the
chattr tool on ext2 filesystems.
# cd /chroot/named # chattr +i etc etc/localtime var
Equivalently, on FreeBSD 4.3, you want to look into
chflags if you wish to make things immutable. As an
example, the following should change everything in the
/chroot/named/etc directory to immutable:
# chflags schg /chroot/named/etc/*(*).
It would be nice to do this for the dev directory
too, but unfortunately that would prevent syslogd
from creating its 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.