diff options
author | Eirik Øverby <ltning-freebsd@anduin.net> | 2021-07-28 16:11:35 +0000 |
---|---|---|
committer | Allan Jude <allanjude@FreeBSD.org> | 2021-07-28 16:26:45 +0000 |
commit | 33ff39796ffe469a764e485ac49c31700a51fd6f (patch) | |
tree | a6449100a9943b82ec6447cd247a2f24e3388433 | |
parent | fed248a6acb33cf6d51880be8d5c774d89e75110 (diff) | |
download | src-33ff39796ffe.tar.gz src-33ff39796ffe.zip |
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
-rwxr-xr-x | libexec/rc/rc.d/zfskeys | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/libexec/rc/rc.d/zfskeys b/libexec/rc/rc.d/zfskeys new file mode 100755 index 000000000000..c558eb3af5d7 --- /dev/null +++ b/libexec/rc/rc.d/zfskeys @@ -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" |