aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Prokopenko <[email protected]>2020-05-03 00:43:42 -0500
committerBrian Behlendorf <[email protected]>2020-05-07 16:41:16 -0700
commit1cc635a2dd0379181950a1458255ea8ae8b9c1e0 (patch)
treeb872c2a08620fe34cd6e7c6824ed6be927a99497
parent746d22ee02d2617ee982f1620b06f882b924ce8e (diff)
Unlock encrypted root partition over SSH
This commit add a new feature for Debian-based distributions to unlock encrypted root partition over SSH. This feature is very handy on headless NAS or VPS cloud servers. To use this feature, you will need to install the dropbear-initramfs package. Reviewed-By: Brian Behlendorf <[email protected]> Reviewed-By: Tom Caputi <[email protected]> Signed-off-by: Andrey Prokopenko <[email protected]> Signed-off-by: Richard Laager <[email protected]> Closes #10027
-rw-r--r--contrib/initramfs/Makefile.am3
-rw-r--r--contrib/initramfs/README.initramfs.markdown12
-rw-r--r--contrib/initramfs/hooks/.gitignore1
-rw-r--r--contrib/initramfs/hooks/Makefile.am6
-rwxr-xr-xcontrib/initramfs/hooks/zfs.in1
-rw-r--r--contrib/initramfs/hooks/zfsunlock.in18
-rw-r--r--contrib/initramfs/scripts/zfs12
-rwxr-xr-xcontrib/initramfs/zfsunlock42
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