Add zfskeys rc.d script for auto-loading encryption keys

ZFS in 13 supports encryption, but for the use case where keys are
available in plaintext on disk there is no mechanism for automatically
loading keys on startup.

This script will, by default, look for any dataset with encryption and
keylocation prefixed with file://. It will attempt to unlock, timing
out after 10 seconds for each dataset found.
User can optionally specify explicitly which datasets to attempt to
unlock.

Also supports (optionally by force) unmounting filesystems and unloading
associated keys.

Sponsored by:	Modirum
Differential Revision:	https://reviews.freebsd.org/D30015
This commit is contained in:
Eirik Øverby 2021-07-28 16:11:35 +00:00 committed by Allan Jude
parent fed248a6ac
commit 33ff39796f

119
libexec/rc/rc.d/zfskeys Executable file
View File

@ -0,0 +1,119 @@
#!/bin/sh
# PROVIDE: zfskeys
# REQUIRE: zpool
# BEFORE: zfs zvol
. /etc/rc.subr
name="zfskeys"
desc="Load dataset keys"
rcvar="zfskeys_enable"
extra_commands="status"
start_cmd="load_zfs_keys"
stop_cmd="unload_zfs_keys"
status_cmd="status_zfs_keys"
required_modules="zfs"
# Note that zfskeys_datasets must have any character found in IFS escaped.
# Forcibly unmounting/unloading only applies to filesystems; ignored for zvols.
: ${zfskeys_datasets:=''}
: ${zfskeys_timeout:=10}
: ${zfskeys_unload_force:='NO'}
encode_args()
{
shift && [ $# -gt 0 ] && printf "%s\0" "$@" | b64encode -r -
}
list_datasets()
{
if [ "$zfskeys_args" ]; then
echo "$zfskeys_args" | b64decode -r |
xargs -0 zfs get -H -s local -o value,name keylocation
elif [ ! "$zfskeys_datasets" ]; then
zfs get -H -t filesystem,volume -s local -o value,name keylocation
else
echo "$zfskeys_datasets" | xargs -n 1 zfs get -H -s local \
-o value,name keylocation
fi
}
unlock_fs()
{
local fs="$1"
local kl="$2"
local k="${kl##file://}"
if [ "$k" ] && [ -f "$k" ] && [ -s "$k" ] && [ -r "$k" ]; then
if [ "$(zfs get -Ho value keystatus "$fs")" = 'available' ]; then
echo "Key already loaded for $fs."
elif keytest=$(zfs load-key -n -L "$kl" "$fs" 2>&1); then
echo "Loading key for $fs from $kl.."
if ! keyload=$(timeout $zfskeys_timeout zfs load-key -L "$kl" "$fs" 2>&1) ; then
if [ $? -eq 124 ]; then
echo "Timed out loading key from $kl for $fs"
else
echo "Failed to load key from $kl for $fs:"
echo "$keyload"
fi
fi
else
echo "Could not verify key from $kl for $fs:"
echo "$keytest"
fi
else
echo "Key file $k not found, empty or unreadable. Skipping $fs.."
fi
}
lock_fs()
{
local fs=$1
if [ "$(zfs get -Ho value mounted "$fs")" = 'yes' ]; then
if checkyesno zfskeys_unload_force ; then
zfs unmount -f "$fs" && echo "Forcibly unmounted $fs."
else
zfs unmount "$fs" && echo "Unmounted $fs."
fi
fi
if [ "$?" -ne 0 ]; then
echo "Unmount failed for $fs"
elif [ "$(zfs get -Ho value keystatus "$fs")" = 'available' ]; then
zfs unload-key "$fs" && echo "Unloaded key for $fs."
else
echo "No key loaded for $fs."
fi
}
status_zfs_keys()
{
local IFS=$(printf "\t")
list_datasets | while read kl fs ; do
echo "$fs: $(zfs get -Ho value keystatus "$fs")"
done
}
load_zfs_keys()
{
local IFS=$(printf "\t")
list_datasets | while read kl fs ; do
unlock_fs "$fs" "$kl"
done
}
unload_zfs_keys()
{
local IFS=$(printf "\t")
list_datasets | while read kl fs ; do
lock_fs "$fs"
done
}
zfskeys_args=$(encode_args "$@")
load_rc_config $name
run_rc_command "$1"