Just like different flats in a building, each of them having their own kitchen, bathroom and living room with their very own furniture, the FreeBSD jail mechanism allows administrators to partition the base system into several independent mini-systems called jails.
The need for Jail came from service providers willing to establish a clean cut between their services and those of their customers, mainly for security and ease of administration reasons. Instead of adding a new layer of fine-grained configuration options, the solution adopted was to compartment the system, both its files and its resources, in a way they can only be accessed by the right compartment.
FreeBSD jails mainly aim at three goals :
- Virtualization : Each jail is a virtual machine (a flat) running on the host machine (the building) with its own files processes, user and superuser accounts etc. (the furniture). From within a jailed process, the environment is (almost) indistinguishable from a real system.
- Security : Each jail is sealed from the others thus providing an additional level of security. In other words, if someone breaks into one flat, he still has to break into the others in order to gain control over the entire building.
- Easy delegation : Thanks to the limited scope of a jail, it allows administrators to painlessly delegate several tasks which require superuser access (the key to one flat) without handing out complete control over the system (the keys to all flats).
Those familiar with UNIX will recognize the chroot jail method of restricting the scope of processes. The FreeBSD jail mechanism is more than that : each process is attached a specific kernel structure which purpose is to limit its interaction with processes running in other jails and restrict the things they can do (for instance a jail is bound to only one IP address and can't access raw, divert or routing sockets).
The jail(8) utility and jail(2) system call first appeared in FreeBSD 4.0. New utilities (for example jls(8) to list jails) and system calls (for example jail_attach(2) to attach a new process to a jail) that render jail management much easier have been added in FreeBSD 5.1.
With Jail it is possible to create various different virtual machines, each of them having their own set of utilities installed and their own configuration. This makes it a safe way to try out software. For example it is possible to run different versions or try different configurations of Apache in different jails. And since the jail is limited to a narrow scope, the effects of a misconfiguration or mistake (even if done by the superuser) does not jeopardize the rest of the system's integrity. There isn't even anything to do to "revert changes" since nothing has actually been modified outside of the jail (deleting the jail's directory tree is a good way to get rid of it).
Virtualization is valuable to service providers wishing to offer their users the ability to have custom configurations and yet keep the overall system easy to maintain. For example two different customers could need different versions of the same software, say PHP...
The traditional way of configuring the server is to install both versions in different directories and make sure that they do not encroach on each other. Needless to say, this configuration isn't easy to maintain and rapidly tends to become messy, specially if the software hasn't been designed with this in mind (eg. XFree86 is notoriously hard to move around). The approach with jails is that every piece of software feels at home because it thinks it has the whole system for itself. It is even possible to allow the customer to do the configuration, or even the installation of new software himself by providing him access to the jail's root account.
However, the FreeBSD jail doesn't achieve true virtualization because it doesn't allow the virtual machines to run different kernel versions than that of the base system. It is possible to use jails to safely test new software, but not new kernels. Linux implements a method called User Mode Linux (UML) which allows one system to run different kernels in different virtual machines but it is way harder to configure and slightly less efficient than its BSD counterpart.
FreeBSD jails are an effective way to increase the security of a server because of the separation between the jailed environment and the rest of the system (the other jails and the base system). This creates what is called a sandbox.
For example suppose that a box is running an Apache web server as user www and that the admin responsible for its contents introduces a security breach, say a PHP include vulnerability. With the traditional scheme, this would compromise the entire system : the attacker would have the rights of the user www which can typically modify files on the web server, wander about in the directory tree and get lots of information, such as the full user list, shell and home dir from /etc/passwd. If the box is serving other purposes than hosting a web server, this can be of a great concern.
However, provided that the web server is jailed, the scope of user www is limited to the jail, which can in turn be minimalist enough not to give away unnecessary information. For example, besides the compulsory basic accounts, only www and perhaps ftp (to update the web server's contents) would be added as users. So the attacker could only mess around with the web server, even though he finds a way to gain superuser access.
If the superuser account of a FreeBSD jail is compromised, the attacker still can't access the entire system because jails are limited in the following ways :
- Jailed processes can't interact with processes in a different jail. For example, the ps command will only show the processes running in the jail.
- Modifying the running kernel by direct access and loading modules is prohibited. Modifying most sysctls and the securelevel is prohibited.
- Modifying the network configuration (interfaces, addresses and routing table) is prohibited. Accessing raw, divert and routing sockets is prohibited. For example a jail is bound to only one IP address and firewall rules can't be changed.
- Mounting and unmounting file systems is prohibited. Jails cannot access files below their root directory (ie. a jail is chrooted).
- Creating device nodes is prohibited.
The problem with UNIX rights management is that users are either regular users with limited privileges, or superusers with all privileges. There is nothing in between. It is therefore difficult to establish fine-grained rights management. Firstly, it is hard to give regular users only part of the superuser's rights because no matter what is done, at one stage the user gets (for a limited duration) full privileges. Secondly, tasks that seem unconnected can in fact be very dependent on each other. For example allowing a user to modify every file enables him to su as any user because he can change the logins.
With jails it is possible to install different daemons in different jails and delegate their administration to other people by giving them access to the superuser account. It is safe for two reasons : the jailed superuser has limited privileges (so you know for example that he can't modify firewall rules) and he can't escape the jail (he can't get any information about the base system).
The FreeBSD jail
Setting up a FreeBSD jail is simple. The steps are roughly the following :
- Create a directory tree, say /usr/jail, that will act as the root
(ie. /) of the jail.
- Then, populate this directory with the vital parts of a complete FreeBSD system (typically a shell in /usr/jail/bin, a mounted procfs in /usr/jail/proc and those applications you wish to run in the jail in /usr/jail/usr/...). As the man page jail(8) explains, it is possible to do this with "make world DESTDIR=xxx" followed by "make distribution DESTDIR=xx" however a complete world is not compulsory to setting up a jailed environment.
- Mount a devfs in the /usr/jail/dev directory. It is possible ti run jails without device nodes but il will be severely limited.
- Finally, "virtually" boot the system with jail by calling its /etc/rc init script with sh. (eg. "
jail /usr/jail hostname 10.0.0.22 /bin/sh /etc/rc")
This will spawn
es that are executed in the context
of the jail and cannot interact with anything else than other process
es of the same jail or access anything in the file system
below the jail's root (ie. /usr/jail).
The jail(8) man page is a good source of information to start with jails.
The FreeBSD jail is a very powerful and interesting feature. The partitioning approach taken is a clean answer to the gaps left by chroot. Adding fine-grained rights management options would probably have rendered maintenance a nightmare by causing more trouble than it would have solved. Virtual machines is a simple and efficient way to add features to existing software without changing one line of them: from a jailed process' point of view, there is nothing different.
Jail is a successful addition because it costed only minor changes to the FreeBSD kernel and proved to be very powerful and expressive. For the record : the implementation of the jail facility added approx. 200 lines of code in total, distributed around approx. 50 source files and about 200 lines in two new kernel files. - Jails: confining the omnipotent root.
http://phk.freebsd.dk/pubs/sane2000-jail.pdf - Jails: Confining the omnipotent root
http://www.freebsd.org/cgi/man.cgi?query=jail&format=html - jail(8) man page
http://www.onlamp.com/pub/a/bsd/2003/09/04/jails.html - FreeBSD Jails at ONLamp