Gentoo on ZFS encrypted root with EFI stub

June 8, 2020

ZFS file system is an interesting option for many Linux users and especially so for Gentoo users due to effortless snapshots and streamlined partition management. More recently added encrypted datasets also simplify file system encryption process, which applies with some caveats to a root mount encryption.

I don’t have a need for a conventional bootloader and always preferred to use EFI stub kernel with an EFI record. Setting up EFI stub loading for ZFS encrypted root require zfs kernel module to be available during the boot, this can be solved either by doing a static ZFS install or by generating initramfs. The later option is preferable in my opinion due to simpler kernel and ZFS upgrade process. You also need to make encryption key available during the boot, there are multiple options available for this including manually unlocking dataset by typing in a passphrase. I opted to store encryption key on a tiny USB flash drive.

This post will contain necessary steps to reproduce my setup. I will intentionally not cover the Gentoo installation process onto ZFS. There are multiple guides available for this and process is pretty straightforward. The following overview will guide you through those options:

The following text assumes that you have access to ZFS userland utilities and Gentoo’s base system.

Encryption key creation

As of now ZFS supports 3 key formats: passphrase, hex and raw. Depending on the key format key location can be prompt or absolute path to a key. The simplest option is to use passphrase that will be typed in via prompt on unlock, this is a bit tedious to manage since you have to manually unlock your datasets on boot. This could be improved by using hex or raw key located on another partition, but this will require some protection to prevent access to a key since it will be located next to an encrypted dataset. The better option will be to use removable media as a key that will be provided at the boot time as an absolute path to a raw device. This will also allow you to remove the drive to prevent unlocking.

First we have to prepare partition on an USB drive that will contain our key data. As of now maximum supported key size is 512 bytes, so a single 1 sector partition should suffice. We have to offset partition by 2048 bytes which are reserved for GPT’s partition table.

$ parted -s /dev/sdX mklabel gpt mkpart key 2048s 2048s

Next we will write raw key to this partition, we have to make sure to exclude newline symbols to prevent undesirable key truncation as described in this comment.

$ tr -d '\n' < /dev/urandom | dd of=/dev/disk/by-partlabel/key

This will be enough for ZFS to create and unlock encrypted datasets using this removable drive.

Setting up ZFS encrypted mounts

Process for setting up ZFS encrypted datasets is not much different from the usual routine, we just have to provide 3 additional options:

$ zfs create -o encryption=on \
             -o keyformat=passphrase \
             -o keylocation=file:///dev/disk/by-id/usb-FK310-_128M_Mobile-Disk-part1 \
             rpool/ROOT/gentoo

This command will enable aes-256-gcm encryption for the provided dataset with the passphare key format and a key which is addressed by the full absolute path to a raw device identified by it’s id. aes-256-gcm encryption algorithm corresponds to the AES block cypher with 256 bits key size and the GCM operation mode, this is a strong and performant cypher and overall a great default.

You can verify that encryption is applied to a dataset by doing:

$ zfs get encryption,keyformat,keylocation rpool/ROOT/gentoo
NAME               PROPERTY     VALUE
rpool/ROOT/gentoo  encryption   aes-256-gcm
rpool/ROOT/gentoo  keyformat    passphrase
rpool/ROOT/gentoo  keylocation  file:///dev/disk/by-id/usb-FK310-_128M_Mobile-Disk-part1

At this stage you can move your existing Gentoo installation to this encrypted dataset. The next step is to make this root mount visible during the boot process.

Creating an EFI record

This section will describe how to create an EFI record that will be able to boot your system using an encrypted root dataset. The following text assumes that you already have necessary kernel configs enabled for the EFI stub per this wiki page.

Since ZFS is most likely installed on your system as a kernel module it won’t be included into your EFI stub, to fix that we need to create an initramfs for our system with a ZFS module in it. There multiple ways to create initramfs, I will use dracut below as I found it easy to use and in most cases it will work without additional settings.

Each time you upgrade and recompile your kernel you will also need to create a new initramfs image using the following command:

$ dracut -H --kver X.Y.Z-gentoo

-H arguments stands for hostonly, i.e. create an image specifically for the local machine instead of generic. You should see zfs module in the dracut’s output and some help text about how to enable ZFS on boot.

The next step is to copy initramfs and EFI stub images to the /boot mount and create EFI record for it:

$ mv /boot/initramfs-X.Y.Z-gentoo.img /boot/efi/gentoo/initramfs-X.Y.Z.img
$ cp /usr/src/linux/arch/x86/boot/bzImage /boot/efi/gentoo/bzImage-X.Y.Z.efi
$ efibootmgr -c -d /dev/sdX -L "Gentoo X.Y.Z" \
             -l '\EFI\Gentoo\bzImage-X.Y.Z.efi' \
             -u 'initrd=\efi\gentoo\initramfs-X.Y.Z.img dozfs root=ZFS=rpool/ROOT/gentoo'

The above command will create a new EFI record with label Gentoo X.Y.Z for the EFI stub image we generated, we will also pass necessary arguments to the stub kernel so it would know where to find initramfs and how to activate ZFS for our root dataset.

At this stage you should be able to reboot and load back into the system from the encrypted /root mount. If something goes wrong you should be able to see problems in the initramfs output. In most cases I would recommend to double check that initramfs image has zfs module in it, your ZFS mounts point to the right place and key is available during the boot.

Back