What are Jails?

Jails were developed as a tool for system administrators to enhance the security of a FreeBSD system. Originally introduced in FreeBSD 4.0, jails continue to be an integral part of the development and progression of the FreeBSD operating system.

Jails were created to expand upon the chroot(2) concept, which is used to change the root directory of a set of processes. Jails create a safe environment independent from the rest of the system. Processes created in this environment cannot access files or resources outside of it. For this reason, compromising a service running in a jail will not compromise the entire system. Jails improved upon the chroot(2) concept by virtualizing access to the file system, users, and the networking subsystem.

A jail is characterized by four elements:

  • A directory subtree: the starting point from which a jail is entered. Once inside the jail, a process is not permitted to escape outside of this subtree.
  • A hostname: which will be used by the jail.
  • An IP address: which is assigned to the jail. The IP address of a jail is often an alias address for an existing network interface.
  • A command: the path name of an executable to run inside the jail.

While the theory is very simple and straightforward, it is important to note that creating a jail can quickly become extremely complex while leveraging systems and tools within the environment.

Note: Jails have their own set of users and their own root account which are limited to the jail environment. The root account of a jail is not allowed to perform operations to the system outside of the associated jail environment.

Examples of Jails

  • Jails are often used as test environments. Running a service or software in a jail allows developers to isolate and test updates or changes without jeopardizing the integrity of the rest of the system. This mitigates the risk of misconfiguration or mistakes. Developers also have the ability to run multiple instances or configurations at the same time without influencing each separate jailed process.

  • In larger, more complex systems, jails can be used to split the system into separate jailed processes. Each jail could contain it’s own utilities and configurations, allowing for a much more streamlined system for specific tasks. Extremely complex processes can also be isolated to a jail, separate from the main system.

  • A Jail can be used as a container for an old system, preserving critical infrastructure either as a backup, or as a way of referring to old code, all contained within a new system. This way, the old system can rely on the modern security and tools while being kept separate. This may be useful for developers who need to preserve a possibly insecure system without large changes needing to be made.

  • Jails are often used in place of a virtual machine, for either FreeBSD, or virtualization of another operating system such as Linux. Some developers looking to port tools and drivers to FreeBSD may find this useful.

Creating a Jail

Note: while the process creating a jail is quite simple, actual application and configuration requires a decent understanding of the FreeBSD operating system. This guide is aimed at people who are already familiar with the basics of the FreeBSD operating system.

Identify and create a directory for the jail. This is where the jail will physically reside within the file system of the jail’s host. A good choice can be /usr/jail/jailname, where jailname is the hostname identifying the jail. Usually, /usr/ has enough space for the jail file system, which for “complete” jails is, essentially, a replication of every file present in a default installation of the FreeBSD base system. In these following examples the directory will be /usr/jail/myjail.

The bsdinstall(8) tool can be used to fetch and install the binaries needed for a jail. Distributions will be installed into the destination directory along with some basic configuration of the jail:

# cd /usr

# mkdir myjail

# bsdinstall jail /usr/jail/myjail

bsdinstall will then start the FreeBSD installation process using the installer.

Once a jail is installed, it can be started by using the jail(8) utility. The 4 elements listed earlier in the guide (directory subtree, hostname, IP address, and command) will serve as mandatory arguments for the utility, but other arguments may be specified too. The command argument depends on the type of the jail employed. For example, if a system requires the startup sequence, such as in the case of a virtual machine, specifying /etc/rc.conf under the command parameter will be ideal.

The FreeBSD rc mechanism provides an easy way to start jails on boot.

Configure specific jail parameters in jail.conf:

myjail { 
    host.hostname = myjail;                        # Set the hostname
    ip4.addr = 192.168.0.10;                       # Set an IP address of the jail 
    path = "/usr/jail/myjail";                     # Path to the jail 
    devfs_ruleset = "5";                           # devfs ruleset 
    mount.devfs;                                   # Mount devfs inside the jail 
    exec.start = "/bin/sh /etc/rc";                # Start command 
    exec.stop = "/bin/sh /etc/rc.shutdown";        # Stop command 
} 

A common configuration can also be used. This configuration will be shared by all jails that are not specifically set up like in the previous example:

host.hostname = "$name";                           # Set the hostname using the $name variable
ip4 = inherit;                                     # Inherit IP address from the host
path = "~/jail.$name";                             # Path to the jail
devfs_ruleset = $name_ruleset";                    # devfs ruleset
mount.devfs;                                       # Mount devfs inside the jail
exec.start = "/bin/sh /etc/rc";                    # Start command 
exec.stop = "/bin/sh /etc/rc.shutdown";            # Stop command 

service(8) can be used to start or stop a jail by hand if an entry for it exists in jail.conf:

# service jail start myjail
# service jail stop myjail

More information about this can be found in the jail(8) manual page, including other arguments that can be set for the jail.

Further Configuration

Fine tuning of a jail’s configuration is mostly done by setting sysctl(8) variables. Here is a list of the main jail-related sysctls, complete with their default value. Please refer to the jail(8) and sysctl(8) manual pages for more information on each variable.

  • security.jail.set_hostname_allowed: 1
  • security.jail.socket_unixiproute_only: 1
  • security.jail.sysvipc_allowed: 0
  • security.jail.enforce_statfs: 2
  • security.jail.allow_raw_sockets: 0
  • security.jail.chflags_allowed: 0
  • security.jail.jailed: 0

These variables will need to be run by the host system system administrator and amend some limitations by default in the jail.

FreeBSD also contains tools for viewing information on active jails and executing commands within the jail itself. The jls(8) command can be used to list all active jails along with their identifier, hostname, path, and IP address. The jexec(8) command can attach to an active jail from the host system in order to run a command or perform administrative tasks. For example jexec(8) can be used to start a shell in an active jail with:

# jexec 1 sh

Updating/Removing a Jail

Jails should be kept as up to date from the host operating system as possible. To update the jail to the latest patch release, execute the following commands on the host:

# freebsd-update -b /usr/jail/myjail fetch
# freebsd-update -b /usr/jail/myjail install

To upgrade the jail to a new major or minor version, first upgrade the host system as described in “Performing Major and Minor Version Upgrades”. Once the host has been upgraded and rebooted, the jail can then be upgraded. For example to upgrade from 12.3-RELEASE to 13.0-RELEASE, on the host run:

# freebsd-update -b /usr/jail/myjail --currently-running 12.3-RELEASE -r 13.0-RELEASE upgrade
# freebsd-update -b /usr/jail/myjail install
# service jail restart myjail
# freebsd-update -b /usr/jail/myjail install

Then, if it was a major version upgrade, reinstall all installed packages and restart the jail again. This is required because the ABI version changes when upgrading between major versions of FreeBSD. From the host:

# pkg -j myjail upgrade -f
# service jail restart myjail

In order to remove a jail, simply remove the directory after making sure that the service has stopped:

# service jail stop myjail
# rm -rf myjail