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: 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 $