FreeBSD jails: ZFS inside
#freebsd #jail #vps
jail
and zfs
allow full delegation of dataset administration to a given jail! Now, don’t you wanna unleash the full power of zfs
from within?
Table of Contents
So, we’ve seen how to create a native jail using FreeBSD’s toolset, and we’ve fine-tuned a few of its settings, including mounting select directories from the host into the jail.
Is that really enough though? 🙃
ZFS inside
We want zfs
inside our jail, period!
But why?
Since we use a dedicated zfs dataset per jail, isn’t that enough? Well, dataset management (and anything disk-related) is handled on the host.
Practically speaking, this means that the root
user inside the jail cannot
alter dataset properties, nor create new ones.
Under specific circumstances, we may want to delegate dataset management & creation to the jail administrator.
How about a backup server scenario, for instance, where each user gets their own jail, and may want or need to manage their own encrypted datasets? 🤓
Let’s!
So, we’re going to delegate a test
dataset to a jail appropriately named
testjail
.
Host config
Using ZFS inside a jail requires a few additionnal jail properties in
/etc/jail.conf
or /etc/jail.conf.d/
. Let’s add the following lines to the
jail’s config:
# jail-specific zfs
allow.mount;
allow.mount.zfs;
enforce_statfs = "1";
Note: we need to set enforce_statfs
from 2
to 1
, in order to lift the
restriction preventing dataset creation within the jail (see bastille
docs).
Create…
On the host, let’s start by creating a dataset:
zfs create zstorage/test
Set…
To assign the test
dataset to any jail, we first have to enable the
jailed
flag on the specific dataset, from the host:
zfs set jailed="on" zstorage/test
Delegate!
This article assumes we have a jail called testjail
running and properly
configured. Now that we’ve set its jailed
property to on
, let’s push the
dataset into the jail (from the host):
zfs jail testjail zstorage/test
And voilà!
By the power of zfs
Let there be light
Once delegation is active, the assigned dataset(s) appear inside the jail with
a simple zfs list
:
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
zstorage 2.68T 19.0T 128K none
zstorage/test 762K 19.0T 139K none
It should not be mounted by default unless the dataset mountpoint
property
has been set, but this is a good opportunity to try and set dataset flags from
within the jail and see what’s up:
zfs set mountpoint="/srv/" zstorage/test
Success! zfs mount
shows zstorage/test
, how cool is that?
How far can we see?
Alongside the delegated test
dataset, we can see see the parent dataset
belonging to the host! Eww, does that mean we can mess with it? Let’s try!
# zfs set atime=on zstorage
cannot set property for 'zstorage': permission denied
Looks like the parent dataset is safe, and that its properties cannot be modified outside of the host. Hint: in addition to zfs-jail, zfs-zone might be involved.
Similarly, attempts at creating another child dataset directly under the host’s parent dataset from the jail also fail:
# zfs create zstorage/hacktheplanet
cannot create 'zstorage/hacktheplanet': permission denied
For the record, zstorage
may well have many other child datasets on the host
or within other jails, but their existence does not leak into our jail!
More autonomous children
What we can do, however, is create child datasets underneath the delegated dataset, from within the jail itself, possibly with encryption properties set outside of the host’s perimeter, as suggested earlier:
zfs create -o encryption="on" \
-o keyformat="passphrase" \
zstorage/test/child
Aw yeah!
Config consolidation
On the jail
To ensure datasets are mounted when the jail starts, we need to enable ZFS in
the jail’s /etc/rc.conf
:
sysrc zfs_enable="yes"
sysrc zfskeys_enable="yes"
Note: If you want to ensure the host cannot access encrypted datasets at rest, unmount them and remove their key from memory. Shutting down the jail doesn’t affect this (since the kernel is shared among all jails):
zfs umount zstorage/test/child
zfs unload-key zstorage/test/child
On the host
To detach a dataset from the jail from the host, the jail must still be running:
zfs unjail testjail zstorage/test
Now, we want these zfs jail
& zfs unjail
commands to be automatically
issued when starting or stopping the jail, so our settings survive a jail restart.
To do so, let’s add the following to the jail’s configuration on the host in
/etc/jail.conf
or /etc/jail.conf.d/
:
exec.created += '/sbin/zfs jail testjail zstorage/test';
exec.prestop += '/sbin/zfs unjail testjail zstorage/test';
There you go!