Making AlpineLinux load ZFS keys for all pools on startup


Problem statement

A while ago I posted about the secure boot with fully encrypted filesystem setup on Alpine Linux that’s ZFS based.

On some machines, though, I use more than one pool (to separate the NVME pool from the SATA SSD pool, as their performance characteristics are different).

Unfortunately, Alpine Linux does not load all the keys before attempting to mount any of the “secondary” (non-boot) pools, and thus they aren’t mounted automatically (as the key material is missing).

Solution

The solution is very straightforward, and in my first version very hacky: Simply run zfs load-key -a before the zfs-mount init script (but after zfs-import).

In other words, a one-liner turned into a 78-liner due to rather excessive ceremony:

The /etc/init.d/zfs-load-keys init script (click to expand)

#!/sbin/openrc-run
#
# zfs-load-keys    This script will load encryption keys for ZFS pools
#
# chkconfig:    2345 01 99
# description:  This script will perform "zfs load-key -a" during system boot.
# probe: true
#
### BEGIN INIT INFO
# Provides:          zfs-load-keys
# Required-Start:    zfs-import
# Required-Stop:     $local_fs mtab
# Default-Start:     S
# Default-Stop:      0 1 6
# X-Start-Before:    zfs-mount
# X-Stop-After:      zfs-mount
# Short-Description: Load ZFS encryption keys
# Description: Run the `zfs load-key -a` command.
### END INIT INFO
#

# Source the common init script
. /etc/zfs/zfs-functions

# ----------------------------------------------------

do_depend()
{
	before zfs-mount
	after zfs-import
	keyword -lxc -openvz -prefix -vserver
}

# Output the status and list of pools
do_status()
{
	check_module_loaded "zfs" || exit 0

	"$ZPOOL" status && echo "" && "$ZPOOL" list
}

do_start()
{
	zfs_action "Loading ZFS encryption key(s)" \
		"$ZFS" load-key -a
}

# ----------------------------------------------------

if [ ! -e /sbin/openrc-run ]
then
	case "$1" in
		start)
			do_start
			;;
		stop)
			# no-op
			;;
		status)
			do_status
			;;
		force-reload|condrestart|reload|restart)
			# no-op
			;;
		*)
			[ -n "$1" ] && echo "Error: Unknown command $1."
			echo "Usage: $0 {start|status}"
			exit 3
			;;
	esac

	exit $?
else
	# Create wrapper functions since Gentoo don't use the case part.
	depend() { do_depend; }
	start() { do_start; }
	status() { do_status; }
fi

Then all that’s needed is to install it properly:

chmod 755 /etc/init.d/zfs-load-keys
rc-update add zfs-load-keys sysinit

Et voilà; after rebooting all volumes get correctly auto-mounted.

Closing words

This script only works well when you use file:// as the keylocation; it doesn’t even bother to verify that and/or test the presence of the file.

In other words, it’s supremely fragile.

So you better make sure you don’t have any of the keylocations set to prompt (and also that the configured locations are accessible at boot time).

A good quick check would be:

$ zfs get keylocation | awk '$3 != "none" { print }'
NAME                  PROPERTY     VALUE                       SOURCE
nvmetank              keylocation  file:///crypto_keyfile.bin  local
ssdtank               keylocation  file:///crypto_keyfile.bin  local

Obviously upstreaming an expanded version of the init script to Alpine might be a good idea, but I’m not sure this is the right call in all contexts. Hence a post, not an upstream attempt.