FreeBSD Jails

What is a Jail?

Those familiar with Java recognize the security concept of a sandbox. For those that aren't, it's the concept that everyone gets a unique, well-equipped sandbox to play in, and a person in
one sandbox isn't allowed into anyone else's sandbox, not even to share
anything with anyone else. On FreeBSD, jails implement this concept —
they keep processes in their own part of the system, denying access to anything
else. A jail requires its own dedicated IP address, though, which can make life
difficult for those with limited address space. If this presents you with a
hardship, consider at least using chroot. It
won't afford you as much security, but it does help.

How does this help security? Take, for example, a box with an external FTP
server and the company extranet. An exploit for the server is discovered, and a
cracker manages to gain root access through the FTP daemon. If the FTP server
is not run in a sandbox or jail, the cracker will have access to everything on
the machine, including sensitive information destined for the company's
partners through the extranet. If, however, the FTP server is run in a
jail, the cracker will only have access to the FTP files.

There are, of course, still potential risks. If you run at secure level
0, the cracker can simply access the raw disk device and read data
from there. The solution is obvious — on a box sensitive enough to require
jails, use appropriate secure levels as well. This will eliminate a cracker's
ability to read from or write to raw disk devices.

Configuring a Jail

Configuring a jail is pleasantly simple.

First, ensure that your system environment is jail-friendly. Because each
jail requires its own IP address, the services on your box must be configured
to listen to specific addresses, not just every available address. For
example, if the box's addresses are 199.232.41.26 (main) and
199.232.41.27 (jail), to get inetd to listen only on
199.232.41.26, add inetd_flags="-wW -a 199.232.41.26" to /etc/rc.conf. If you fail to do this, conflicts may occur over the aliased IP address.

For some daemons, this is not an easy process — sendmail
and rpcbind are two examples. If you're using these services on
your box, you might consider simply running them inside of a jail of their own.
After configuring all of the non-jailed daemons to listen to a specific address,
reboot the machine. This will put everything into a known state, eliminating
any potential for confusion.

With the proper host environment in place, create the directory that will
house the jail. In this example, it's /usr/jail/ftp. Now go to
/usr/src and run:

These commands build the jail and populate it with all of the tools that your
processes will need to run. Actually, they put in a lot more than just what your
processes will need. For example, perl, gcc, and
sendmail will all be installed, but you probably don't need them
in your jail. Keep in mind, though, that it's a lot easier to take stuff out
until something breaks than it is to put stuff back in until everything
works.

To configure the jail environment, you might want to copy
/stand/sysinstall into /usr/jail/ftp/stand, to
provide you with an easy configuration interface. I'll show you how to use it
in just a second.

With the system rebooted, you're now ready to configure the jail
environment. Start the jail for the first time by running:

# jail /usr/jail/ftp jail.hostname.com 199.232.41.27 /bin/sh

This will put you at a shell prompt in your jail environment. From here, you
can run /stand/sysinstall (literally, since / now
refers to the jail's root directory, not the system's.)

There are several configuration tasks to perform, such as setting the root
password (don't make it the same as the main system root password!), adding
user accounts, and configuring /etc/resolv.conf. Read man 8
jail for more configuration tasks that you'll need to perform. Keep in
mind that you want to be able to log in to the environment, so consider running
an SSH daemon inside the jail.

Once you're done configuring the jail environment, exit the shell and the
jail will be shut down.

You're almost ready to start the jail "for real." First, add the appropriate
IP address alias. For our example, this is done via:

ifconfig fxp0 inet alias 199.232.41.27 255.255.255.255

You can configure this in /etc/rc.conf to be done automatically
at boot.

You'll see some warning messages scroll by, but don't worry about them. You
can now see all the daemons running inside the jail, as indicated by the
J flag shown in the ps output. If you enabled SSH
within the jail, you can ssh to the jail environment.

Since normal shutdown commands like halt don't work in the
jail, you must take a special measure to shut the jail down. First, log in to
the environment and become root. You can then kill all the
processes inside the jail via kill -TERM -1 or kill t -KILL
-1. You can also do this from outside the jail by manually killing any
PID within the jail.

You should now be able comfortably to create and use jails to secure
everything from FTP daemons to DNS servers. Before we finish, there's just one
more note.

The security.jail.set_hostname_allowed (or jail.set_hostname_allowed on pre-5.0 machines) sysctl variable determines whether or not the superuser
within the jail can set the hostname. This is enabled by default, and you might consider disabling it by placing
security.jail.set_hostname_allowed=0 in /usr/jail/ftp/etc/sysctl.conf. Remember that it's
jail.set_hostname_allowed=0 on machines running FreeBSD-4.x!

Conclusion

You've now seen both how jails work and how to set one up. Whether you're
running multiple public services from the same box, providing login shells to
untrusted users, or offering a public service on an otherwise private machine,
using jails properly will help you sleep at night.

Mike DeGraw-Bertsch
is a security and Unix system administration
consultant in the Boston, Mass. area. When he's not at a job, writing,
hacking with Perl, or playing with his wireless network, he can usually be
found playing goal in ice hockey.