diff options
-rw-r--r-- | contrib/initramfs/Makefile.am | 3 | ||||
-rw-r--r-- | contrib/initramfs/README.initramfs.markdown | 12 | ||||
-rw-r--r-- | contrib/initramfs/hooks/.gitignore | 1 | ||||
-rw-r--r-- | contrib/initramfs/hooks/Makefile.am | 6 | ||||
-rwxr-xr-x | contrib/initramfs/hooks/zfs.in | 1 | ||||
-rw-r--r-- | contrib/initramfs/hooks/zfsunlock.in | 18 | ||||
-rw-r--r-- | contrib/initramfs/scripts/zfs | 12 | ||||
-rwxr-xr-x | contrib/initramfs/zfsunlock | 42 |
8 files changed, 92 insertions, 3 deletions
diff --git a/contrib/initramfs/Makefile.am b/contrib/initramfs/Makefile.am index 7d8597312..ee17de096 100644 --- a/contrib/initramfs/Makefile.am +++ b/contrib/initramfs/Makefile.am @@ -1,5 +1,8 @@ initrddir = /usr/share/initramfs-tools +dist_initrd_SCRIPTS = \ + zfsunlock + SUBDIRS = conf.d conf-hooks.d hooks scripts EXTRA_DIST = \ diff --git a/contrib/initramfs/README.initramfs.markdown b/contrib/initramfs/README.initramfs.markdown index c8bc9f4bd..ddae71a2e 100644 --- a/contrib/initramfs/README.initramfs.markdown +++ b/contrib/initramfs/README.initramfs.markdown @@ -72,3 +72,15 @@ The following kernel command line arguments are supported: * `zfsdebug=(on,yes,1)`: Show extra debugging information * `zfsforce=(on,yes,1)`: Force import the pool * `rollback=(on,yes,1)`: Rollback to (instead of clone) the snapshot + +### Unlocking a ZFS encrypted root over SSH + +To use this feature: + +1. Install the `dropbear-initramfs` package. You may wish to uninstall the + `cryptsetup-initramfs` package to avoid warnings. +2. Add your SSH key(s) to `/etc/dropbear-initramfs/authorized_keys`. Note + that Dropbear does not support ed25519 keys; use RSA (2048-bit or more) + instead. +3. Rebuild the initramfs with your keys: `update-initramfs -u` +4. During the system boot, login via SSH and run: `zfsunlock` diff --git a/contrib/initramfs/hooks/.gitignore b/contrib/initramfs/hooks/.gitignore index 73304bc2c..4e1604e18 100644 --- a/contrib/initramfs/hooks/.gitignore +++ b/contrib/initramfs/hooks/.gitignore @@ -1 +1,2 @@ zfs +zfsunlock diff --git a/contrib/initramfs/hooks/Makefile.am b/contrib/initramfs/hooks/Makefile.am index d76845494..9b20c080a 100644 --- a/contrib/initramfs/hooks/Makefile.am +++ b/contrib/initramfs/hooks/Makefile.am @@ -1,10 +1,12 @@ hooksdir = /usr/share/initramfs-tools/hooks hooks_SCRIPTS = \ - zfs + zfs \ + zfsunlock EXTRA_DIST = \ - $(top_srcdir)/contrib/initramfs/hooks/zfs.in + $(top_srcdir)/contrib/initramfs/hooks/zfs.in \ + $(top_srcdir)/contrib/initramfs/hooks/zfsunlock.in $(hooks_SCRIPTS):%:%.in Makefile -$(SED) -e 's,@sbindir\@,$(sbindir),g' \ diff --git a/contrib/initramfs/hooks/zfs.in b/contrib/initramfs/hooks/zfs.in index 15f23c908..ff7e49f12 100755 --- a/contrib/initramfs/hooks/zfs.in +++ b/contrib/initramfs/hooks/zfs.in @@ -21,6 +21,7 @@ COPY_FILE_LIST="$COPY_FILE_LIST @udevruledir@/69-vdev.rules" # These prerequisites are provided by the base system. COPY_EXEC_LIST="$COPY_EXEC_LIST /usr/bin/dirname /bin/hostname /sbin/blkid" COPY_EXEC_LIST="$COPY_EXEC_LIST /usr/bin/env" +COPY_EXEC_LIST="$COPY_EXEC_LIST $(which systemd-ask-password)" # Explicitly specify all kernel modules because automatic dependency resolution # is unreliable on many systems. diff --git a/contrib/initramfs/hooks/zfsunlock.in b/contrib/initramfs/hooks/zfsunlock.in new file mode 100644 index 000000000..c8ae86363 --- /dev/null +++ b/contrib/initramfs/hooks/zfsunlock.in @@ -0,0 +1,18 @@ +#!/bin/sh + +PREREQ="dropbear" + +prereqs() { + echo "$PREREQ" +} + +case "$1" in + prereqs) + prereqs + exit 0 + ;; +esac + +. /usr/share/initramfs-tools/hook-functions + +copy_exec /usr/share/initramfs-tools/zfsunlock /usr/bin diff --git a/contrib/initramfs/scripts/zfs b/contrib/initramfs/scripts/zfs index dbc4e253f..a795fd39f 100644 --- a/contrib/initramfs/scripts/zfs +++ b/contrib/initramfs/scripts/zfs @@ -405,6 +405,8 @@ decrypt_fs() ENCRYPTIONROOT="$(get_fs_value "${fs}" encryptionroot)" KEYLOCATION="$(get_fs_value "${ENCRYPTIONROOT}" keylocation)" + echo "${ENCRYPTIONROOT}" > /run/zfs_fs_name + # If root dataset is encrypted... if ! [ "${ENCRYPTIONROOT}" = "-" ]; then KEYSTATUS="$(get_fs_value "${ENCRYPTIONROOT}" keystatus)" @@ -418,6 +420,7 @@ decrypt_fs() # Prompt with plymouth, if active elif [ -e /bin/plymouth ] && /bin/plymouth --ping 2>/dev/null; then + echo "plymouth" > /run/zfs_console_askpwd_cmd while [ $TRY_COUNT -gt 0 ]; do plymouth ask-for-password --prompt "Encrypted ZFS password for ${ENCRYPTIONROOT}" | \ $ZFS load-key "${ENCRYPTIONROOT}" && break @@ -426,6 +429,7 @@ decrypt_fs() # Prompt with systemd, if active elif [ -e /run/systemd/system ]; then + echo "systemd-ask-password" > /run/zfs_console_askpwd_cmd while [ $TRY_COUNT -gt 0 ]; do systemd-ask-password "Encrypted ZFS password for ${ENCRYPTIONROOT}" --no-tty | \ $ZFS load-key "${ENCRYPTIONROOT}" && break @@ -434,7 +438,8 @@ decrypt_fs() # Prompt with ZFS tty, otherwise else - # Setting "printk" temporarily to "7" will allow prompt even if kernel option "quiet" + # Temporarily setting "printk" to "7" allows the prompt to appear even when the "quiet" kernel option has been used + echo "load-key" > /run/zfs_console_askpwd_cmd storeprintk="$(awk '{print $1}' /proc/sys/kernel/printk)" echo 7 > /proc/sys/kernel/printk $ZFS load-key "${ENCRYPTIONROOT}" @@ -964,6 +969,11 @@ mountroot() mount_fs "$fs" done + touch /run/zfs_unlock_complete + if [ -e /run/zfs_unlock_complete_notify ]; then + read zfs_unlock_complete_notify < /run/zfs_unlock_complete_notify + fi + # ------------ # Debugging information if [ -n "${ZFS_DEBUG}" ] diff --git a/contrib/initramfs/zfsunlock b/contrib/initramfs/zfsunlock new file mode 100755 index 000000000..1202a144d --- /dev/null +++ b/contrib/initramfs/zfsunlock @@ -0,0 +1,42 @@ +#!/bin/sh + +set -eu +if [ ! -e /run/zfs_fs_name ]; then + echo "Wait for the root pool to be imported or press Ctrl-C to exit." +fi +while [ ! -e /run/zfs_fs_name ]; do + if [ -e /run/zfs_unlock_complete ]; then + exit 0 + fi + sleep 0.5 +done +echo +echo "Unlocking encrypted ZFS filesystems..." +echo "Enter the password or press Ctrl-C to exit." +echo +zfs_fs_name="" +if [ ! -e /run/zfs_unlock_complete_notify ]; then + mkfifo /run/zfs_unlock_complete_notify +fi +while [ ! -e /run/zfs_unlock_complete ]; do + zfs_fs_name=$(cat /run/zfs_fs_name) + zfs_console_askpwd_cmd=$(cat /run/zfs_console_askpwd_cmd) + systemd-ask-password "Encrypted ZFS password for ${zfs_fs_name}:" | \ + /sbin/zfs load-key "$zfs_fs_name" || true + if [ "$(/sbin/zfs get -H -ovalue keystatus "$zfs_fs_name" 2> /dev/null)" = "available" ]; then + echo "Password for $zfs_fs_name accepted." + zfs_console_askpwd_pid=$(ps a -o pid= -o args | grep -v grep | grep "$zfs_console_askpwd_cmd" | cut -d ' ' -f3 | sort -n | head -n1) + if [ -n "$zfs_console_askpwd_pid" ]; then + kill "$zfs_console_askpwd_pid" + fi + # Wait for another filesystem to unlock. + while [ "$(cat /run/zfs_fs_name)" = "$zfs_fs_name" ] && [ ! -e /run/zfs_unlock_complete ]; do + sleep 0.5 + done + else + echo "Wrong password. Try again." + fi +done +echo "Unlocking complete. Resuming boot sequence..." +echo "Please reconnect in a while." +echo "ok" > /run/zfs_unlock_complete_notify |