This document was published in the
October 2001 Daemonnews
issue.
Running BIND9 in a chroot cage
using NetBSD's new startup system
Hubert Feyrer, August 2001
Preface
The Berkeley Internet Name Domain software, AKA "named" daemon, is one
of the most important pieces in the mosaic that represents the
structure of the Internet. Due to this importance, it's unfortunately
also a preferred target for hackers, who want to keep DNS from working
or worse, use security holes in the software to gain control over the
machine. Such hacking of a DNS server can result in a break of
confidentiality of the data returned from this server, and is in
general a bad thing.
The "named" process usually runs as the root user on a system. There
are two things that can be done to make things more safe against
breakins. The first is to not let the daemon run with full system
(root) privileges, the other is to limit the possible damage by
putting the named into a chroot environment where it can't access
anything but a few files it needs to run. Running a program with a
different root directory than /, which is done via the chroot command
is also called "sandboxing" or "jailing".
This article describes the steps that are necessary to run the BIND9
package on NetBSD in a chroot cage, using NetBSD's new rc.d based
startup system.
Installing the BIND package
We will demonstrate the setup of named in a chroot cage using BIND
version 9 as an example. It can be installed e.g. via pkgsrc or as a
binary package. When installing via pkgsrc, the following steps will
do what we need:
# cd /usr/pkgsrc/net/bind9
# make install
As an alternative, a binary package can be installed. Look for
bind-9.1.3.tgz on ftp.netbsd.org in the /pub/NetBSD/packages
directory.
Creating a "named" user
After installing the BIND package, we need to create an account under
which the "named" daemon will run. We call the user "named", and it
should not have system (root) privileges. We will later use the user's
home directory as the root directory for our chroot cage. The easiest
way to create such a user is using useradd(8):
# useradd -m named
Setup chroot cage in /home/named
When running the "named" daemon in a chroot cage, there are a few
system files needed to run it, and they need to be present in the
chroot directory. The following files are needed:
- The "named" binary itself. It's located in /usr/pkg/sbin.
- All the shared libraries the "named" binary needs. We can use
"ldd" to find which libs are needed on ELF platforms; on a.out,
some guessing will be needed - take the following list for a
hint:
# ldd /usr/pkg/sbin/named | awk '/=>/ { gsub(".so.*", ".so*", $3); print $3 ; }'
/usr/pkg/lib/liblwres.so*
/usr/pkg/lib/libomapi.so*
/usr/pkg/lib/libdns.so*
/usr/lib/libcrypto.so*
/usr/pkg/lib/libisc.so*
/usr/lib/libc.so*
This example uses all versions (.so*) of the shared libs, to not
catch only the numbered symlinks.
- The shared library loader, either ld.so (a.out) or ld.elf_so
(ELF). They are both located in /usr/libexec and are needed by
any dynamically linked program to load it's shared libraries.
- The "named" daemon's config files. These are usually stored in
the /etc/named directory plus in the /etc/named.conf config file
(which is often only a symlink to /etc/named/named.conf).
All the files named above need to be copied in the chroot cage,
/home/named.
Here's a command that copies the named files into their place:
tar plcf - \
/usr/pkg/sbin/named \
`ldd /usr/pkg/sbin/named | awk '/=>/ { gsub(".so.*", ".so*", $3); print $3 ; }'` \
/usr/libexec/ld*so \
/etc/named.conf \
/etc/namedb \
| ( cd /home/named ; tar vxf - )
Setting up password information in the chroot cage
Besides the files described above, one more piece of information is
needed. Password information is needed so the "named" binary can obtain
information about the new user ID it should switch to after
starting up in the chroot cage. Besides /etc/passwd the files
master.passwd as well as their database backends, pwd.db and
spwd.db are needed. These files could be copied from the /etc dir, but
for security reasons it's better to create only a minimal data set
that contains only the "named" user.
The easiest way to set up a set of passwd data files is to setup the
user in the "normal" system as we have done above, then extract the
data from /etc/master.passwd and create the other files from that:
# grep named /etc/master.passwd > /home/named/etc/master.passwd
# pwd_mkdb -p -d /home/named /home/named/etc/master.passwd
Syslog logging from the chroot cage
syslogd creates a socket /var/run/log that programs can log messages
to. As our "named" will run in a chroot environment, it can't reach
that file. In order to fix this, NetBSD's syslogd allows specifying
several locations in which logging sockets can be created. We'll tell
syslog to look in $named_chroot/var/run/log.
To keep data in one place (and as we needed it later :), we use the
variable $named_chroot in /etc/rc.conf to reference the chroot dir.
So to change syslogd as described above, add the following to your
/etc/rc.conf file:
named_chroot=/home/named
syslogd_flags="-p $named_chroot/var/run/log -p /var/run/log"
"named" will not run with root privileges but with that of the "named"
user. We have to make sure that user (and it's "named" process :) can
create the file in which the process' process ID (PID) will be
stored. The easiest solution is to hand over the /home/named/var/run
directory to the "named" user:
# install -d -o named /home/named/var/run
After these steps, the "syslogd" daemon should be restarted to
(re)create it's routing sockets:
# sh /etc/rc.d/syslogd restart
Make sure that everything is set up properly after doing so:
# ls -l /var/run/log /home/named/var/run/log
srw-rw-rw- 1 root named 0 Aug 25 22:59 /home/named/var/run/log
srw-rw-rw- 1 root wheel 0 Aug 25 22:59 /var/run/log
Preparing rc.conf
Of course the startup system needs to be told about our plans,
too. For that, the "named" version that comes with NetBSD should be
disabled, and our "named" (version 9) be enabled in /etc/rc.conf. We
also need to tell the system to start our "named" in a chroot dir, and
to give the "named" daemon a command to change to the "named" user
privileges after initial startup. The following lines in /etc/rc.conf
will do the job:
named=no # disable NetBSD's named
named9=yes # enable our named
named_flags="-u named" # switch privileges to this user
Some more nits
Before we can fire up our new "named", we need to do two more things.
First, NetBSD 1.5.2 and before has a bug in /etc/rc.subr that prevents
it from looking for the PID-file in the right place when chroot is
used. We simply place a symlink to work around this bug:
ln -s /home/named/var/run/named.pid /var/run/named.pid
Second, we want to make starting our new "named" permanent when the
system starts up. The easiest way to do so is to make the "named9"
startup script that comes with the BIND9 package available in
/etc/rc.d:
ln -s /usr/pkg/etc/rc.d/named9 /etc/rc.d
This only works if /usr/pkg is on the root (/) filesystem though, else
the /etc/rc.d/named9 symlink cannot be read at system startup. If your
/usr/pkg is not on the root filesystem, you can put the following into
/etc/rc.local instead:
if [ -f /usr/pkg/etc/rc.d/named9 ] ; then
sh /usr/pkg/etc/rc.d/named9 start
fi
Starting named
Now we are set up to run our "named" in a chroot cage! Using the new
NetBSD startup system, the easiest way to do so is:
sh /etc/rc.d/named forcestop # just in case
sh /etc/rc.d/named9 start
If you cannot have a /etc/rc.d/named9 script (link, see above), you
can call the pkg's script via it's real name manually:
# sh /usr/pkg/etc/rc.d/named9
To make sure everything is setup fine now, check with the following
command:
# ps -aux | grep named
named 19306 0.0 0.5 868 632 ?? Is 11:44PM 0:00.18 /usr/pkg/sbin/named -u named
See the "named" user ID here in the first column, telling that the
"named" process is running under the designated non-root user-id.
Checking the root dir can be done with the fstat(1) command:
# fstat -p 19306
USER CMD PID FD MOUNT INUM MODE SZ|DV R/W
named named 19306 root /home 3745792 drwxr-xr-x 512 r
...
# ls -ldi /home/named
3745792 drwxr-xr-x 5 named named 512 Aug 25 22:58 /home/named
We use fstat(1) to get the mountpoint and inode number of the process'
root directory. We use ls(1) to verify it's actualy the right
directory by comparing the inode numbers.
Conclusion
While trying to set the chroot cage for "named", I ran into a small
problem with how to determine under which user the daemon should
run. There are two possible ways to set this. Either by using su(1)
before starting the daemon, or by telling the user to switch to our
"named" user on it's own. It turns out that the first option is not
usable for our purpose, as the daemon needs the root privileges it
first gets started with to bind to it's listening socket, which is on
a privileged port (53/UDP). As a result, the setup above uses the
"name_flags" variable to hand the username to switch to to "named",
instead or using "name_user", which would invoke su(1).
In general, the above procedure shows how to further tune the default
NetBSD installation. While NetBSD is secure in the default
installation, various aspects can be improved even more if the
application demands it. Most people won't want to run their own
DNS server at all, but there are good reasons to make things locked
tight if you do so, and if you're doing a business on it and your and
your customers' business depends on it, more specific actions are
needed.
There are many other areas where specific subsystems can be tuned such
as samba, NFS, web servers, but discussing them would be too much here
and now. This article should give some hints on to tune any subsystem
in general, though.
(c) Copyright 20000110 Hubert Feyrer
$Id: named-chroot.html,v 1.4 2001/08/27 23:31:04 feyrer Exp $