Background
I recently built and configured a small home server, mostly for fun, but also because I hadn’t administered a non-production linux box for a while and kind of missed it. I hadn’t used ZFS before, but I’d heard good things so I figured what better time to try than now?
Wanting some kind of encryption, I originally tried following Ubuntu 18.04 Root on ZFS but I had a lot of trouble getting the luks prompt to appear after loading the boot partition. The ZFS file system worked fine, and could be imported and mounted successfully if I booted from a live USB and unlocked the luks partition first.
Eventually, I gave up and decided to try something different. I don’t really care if my root partition is unencrypted, I mostly care about my home partition. I’m not personally worried about someone entering my apartment and using physical access to install some malware to steal secrets, because if I’ve annoyed someone that much then there’s far more effective methods for getting my encryption key anyway. I’m more concerned about some SSH/API keys or code in my server’s home directory being out in the world if the box is stolen or disposed of without being properly wiped. For that, ZFS dataset-level encryption of individual mount points is more than sufficient.
Note: Ubuntu 19.10 (scheduled for GA release on October 10, 2019) will include ZFS 0.8.1. It may be easier more stable to do-release-upgrade
after this date than install ZFS from source. You’ve been warned.
Prerequisites
- An (ideally) bootable system with a partition for adding your new encrypted ZFS pool to (you cannot add encryption to an existing pool)
- An appetite for potential data loss. Seriously, take backups (not just ZFS snapshots) and don’t do this in production
Steps
Build
Being able to boot into the system you want to build ZFS for makes this easier. If this is you, then you can simply follow the steps outlined in Building ZFS in the ZFS on Linux GitHub repo to build the ZFS debs (make sure you do git checkout zfs-0.8.1
before building). Specifically, run the following:
# Install required packages
$ sudo apt install --yes build-essential autoconf automake libtool gawk alien fakeroot ksh
$ sudo apt install --yes zlib1g-dev uuid-dev libattr1-dev libblkid-dev libselinux-dev libudev-dev
$ sudo apt install --yes libacl1-dev libaio-dev libdevmapper-dev libssl-dev libelf-dev
$ sudo apt install --yes linux-headers-$(uname -r)
$ sudo apt install --yes python3 python3-dev python3-setuptools python3-cffi
# Clone the repo
$ git clone https://github.com/zfsonlinux/zfs
# Checkout version 0.8.x
$ git checkout zfs-0.8.1
# Build
$ cd zfs
$ sh autogen.sh
$ ./configure
$ make -s -j$(nproc)
# Make the necessary deb files
$ make deb
If you can’t boot into the system, you can still build the necessary ZFS debs by specifying the path to the target kernel on the when running ./configure
. On Ubuntu/Debian that should be something like (untested):
$ ./configure --with-linux{,-obj}=/usr/src/linux-headers-5.0.0-20/
After this is complete, you should have a list of debs that looks something like this:
~/src/zfs tags/zfs-0.8.1 tl@hotbox
➜ ls -1 *.deb
kmod-zfs-5.0.0-20-generic_0.8.1-1_amd64.deb
kmod-zfs-devel_0.8.1-1_amd64.deb
kmod-zfs-devel-5.0.0-20-generic_0.8.1-1_amd64.deb
libnvpair1_0.8.1-1_amd64.deb
libuutil1_0.8.1-1_amd64.deb
libzfs2_0.8.1-1_amd64.deb
libzfs2-devel_0.8.1-1_amd64.deb
libzpool2_0.8.1-1_amd64.deb
python3-pyzfs_0.8.1-1_amd64.deb
zfs_0.8.1-1_amd64.deb
zfs-dkms_0.8.1-1_amd64.deb
zfs-dracut_0.8.1-1_amd64.deb
zfs-initramfs_0.8.1-1_amd64.deb
zfs-test_0.8.1-1_amd64.deb
Install
Before you can install your freshly-built ZFS source, you need to uninstall the one you previously installed with apt. This can be safely done while the ZFS datasets are mounted and the system is running as the kernel module will remain loaded until you manually unload it or reboot.
$ sudo apt remove zfsutils-linux
Note: If you reboot after this but before installing the new ZFS packages (eg because of a power outage) you can install the built debs from a live USB by mounting and chrooting into your ZFS datasets.
The next step is to install the necessary debs from the ones created. The makefile builds a number of dev debs that are unnecessary for normal users. To install all the ones you need, do the following:
$ sudo dpkg -i \
zfs-initramfs_0.8.1-1_amd64.deb \
zfs_0.8.1-1_amd64.deb \
python3-pyzfs_0.8.1-1_amd64.deb \
libzpool2_0.8.1-1_amd64.deb \
libzfs2_0.8.1-1_amd64.deb \
libuutil1_0.8.1-1_amd64.deb \
libnvpair1_0.8.1-1_amd64.deb \
kmod-zfs-$(uname -r)_0.8.1-1_amd64.deb
Then reboot your system and you should be successfully running ZFS 0.8.1:
~ tl@hotbox
➜ zfs --version
zfs-0.8.1-1
zfs-kmod-0.8.1-1
Caveats
Kernel updates
When you update your kernel you will have to rebuild ZFS again. This is because it relies heavily on kernel modules, which must be built against a specific version. This may be painful to maintain.
System recovery
You will not be able to mount any encrypted datasets with pre-0.8.x ZFS. If you try, you’ll get an error something like this:
status: The pool uses the following feature(s) not supported on this system:
com.datto:encryption (Support for dataset level encryption)
action: The pool cannot be imported. Access the pool on a system that supports
the required feature(s), or recreate the pool from backup.
This can make it difficult to recover your system with a live USB if said recovery requires access to one of your encrypted datasets.
If you like, you can pin your kernel version to prevent it being upgraded automatically with the following one-liner (source) so that you control when you have to go through the upgrade process:
for i in $(dpkg -l "*$(uname -r)*" | grep kernel | awk '{print $2}'); do echo $i hold | dpkg --set-selections; done