In an older blog post I focused on creating FreeBSD base jails with VNET on ZFS. Today, I want to do the same thing with Thick jails, since I have some use cases where base jails don’t quite do what I want to do in terms of management.

Thick Jail?

A thick jail in creation is basically half the steps of a base jail. It is a jail with its own complete userspace, no nullfs mounts and the sort.

My reasons for wanting a thick jail is because I used base jails to have an easy to manage jail. freebsd-update the base they were built on, restart the jails, then I’m done. But to keep my jail versions pure and be able to delete older FreeBSD versions, I’ve migrated my jails from base to base. For most, this is not a problem. However, I have grown very tired of doing this with my Nextcloud jail. Now yeah yeah I could use Ansible to do it all for me but for this single jail, that’s a bit much.

The thick jail is has its own copy of the FreeBSD userland, which then means it uses more space the more thick jails you add. It also means I have to independently manage each jail. That makes it easier for me with Nextcloud, and some other single use jails where it’s probably easier to just treat each jail as a virtual machine, so to speak.

Creating the Jail

First off, we need to create the jail. I’m going to start by making a new jails dataset and mounting it at /jails

zfs create -o mountpoint=/jails zroot/jails

Here is the foundation for everything. Now I’ll create a few other datasets for our releases and templates and running jails, as well as our first release dataset (14.0-RELEASE)

zfs create -p zroot/jails/jails/myjailname

Next, we need to download the base OS as well as lib32 for our jail. The contents should be extracted into /jails/jails/myjailname in the end.

fetch https://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/14.0-RELEASE/base.txz -o /tmp/base.txz
fetch https://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/14.0-RELEASE/lib32.txz -o /tmp/lib32.txz
tar -xf /tmp/base.txz -C /jails/jails/myjailname
tar -xf /tmp/lib32.txz -C /jails/jails/myjailname

Now let us update the jail contents

env UNAME_r=14.0-RELEASE freebsd-update -b /jails/jails/myjailname fetch install
env UNAME_r=14.0-RELEASE freebsd-update -b /jails/jails/myjailname IDS

Then, we can copy our /etc/localtime and our /etc/resolv.conf files into the jail

cp /etc/localtime /jails/jails/myjailname/etc/localtime
cp /etc/resolv.conf /jails/jails/myjailname/etc/resolv.conf

Jail Configuration

Now that the jail is done, we need to configure it to start it. This is done by editing the /etc/jail.conf file. For this single jail, it should look something like this

$jails="/jails/jails";

exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.clean;
exec.system_user = "root";
exec.jail_user = "root";
mount.devfs;
vnet;
vnet="new";
allow.raw_sockets = 1;
devfs_ruleset="5";

myjailname {
        host.hostname = "myjailname";
        path = "$jails/myjailname";
        #securelevel="2";
        vnet.interface = "e0b_jeth";
        exec.prestart += "sh /usr/local/bin/jib addm jeth em0";
        exec.poststop += "/usr/local/bin/jib destroy jeth";
}
Note
em0 is my hosts interface and jeth is the name of the interface that jib expects

We will need to drop some basic configs in the jail soon such as an rc.conf and pf.conf file since this configuration is set up to run pf in each jail on its own.

Inside the Jail

Inside the jail, we need a working rc.conf and pf.conf. To start, my rc.conf looks like this

cron_flags="$cron_flags -J 15"

# Disable Sendmail by default
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"

# Run secure syslog
syslogd_flags="-c -ss"

hostname="myjailname"
# NOTE the usage of jeth here with /etc/jail.conf
ifconfig_e0b_jeth="192.168.1.100/24"
defaultrouter="192.168.10.1"

pf_enable="YES"

And the pf.conf file (definitely be sure to configure this properly, this is only an example)

block all
pass out

Host Configuration

For the host, we need to setup a few things:

  • Enable the service

  • Get jib in the right place

  • Configure our interface (optional)

Enable the Service

sysrc jail_enable="YES"
sysrc jail_list="myjailname"

The above enables the jail server and jail_list will autostart the jail when the service gets started and stopped when the service stops

Get jib in the Right Place

Either copy or symlink jib to /usr/local/bin

cp /usr/share/examples/jails/jib /usr/local/bin/jib

Wrapping It Up

The jail should be able to start now. Here is one way the jail can be started

service jail start myjailname

And now it is possible to jexec into it

jexec myjailname

Now you can do whatever you wish with this jail like any other jail.

UPDATES

  • 2024-01-15 - Updated release numbers to 14.0