freebsdzine.org
Most people wouldn't know music if it came up and bit them on the ass. -- Frank Zappa

[ Home  · Latest BSD News  · Site Statistics  · Wanted Articles  · Request an Article  · Submit an Article ]

Search freebsdzine.org

FreeBSD 'zine Polls

How much wood could a woodchuck chuck if a woodchuck could chuck wood?

a little
a lot
4
what?
isn`t that a beaver?
pi
eleventeen
moses

Results  · More polls


Sections
· Wanted articles
· Request an article
· Contribute
· Mailing lists
· About the site
· The staff
· Copyright info
· Privacy policy
· Change log
· Contact us

Resources
· The FreeBSD Project
· The FreeBSD Diary
· BSD Today
· Daemon News
· Daily Daemon News
· Slashdot BSD
· FreshPorts
· The FreeBSD Mall
· BSDVault
· The FreeBSD Browser
· GreasyDaemon.com
· iso-nix.com

FreeBSD Books
· Complete FreeBSD
· FreeBSD Handbook
· FreeBSD Corporate
Networker's Guide


Runs on FreeBSD
Running BIND in a Jail
Peter McGarvey <[email protected]>

For a while now I've had a daemon watching all IP connections to my box. Over the past couple of weeks I've seen a marked increase in the number of people querying my name server - even though it just runs as a cache, and doesn't listen on a public interface. I can only assume the world is full of kind-hearted individuals scanning the entire net in an effort to help over worked sys-admins get pro-active about security. I'm not too worried about this. Luckily I work with a guy who can upgrade a DNS in a fraction of the time it takes me.

Now, I've got this box that I want to run BIND. I could grab the latest sources and install them as normal. I'm sure I would be fine. But for a while now I've wanted to setup a BIND nameserver in a chroot jail. No particular reason. I suppose I always just thought it would be fun. I feel I run acceptable level of risk, running named as a non-privileged user, and upgrading swiftly whenever the need arises (usually a quick CVS session followed by a make world). So, yeah, it's just for fun.

A while back I'd administed a NetBSD box running named chroot'd, so I knew a little about what was involved. A quick browse of the BIND sources in the source tree and I found this:

Chroot

-t followed by a directory path on the named(8) command line will cause the server to chroot() to that directory before it starts loading the configuration file.

Setting up a chrooted area varies somewhat by operating system. Some experimentation may be necessary. Here are some hints:

Don't forget to install named-xfer(8).

Either don't use shared libraries when you build, or do whatever is required on your OS to allow shared libraries to be used after a chroot().

syslog() is often troublesome after chrooting. Use the "logging" statement and log to a file instead.

/dev/null should be in the chroot directory hierarchy. You can usually find out the mknod parameters for a null device by looking in /dev/MAKEDEV.

You'll have to edit ndc(8) to get it to start the server with the appropriate flags, and to use the right pid file.

Note: this feature is still experimental.

Which I actually find one the most amazingly concise, yet useful, piece of text I've ever found in any INSTALL file. Not exactly precise instructions I'll admit, but enough to set my feet firmly on the path. I still needed to do some experimentation, so I created the directory /usr/chroot to give me somewhere to work, unzipped fresh copy of the BIND sources, and started to play.

Right, so I needed to work out how to compile a static version of named(8). I also needed to know where BIND actually put all it's files. So what I really needed to know was how to manipulate the source to get it to behave as I desired. By actually reading the READMEs I discovered the answers I was looking for. Specifically, the following lines in the src/INSTALL file:

The following variables can be used to change where things get installed:
DESTDIR prefix used in front of all other DEST variables. The default is the empty prefix. (for non-root installs; not equivalent to autoconf's --prefix)
DESTLIB libraries
DESTINC include files
DESTBIN ordinary binaries (e.g., dig, nslookup)
DESTSBIN system binaries (e.g., named)
DESTEXEC helper binaries (e.g., named-xfer)
DESTHELP place to put nslookup's help file
DESTMAN man file location
DESTETC configuration file
DESTRUN PID file location and "ndc" control channel location. This cannot be the same directory as DESTSBIN.

These variables should be specified in the Makefile.set for your port (e.g., if you use Solaris, in src/port/solaris/Makefile.set).

So the file I wanted was src/port/freebsd/Makefile.set. Here is what the original looked like:

'CC=cc'
'CDEBUG=-O2 -g'
'DESTBIN=/usr/bin'
'DESTSBIN=/usr/sbin'
'DESTEXEC=/usr/libexec'
'DESTMAN=/usr/share/man'
'DESTHELP=/usr/share/misc'
'DESTETC=/etc'
'DESTRUN=/var/run'
'LEX=lex -I'
'YACC=yacc -d'
'SYSLIBS=-ll -lutil'
'INSTALL=install'
'MANDIR=cat'
'MANROFF=(tbl|nroff -man)'
'CATEXT=0'
'PS=ps'
'RANLIB=ranlib'

A quick edit of this file enabled me to test compile BIND to run in /tmp/named, and to do a static compile. At this stage, I did plenty of fact-finding. I'll spare you the full details because it's mostly boring. Besides, this article is supposed to detail "how to do it" without the tedious messing about that I was forced to do.

I realized several things quite early on in my trials:

  1. The only thing that should go into the jail was named(8) and named-xfer(8). As far as these two were concerned they would happily run inside my chroot jail with absolutely no changes to the build configuration. The only thing I had to get right was the directory structure.

  2. Everything else (namely dig(1), host(1), and nslookup(8)) would happily run in the standard locations.

  3. The only thing that needed to run outside the jail, but need to know all about inside the jail, was ndc(8). I knew this would be a massive headache, so I left this until last.

  4. It would be a much easier to get BIND running outside the jail first.

So, having played and thought about it, I wrote down my plan of action:

  1. Download and extract the latest BIND sources.

  2. Do a static compile and install.

  3. Get named(8) running normally.

  4. Build the chroot hierarchy, and populate it.

  5. Force syslogd(8) to work with named(8).

  6. Get named(8) running chrooted and set it to run at boot time.

  7. Stop make world from clobbering it.

  8. Fix ndc(8).

  9. Tidy up.

So to business, here is how you get bind-8.2.3 running on a box running FreeBSD 4.2-STABLE.

Note: From here on in everything gets done as root. So take care not to break things.

Download

I used wget to download the sources. You may not have wget installed, but I'm sure you can work out how to download one file. Here is what I did (using fetch(1) instead of wget will also work):

# wget ftp://ftp.isc.org/isc/bind/src/8.2.3/bind-src.tar.gz

And once this had finished:

# tar xvzf bind-src.tar.gz

This creates the directory src, and in this directory you will find all things BIND. BIND documention comes in a seperate tarball so if you want that too you have to work it out for yourself.

The Static Compile

This is easy. Edit the file src/port/freebsd/Makefile.set and change the line:

'CDEBUG=-O2 -g'

to:

'CDEBUG=-O2 -g -static'

Now, change into the src directory and run these commands one at a time:

# make clean
# make depend
# make
# make install

This will compile and install a static copy of the BIND distribution.

Get named running normally

The base FreeBSD install includes BIND. So most of the files you will need are to be found in /etc/namedb.

You will need to generate the file localhost.rev. This is simple:

# cd /etc/namedb
# sh make-localhost

You will also need to create your named.conf file. The installed named expects to find it's configuration in /etc/namedb. The newly compiled named expects to find it in /etc. You could copy (or link) /etc/namedb/named.conf to /etc/named.conf, however, the included file is a sample and it will need editing so it is actually much easier to create one from scratch. Here is a simple config that will allow named to run a simple caching only nameserver.

options {
    directory "/etc/namedb";
    query-source address * port 1024;
};

zone "." {
    type hint;
    file "named.root";
};

zone "0.0.127.IN-ADDR.ARPA" {
    type master;
    file "localhost.rev";
};

This is fine for testing, and is easily expanded later.

You can now start named(8). However, I like named to log via syslog. So, before starting named add this to the end of /etc/syslog.conf:

!named
*.*        /var/log/named.log

Then run these commands:

# touch /var/log/named.log
# killall -HUP syslogd

Now start named(8):

# /usr/sbin/named -u bind -g bind

The -u bind -g bind tells named to run as user bind and group bind. You don't have to create this user and group, they come as part of the base install.

Use dig(1) to verify that everything is working:

# dig www.freebsdzine.org @localhost

Now kill named(8):

# ndc stop
Build & Populate the Chroot Hierarchy

I've chosen to make my "chroot root" /usr/chroot/named. Put your "root" anywhere you like, but bear in mind all instructions refer to my location.

Now it's time to make the directories that are needed. Do the following:

# mkdir /usr/chroot/named
# cd /usr/chroot/named
# mkdir dev etc usr usr/libexec usr/sbin var var/run

Now move named(8), named-xfer(8), and your configuration files into their new homes:

# mv /usr/sbin/named usr/sbin/
# mv /usr/libexec/named-xfer usr/libexec/
# mv /etc/named.conf etc/
# mv /etc/namedb etc/

Additionally, in order to run, named(8) requires /dev/null to exist within in the chroot directory hierarchy. I discovered how to do this by reading /dev/MAKEDEV.

Here is what you need to do:

# cd dev
# mknod null c 2 2
# chmod 0666 null
Named & Syslog

The original helpful advice from the BIND source stated:

syslog() is often troublesome after chrooting. Use the "logging" statement and log to a file instead.

If you were to pay attention to this then you can simply fire up named(8) now. It'll work - it just won't use syslog. However getting syslog and named(8) to work together is not actually that hard. All you need to do is get syslogd(8) to create an additional log socket somewhre that named can find it. The syslogd(8) man page states that the -l option can be used to:

Specify a location where syslogd should place an additional log socket. Up to 19 additional logging sockets can be specified. The primary use for this is to place additional log sockets in /var/run/log of various chroot filespaces.

So according to this man page fragment, running syslogd(8) like this:

# syslogd -l /usr/chroot/named/var/run/log

The above will create an additional UNIX domain socket where named can find it, and thereby allow logging via syslog. Alas, whereas this is correct, it's only half correct. The syslog functions uses /dev/log which is a symlink to /var/run/log. I've no idea why it's done like this, but on the assumption it's done like this for a good reason...

# killall syslogd
# syslogd -l /usr/chroot/named/var/run/log
# cd /usr/chroot/named/dev
# ln -fs /var/run/log

Note how the symlink will actually work both inside and outside the chroot jail.

This will allow syslog to listen to named(8), but the timestamps will be wrong. In order to get time working correctly you have to:

# cp /etc/localtime /usr/chroot/named/etc/

You also have to ensure syslog starts with the correct options at boot time. Simply edit /etc/rc.conf and add the line:

syslog_flags="-l /usr/chroot/named/var/run/log"
Starting named

Starting your chrooted named(8) is now easy. Just enter this command:

# /usr/chroot/named/usr/sbin/named -u bind -g bind -t /usr/chroot/named/

Starting named(8) automatically on boot is easy too. Simply edit /etc/rc.conf and add the following lines:

named_enable="YES"
named_program="/usr/chroot/named/sbin/named"
named_flags="-u bind -g bind -t /usr/chroot/named"
Stop make world from clobbering it

As you are no doubt aware BIND is included with FreeBSD, and it's also included in the source tree. So doing a make world will install a new copy of named(8), and likely clobber your carefully chrooted copy. To stop this from happening edit /etc/make.conf and put the following line in it:

NO_BIND=	true
Fix ndc

Working out how to get ndc(8) to work with a chrooted named(8) was fairly hard. This was mainly because I was working with a clean copy of the sources. I then discovered that the make depend stage of the compilation process created an include file called pathnames.h. And it is this file that requires mangling.

Change directories to your named sources, then:

# cd bin/ndc

Now open pathnames.h into your favorite editor. There are many definitions in this file, but only 3 of these are used in the ndc(8) source:

_PATH_NAMED
_PATH_PIDFILE
_PATH_NDCSOCK

Each of these needs to be edited to point to inside your chroot root. You need to change the following:

#define _PATH_NDCSOCK	"/var/run/ndc"

should be changed to:

#define _PATH_NDCSOCK	"/usr/chroot/named/var/run/ndc"

And change:

#define _PATH_PIDFILE	"/var/run/named.pid"

to:

#define _PATH_PIDFILE	"/usr/chroot/named/var/run/named.pid"

Simply adding the path to the _PATH_NAMED definition, however, is not sufficient. When you run ndc start and ndc restart, you need to ensure named(8) is passed the correct options to cause it to run as the correct user, in the correct group, and to chroot to the correct directory. Hence this definition:

#define _PATH_NAMED	"/usr/sbin/named"

needs to be changed to:

#define _PATH_NAMED	"/usr/chroot/named/usr/sbin/named -u bind -g bind \
  -t /usr/chroot/named/"

Once you have made these changes compile and install your modified ndc(8):

# make
# install -s -c -m 755 ndc /usr/sbin/ndc

Note: Running "make install" will install ndc(8) in /usr/local/sbin which is not where we want it.

Now check your log file and restart named(8):

In one terminal do:

# tail -f /var/log/named.log

And in another do:

# ndc restart

If you've done things right, you should see messages including:

chrooted to /usr/chroot/named/
group = bind
user = bind
Ready to answer queries.

So now ndc works. But, you still have to be root to run it. A little trick I use is this:

# chmod o-x,u+s /usr/sbin/ndc

Which will allow anyone in the "wheel" group to run ndc(8) as root. However, this does have security implications so only do this if you are prepared to accept the concequences.

Finishing touches

Okay, named is installed and running in it's chroot jail. It starts up at boot time, and you can control it with ndc(8). However, there are a couple of things I feel still need to be done.

Personally I'd put a note in /etc/motd. I mainly do this because I've got a problem with my short-term memory, but it also helps others who may have to do some DNS admin.

Also, I'd place a couple of symlinks in /etc:

# cd /etc
# ln -s /usr/chroot/named/etc/named.conf
# ln -s /usr/chroot/named/etc/namedb

Because even though I've left a message, most people won't read it (after all I know I won't). So putting a link where everyone expects to find named config files makes the chrooting almost transparent.

And you really should add something like this in /etc/newsyslog.conf:

/var/log/named.log      644  3     *    $W0D23 Z

If you don't, /var is going to fill up one day.

Oh yes, if you plan to provide secondary DNS, the slave directory needs to be writeable by the bind user:

# mkdir etc/named/s
# chown bind:bind etc/named/s

Finally, delete your sources if you feel you don't need them any more.

Oh yes, one more thing. If you've got named(8) running on your box, it makes sense to have your resolver use it. So edit /etc/resolv.conf, comment out all the existing nameserver entries and add the line:

nameserver      127.0.0.1

Once you've done all this, sit down, put your feet up and have a well earned cup of tea. And, if you can think of something I've forgotten, please let me know.

- Peter

Return to Issue 1, May 2001



Issues
2001
· May #1
· April
· March
· February
· January

2000
· December
· August
· July
· June
· May
· April
· March
· February

1999
· January

Other issues from 1999 are available in the attic for now.

Other News
· Slashdot
· FreeBSD Diary
· BSD Today
· FreshPorts
· Daemon News
· OS Online
· Rootprompt
· Maximum BSD

Miscellaneous
· Jim's site

IRC
#freebsdzine
If you'd like to hang out with us and talk about the site, join us in #freebsdzine on Undernet.

Backend
You can add a list of our latest issue's articles to your site by using our RDF/RSS file. You can also add it to your My Netscape page, or add our slashbox once you log in over at Slashdot.

[ Home  · Latest BSD News  · Site Statistics  · Wanted Articles  · Request an Article  · Submit an Article ]

Copyright © 1998-2001 · The FreeBSD 'zine · All rights reserved.