Creating FreeBSD jails disregarding the manual

Computer virtualization has been around for quite some time as any mainframe guru will tell you. If you search around the Internet for information on this subject, you’ll find tons of references to different types of virtualization. VMware, Virtualbox, Qemu and Xen allow you run practically any OS, but are quite demanding on the hardware you’re running them on. Operating system-level virtualization is a more lightweight version that doesn’t allow you to run a different OS, but the same OS inside the OS. FreeBSD makes use of this type of virtualization since version 4.0, released in march 2000. The use of this type of virtualization is theoretically endless, but given typical uses of FreeBSD this is mostly used as a feature-rich ‘sandbox’ environment; an environment that allows you to host services that are completely disconnected from other services on the same machine. In FreeBSD-speak these are called ‘jails’. If you were to run a program in a jail, it would be constrained to that particular jail and not be able to break out. If you lookup the FreeBSD manual page for ‘jail’, you’ll find that you can create one in just 6 commands. There is a downside to that method, because it’ll start to build binaries for the entire OS from scratch. Depending on your hardware, that might take a while! Suppose you were to work in a very secure environment that doesn’t allow Internet access, the use of USB drives or compilers to be installed, then you’d think that setting up a jail would be impossible. Luckily, this isn’t the case since a jail is basically a copy of most of the OS inside another directory, you can simply copy those parts over to a jail – provided that you’ve used trusted media to install the OS in the first place, and nothing has changed ever since ofcourse. After that, simply setup the proper configuration in the jail and on the host, and you’re up and running. The script below does exactly that, within a fraction of time of a build world!

WARNING: DO NOT USE THIS WITHOUT PROPER BACKUPS. THIS MAY DESTROY YOUR MACHINE. USE AT OWN RISC.

#!/bin/sh
# Author: B.J. van Buuren
# License: BSD 3 clause (http://www.opensource.org/licenses/BSD-3-Clause)
# Globals
JAILNAME="${1:-epsilon39}"
JAILIF="${2:-lo0}"
JAILIP="${3:-172.16.251.139}"
JAILROOT="/usr/jails"

# Stop and REMOVE(!) any earlier incarnation of our jail
/etc/rc.d/jail stop ${JAILNAME} >/dev/null 2>&1
chflags -R noschg ${JAILROOT}/${JAILNAME} >/dev/null 2>&1
rm -rf ${JAILROOT}/${JAILNAME} >/dev/null 2>&1

# Setup and populate directory structures
mkdir -p ${JAILROOT}/${JAILNAME}
mtree -f /etc/mtree/BSD.root.dist -p ${JAILROOT}/${JAILNAME}/ -U >/dev/null
mtree -f /etc/mtree/BSD.usr.dist -p ${JAILROOT}/${JAILNAME}/usr -U >/dev/null
cat << EOF > ~/jail-setup
bin
sbin
etc
lib
libexec
usr/bin
usr/sbin
usr/share
usr/lib
usr/libexec
usr/libdata
EOF
tar -c -f - -C / -T ~/jail-setup | ( cd ${JAILROOT}/${JAILNAME} && tar xf - )
rm -f ~/jail-setup >/dev/null 2>&1

# Configure administrator access
echo 'sshd_enable="YES"' > ${JAILROOT}/${JAILNAME}/etc/rc.conf
echo 'sshd_flags="-o PermitRootLogin=yes"' >> ${JAILROOT}/${JAILNAME}/etc/rc.conf
echo '' > ${JAILROOT}/${JAILNAME}/etc/fstab
rm -f ${JAILROOT}/${JAILNAME}/etc/ssh/ssh_host* >/dev/null 2>&1

# Configure host system configuration for our jail
grep '^jail_enable="YES"' /etc/rc.conf >/dev/null 2>&1 || echo 'jail_enable="YES"' >> /etc/rc.conf
grep '^jail_list=' /etc/rc.conf >/dev/null 2>&1 || echo "jail_list=\"${JAILNAME}\"" >> /etc/rc.conf
sed -e "/^jail_list/s/${JAILNAME}//g" -e "/^jail_list/s/ *\"$/ ${JAILNAME}\"/" -e 's/" */"/' /etc/rc.conf > ~/rc.conf.new && cat ~/rc.conf.new > /etc/rc.conf
rm -f ~/rc.conf.new >/dev/null 2>&1
grep '^jail_devfs_enable="YES"' /etc/rc.conf >/dev/null 2>&1 || echo 'jail_devfs_enable="YES"' >> /etc/rc.conf
grep '^jail_interface=' /etc/rc.conf >/dev/null 2>&1 || echo "jail_interface=\"${JAILIF}\"" >> /etc/rc.conf
grep "^jail_${JAILNAME}_ip=" /etc/rc.conf >/dev/null 2>&1 || echo "jail_${JAILNAME}_ip=\"$JAILIP\"" >> /etc/rc.conf
grep "^jail_${JAILNAME}_hostname=" /etc/rc.conf >/dev/null 2>&1 || echo "jail_${JAILNAME}_hostname=\"${JAILNAME}\"" >> /etc/rc.conf
grep "^jail_${JAILNAME}_rootdir=" /etc/rc.conf >/dev/null 2>&1 || echo "jail_${JAILNAME}_rootdir=\"${JAILROOT}/${JAILNAME}\"" >> /etc/rc.conf

# Start our jail
/etc/rc.d/jail start ${JAILNAME}

How to use this? Copy and paste this into a file, say ‘jail_setup.sh’. Set execute-rights on that file (chmod +x jail_setup.sh). It takes 3 parameters:
1st: The (short) hostname of the jail. ‘ds301’ or ‘aura09’ for example.
2nd: The interface that will allow the jail to communicate with the network. If you were to use ‘lo0′ then you would need to setup IP-routing and/or NAT, but any will do. ’em0′ or so, but check ifconfig for that.
3rd: The IP-address to use on the interface.

Typical use may look like this:

./jail_setup.sh epsilon41 lo0 172.16.251.141
Configuring jails:.
Starting jails: epsilon41.

Type ‘jls’, and you should see the jail listed. Use ‘/etc/rc.d/jail stop epsilon41’ to stop the jail.

It is very easy to setup a whole bunch of them:

./jail_setup.sh epsilon42 lo0 172.16.251.142
Configuring jails:.
Starting jails: epsilon42
./jail_setup.sh epsilon43 lo0 172.16.251.143
Configuring jails:.
Starting jails: epsilon43.
./jail_setup.sh epsilon44 lo0 172.16.251.144
Configuring jails:.
Starting jails: epsilon44.

Now you can login to the guest with ‘ssh root@172.16.251.144’ using the same password as on the host and voila! A crispy fresh installed FreeBSD-OS for you.

Epilogue

A jail is typically around 180 Megabytes in size when using this script to set them up (Tested on FreeBSD 8.2-RELEASE). There are ways to reduce that footprint quite a bit, but that is beyond the scope of this article. It should be possible to use the freebsd-update command to update the binaries inside each jail, although I haven tested that (yet). Depending on how strict you handle precompiled binaries from the Internet, that might be difficult.

The script enables the secure shell daemon (sshd) in the jail so you can log in over the network to your jail as root. All users and passwords inside the jail are the same as on the host, since your host was copied to the jail. Both may be undesirable.

The name of the jail and its IP-address should be registered or configured with a local authoritive DNS and/or in the /etc/hosts-file of the host. Alternatively, you may wish to set UseDNS=”NO” in /etc/ssh/sshd_config in the jail. These aren’t handled by the script, which might cause a delay of 10 to 30 seconds when logging into the jail.

Beware that the script will first attempt to stop a jail by the name you have entered on the command line, and will remove its root-directory. The idea behind this was that you could easily reinstall a jail without too much hassle, the downside being removal of potential valuable data.

Most important: Disregarding the manual page and doing something like this is a hack. If you are not sure if this is such a good idea, then do not use this method. You could argue that FreeBSD should include jail-management tools to prevent hacking like this.

 

This entry was posted in FreeBSD and tagged . Bookmark the permalink.

1 Response to Creating FreeBSD jails disregarding the manual

  1. Jacco says:

    freebsd-update command works like a charm! Tested on 8.2-release.

Comments are closed.