Skip to content

Implementation

Mkinitfs

Mkinitfs is the initramfs generator of Alpine Linux. Among the usual operations to setup zlevis, i.e. writing the encryption key of the ZFS root pool to the TPM after the creation of the ZFS root pool, enabling the zlevis-hook in mkinitfs.conf and installing zlevis on the host system, we require to make an adaptation to the initramfs-init script of mkinitfs.

Particularly, in the prepare_zfs_root() function we need to add the option zlevis decrypt "$_root_pool" | zfs load-key -L prompt "$_root_pool":

/usr/share/mkinitfs/initramfs-init
# Do some tasks to make sure mounting the ZFS pool is A-OK
prepare_zfs_root() {
    local _root_vol=${KOPT_root#ZFS=}
    local _root_pool=${_root_vol%%/*}

    # Force import if this has been imported on a different system previously.
    # Import normally otherwise
    if [ "$KOPT_zfs_force" = 1 ]; then
        zpool import -N -d /dev -f $_root_pool
    else
        zpool import -N -d /dev $_root_pool
    fi


    # Ask for encryption password
    if [ $(zpool list -H -o feature@encryption $_root_pool) = "active" ]; then
        local _encryption_root=$(zfs get -H -o value encryptionroot $_root_vol)
        if [ "$_encryption_root" != "-" ]; then
            zlevis decrypt "$_root_pool" | zfs load-key -L prompt "$_root_pool" || eval zfs load-key $_encryption_root
        fi
    fi
}

Retaining the fallback to eval zfs load-key with the || operator.

Furthermore, we need to configure the zlevis-hook by notifying mkinitfs which binaries and libraries to add into the initramfs:

/etc/mkinitfs/features.d/zlevis.files
/usr/bin/zlevis
/usr/bin/zlevis-decrypt
/usr/bin/tpm2*
/usr/bin/jose
/usr/lib/libtss2-tcti*

and to notify what kernel drivers are required:

/etc/mkinitfs/features.d/zlevis.modules
kernel/drivers/char/tpm*

Dracut

Dracut is the default initramfs generator of Gentoo Linux and Void Linux. Among the usual operations to setup zlevis, i.e. writing the encryption key of the ZFS root pool to the TPM after the creation of the ZFS root pool, enabling the zlevis module in /etc/dracut.conf.d and installing zlevis on the host system, we require to add the zlevis module in /usr/lib/dracut/modules.d.

Create the executable module setup of the zlevis-dracut module:

/usr/lib/dracut/modules.d/85zlevis/module-setup.sh
#!/bin/bash

# Depend on udev-rules and zfs
depends() {
  echo udev-rules zfs
  return 0
}

install() {
  # Install the appropriate binaries and libraries
  inst_multiple /usr/bin /usr/bin/zlevis /usr/bin/zlevis-decrypt /usr/bin/jose /usr/bin/tpm2*
  inst_multiple /usr/lib /usr/lib/libtss2-tcti*

  # Run the zlevis decryption hook before the 90zfs hook
  inst_hook pre-mount 85 "${moddir}/zlevis.sh"
  inst_simple "${moddir}/zlevis.sh" "/sbin/zlevis.sh"
}

and create the executable module script of the zlevis-dracut module:

/usr/lib/dracut/modules.d/85zlevis/zlevis.sh
#!/bin/sh

# Load the ZFS kernel module
modprobe zfs 2>/dev/null
udevadm settle

# Search for encrypted pool's by means of the cmdline root atribute
local _root_vol="${root}"
local _root_pool="${_root_vol%%/*}"

# Import the root pool
zpool import -N -d /dev $_root_pool

# If the pool is encrypted run `zlevis decrypt` to obtain the key stored in the TPM and load the key
if [ $(zpool list -H -o feature@encryption $_root_pool) = "active" ]; then
    local _encryption_root=$(zfs get -H -o value encryptionroot $_root_vol)
    if [ "$_encryption_root" != "-" ]; then
        zlevis decrypt $_root_pool | zfs load-key -L prompt "$_root_pool" || echo "Failed to unlock $_root_pool with TPM"
    fi
fi

Now dracut will recognise the zlevis module, and if enabled will be added to the initramfs to be executed before the 90zfs hook.