chrooting BIND 8.x on FreeBSD 4.x
Nicholas "Echo|Fox" Paufler - echofox_NOSPAM_@discordia_NOSPAM_.ca (remove the obvious to mail me)
v1.02 - Last Updated: March 16, 2002
This guide will walk you though the steps you will need to take to chroot BIND 8 on a FreeBSD 4.x system. I'm not going to go in depth into BIND configuration as that is a lesson in itself.
First off, let's answer a few questions.
What is BIND?
BIND is the Berkeley Internet Name Domain service, developed by the Internet Software Consortium. It is one implementation of the domain name service that is one of the core elements of the Internet.
What does it mean to chroot something?
chroot is a program that CHanges the ROot of a program. In other words, for as long as that program is running it is constrained to a specific directory and cannot descend below that root. This is a commonly used tactic for securing UNIX machines because it can greatly diminish the amount of damage a malicious cracker can do if they manage to gain access by exploiting a specific program.
Why should we chroot BIND?
Throughout the years there have been many exploits in BIND and it has always been a common target for crackers seeking to break into systems. In recent days a concerted effort has been made to secure the BIND source code and it is considered much safer now. Still, it is highly encouraged to be proactive about security and chrooting BIND now and limiting access is a simple way you can do so.
If someone does manage to exploit the BIND service they will be locked into the chrooted directory and while they could trash your data in that directory they will have a much harder time touching the rest of the system. There are no guarantees, of course, but it is a wise precaution and combined with other security measures can make your system vastly more secure.
What will I need?
This guide is specifically written for chrooting BIND 8 on a FreeBSD 4.x system, as such, assumes that that is what you have. The same steps will probably apply to both FreeBSD 3.x and FreeBSD 5.x but since I have no such machines I cannot test. Also, BIND 9 is available now and the steps should be similar however there are some noticeable differences (The replacement of ndc with rndc, for example) so it is best to consider this guide a starting point rather than a walk-through if you wish to apply it to BIND 9.
This document was written based on a FreeBSD 4.5-STABLE machine running BIND 8.3.1.
Before we start to configure anything we are going to want to create a few directories for our chrooted environment. By default, FreeBSD stores it's BIND configuration files in /etc/namedb. However, I would not recommend that you use that directory for our environment and instead create a new one. I would suggest something like /var/named but whatever you choose is up to you, just be aware that if you use a different directory, replace /var/named with that directory anywhere I reference it.
A normal install of FreeBSD will create a user called bind as well as a group called bind. If these do not exist on your system you will need to add them. Make sure the user has it's password disabled and it's shell set to something like /sbin/nologin for security.
Warning: All of the following commands need to be executed as root so either su to root first or use sudo to execute each command.
We need to create the following directories:
Once that is done we can start copying files. Assuming you have an existing BIND installation running in /etc/namedb (or you have never run BIND before), execute the following copy commands:
cp /etc/namedb/named.conf /var/named/named.conf
cp /etc/namedb/make-localhost /var/named/make-localhost
cp /etc/namedb/named.root /var/named/named.root
cp /etc/localtime /var/named/etc/localtime
cp /usr/lib/libc.so.4 /var/named/usr/lib/libc.so.4
cp /usr/libexec/ld-elf.so.1 /var/named/usr/libexec/ld-elf.so.1
cp /usr/libexec/named-xfer /var/named/usr/libexec/named-xfer
Before we go any further let's make a quick change to named.conf. Open up /var/named/named.conf with your favorite editor and go down to the directory = line. By default it will be set to /etc/namedb, but we will need to change that because a chrooted instance of BIND will not be able to see that directory. Change the "/etc/namedb" to "/" if you want to store your zonefiles in the /var/named directory. This is fine if you only have a handful of zonefiles, but if have a large number of them you may want to put them in a separate directory, for example one called /zonefiles. Change the directory value to whatever directory you would like to store them in and then make sure you make that directory afterwards (i.e. mkdir /var/named/zonefiles).
Next up we need to create some device entries, so do the following:
mknod /var/named/dev/null c 2 2
mknod /var/named/dev/random c 2 3
Almost done! Since we are going to be running bind as the user bind we need to set the permissions on all of these files so they are owned by bind. Do the following:
chown -R bind:bind /var/named/*
If you managed to do all of that successfully, congratulations, you should now have a properly chrooted BIND setup. However there is one last thing we need to do. Since BIND will be chrooted into the /var/named directory it is not going to be able to write to the /var/log directory and thus you won't have any logging. Not much good to you, obviously, so we need to take advantage of a feature of the syslog daemon. By using the -l option we can have it create another syslog socket inside of the BIND chroot that will allow it to work normally.
To do this we need to stop the syslog daemon and then restart it with additional options so again, execute the following commands:
syslogd -s -l /var/named/dev/log
This will restart syslogd with an additional logging socket in /var/named/dev, so if you do an ls -la in /var/named/dev/log you should see the following:
srw-rw-rw- 1 root bind 0 Mar 16 15:53 log
It is now time to test our installation. If BIND is already running you will need to stop it first (i.e. killall named), otherwise our chrooted instance will not be able to start correctly. Run the following command and BIND should start up:
/usr/sbin/named -c /named.conf -t /var/named -u bind -g bind
For reference, the -c file tells named which config file to read (and it is /named.conf because it is in the root of the chrooted environment). The -t option tells named to chroot itself to the defined directory, and the -u and -g options tell it what user and group to run as.
If you don't see any errors, tail /var/log/messages and you should see a few lines like so:
Mar 16 22:58:52 www named: starting (/named.conf). named 8.3.1-REL Fri Mar 8 00:27:31 MST 2002 email@example.com:/usr/obj/usr2/src/usr.sbin/named
Mar 16 22:58:52 www named: Ready to answer queries.
If you can see those then BIND is now running inside your chrooted environment. Since we have now got everything running manually we need to make a few changes so that our chrooted BIND will start up on boot.
To configure BIND to start chrooted on boot, again, fire up your favorite editor and open /etc/rc.conf and then add the following lines:
named_flags="-c /named.conf -t /var/named -u bind -g bind"
syslogd_flags="-s -l /var/named/dev/log"
Making ndc Work Correctly
So we're done now, right? Well, no. Not quite. Anyone familiar with BIND will know about the named.reload, named.restart and ndc commands used for reloading zonefiles. The only problem is that the ndc program does not know that it needs to be using a different socket (by default, it looks for /var/run/ndc). One possible solution is as follows:
mv /usr/sbin/ndc /usr/sbin/realndc
Create a file called /usr/sbin/ndc and put the following into it:
exec /usr/sbin/realndc -c /var/named/var/run/ndc $1 $2 $3 $4 $5 $6 $7 $8 $9
And then make it executable,
chmod +x /usr/sbin/ndc
What this will do is move the "real" ndc file out of the way into a file called realndc. We then have a shell script called ndc which passed all of it's called arguments onto the real ndc program. This allows us to maintain compatibility with any program that wants to call ndc (i.e. named.reload or named.restart).
You will need to be aware that the next time you upgrade FreeBSD it will almost certainly overwrite your ndc shell-script with another copy of the "real" ndc program. Consequently you should probably keep a backup copy of your script so that after an upgrade you can copy the ndc program over top of the old realndc program (in case anything has changed) and then copy your ndc script over top of the ndc program. At the same time, you may need to update the ld-elf.so.4, libc.so.4 and named-xfer files in the chrooted environment if they have changed. You may even want to automate this process somewhat by creating a shell-script which will copy all of the relevant files to their chrooted homes which you can run after any upgrade. Again, remember to set all permissions correctly.
Now that you have BIND chrooted do not think you are immune to attack from this vector. Just because it is chrooted does not mean you are invulnerable. Security holes in BIND may well exist and it may be possible to exploit your machine through them if they are found. However, because of the chroot the amount of damage that can be done is potentially less. This method should be but one part of an active and consistent security policy on all machines you administer.
Thanks to darxpryte and trillion_ for proofreading this document and catching a few typos. Thanks to GhostRdr for providing the excellent resource that is DefCon1.org.