commit:     9e7b41cda3df648b509a0f8a2af29a683100a46f
Author:     Thomas Deutschmann <whissi <AT> gentoo <DOT> org>
AuthorDate: Fri Jan 10 16:07:20 2020 +0000
Commit:     Thomas Deutschmann <whissi <AT> gentoo <DOT> org>
CommitDate: Sat Jan 11 19:54:40 2020 +0000
URL:        https://gitweb.gentoo.org/proj/genkernel.git/commit/?id=9e7b41cd

Rework ZFS unlock

- Prompt for key when key is unavailable, not when key is available.

- Check ZFS' keystatus property instead of return value to allow
  remote unlock.

- Add unlock-zfs command to remote rescue shell.

Closes: https://bugs.gentoo.org/705032
Signed-off-by: Thomas Deutschmann <whissi <AT> gentoo.org>

 defaults/initrd.defaults |  3 ++
 defaults/initrd.scripts  | 38 +++++++++++++++-----
 defaults/linuxrc         | 42 +++++++++++++++-------
 defaults/login-remote.sh |  5 +++
 defaults/unlock-zfs.sh   | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
 gen_initramfs.sh         |  6 ++++
 6 files changed, 165 insertions(+), 20 deletions(-)

diff --git a/defaults/initrd.defaults b/defaults/initrd.defaults
index 198800b..15326dd 100644
--- a/defaults/initrd.defaults
+++ b/defaults/initrd.defaults
@@ -103,6 +103,9 @@ CRYPT_KEYFILE_SWAP='/tmp/swap.key'
 CRYPT_ROOT_OPENED_LOCKFILE='/tmp/ROOT.opened'
 CRYPT_SWAP_OPENED_LOCKFILE='/tmp/SWAP.opened'
 
+ZFS_ENC_ENV_FILE='/etc/ZFS_ENC_ENV.conf'
+ZFS_ENC_OPENED_LOCKFILE='/tmp/ZFS.opened'
+
 # Flag for if ok when using CDROOT
 got_good_root='0'
 # if LOOP found on root before mount, trigger Unpacking additional packages

diff --git a/defaults/initrd.scripts b/defaults/initrd.scripts
index 73cd94c..6c7d72b 100644
--- a/defaults/initrd.scripts
+++ b/defaults/initrd.scripts
@@ -1087,6 +1087,20 @@ warn_msg() {
        [ "$2" != '1' ] && printf "%b\n" "${WARN}**${NORMAL}${BOLD} 
${msg_string} ${NORMAL}"
 }
 
+write_env_file() {
+       local env_file=${1}
+       shift
+
+       run touch "${env_file}"
+
+       local varname= varvalue=
+       for varname in $*
+       do
+               eval varvalue=\$${varname}
+               echo "${varname}=${varvalue}" >> "${env_file}"
+       done
+}
+
 crypt_filter() {
        if [ "${CRYPT_SILENT}" = '1' ]
        then
@@ -2265,14 +2279,15 @@ start_sshd() {
                return
        fi
 
-       # setup environment variables for the ssh login shell
-       local varname= varvalue=
-       run touch "${CRYPT_ENV_FILE}"
-       for varname in CRYPT_ROOT CRYPT_ROOT_TRIM CRYPT_SILENT CRYPT_SWAP
-       do
-               eval varvalue=\$${varname}
-               echo "${varname}=${varvalue}" >> "${CRYPT_ENV_FILE}"
-       done
+       # setup environment variables for the remote rescue shell
+       # ZFS will use a different file because $REAL_ROOT for ZFS
+       # isn't known yet.
+       write_env_file \
+               "${CRYPT_ENV_FILE}" \
+               CRYPT_ROOT \
+               CRYPT_ROOT_TRIM \
+               CRYPT_SILENT \
+               CRYPT_SWAP
 
        run touch /var/log/lastlog
 
@@ -2679,6 +2694,13 @@ get_mount_device() {
                ' ${NEW_ROOT}/etc/fstab
 }
 
+get_zfs_property() {
+       local device=${1}
+       local propertyname=${2}
+
+       echo "$(zfs get -H -o value ${propertyname} "${device}" 2>/dev/null)"
+}
+
 # If the kernel is handed a mount option is does not recognize, it WILL fail to
 # mount. util-linux handles auto/noauto, but busybox passes it straight to the 
kernel
 # which then rejects the mount.

diff --git a/defaults/linuxrc b/defaults/linuxrc
index f585017..7fbd0ad 100644
--- a/defaults/linuxrc
+++ b/defaults/linuxrc
@@ -491,7 +491,7 @@ then
                if [ ! -x ${i} ]
                then
                        USE_ZFS=0
-                       bad_msg 'Aborting use of zfs because ${i} not found!'
+                       bad_msg "Aborting use of ZFS because ${i} not found!"
                        break
                fi
        done
@@ -740,7 +740,7 @@ do
                                        ROOT_DEV="${REAL_ROOT#*=}"
                                        if [ "${ROOT_DEV}" != 'ZFS' ]
                                        then
-                                               if [ "$(zfs get type -o value 
-H ${ROOT_DEV} 2>/dev/null)" = 'filesystem' ]
+                                               if [ "$(get_zfs_property 
"${ROOT_DEV}" type)" = 'filesystem' ]
                                                then
                                                        got_good_root=1
                                                        REAL_ROOT=${ROOT_DEV}
@@ -753,7 +753,7 @@ do
                                                        continue
                                                fi
                                        else
-                                               BOOTFS=$(/sbin/zpool list -H -o 
bootfs 2>/dev/null)
+                                               BOOTFS=$(zpool list -H -o 
bootfs 2>/dev/null)
                                                if [ "${BOOTFS}" != '-' ]
                                                then
                                                        for i in ${BOOTFS}
@@ -801,6 +801,14 @@ do
                        echo
                fi
 
+               if [ "${USE_ZFS}" = '1' ]
+               then
+                       write_env_file \
+                               "${ZFS_ENC_ENV_FILE}" \
+                               REAL_ROOT \
+                               ROOTFSTYPE
+               fi
+
                # Check for a block device or /dev/nfs or zfs encryption
                if [ -n "${REAL_ROOT}" ] && [ "${REAL_ROOT}" = "/dev/nfs" ] || 
[ "${ROOTFSTYPE}" = "zfs" ] || [ -b "${REAL_ROOT}" ]
                then
@@ -810,20 +818,30 @@ do
                                # let's check if this dataset is encrypted and 
ask for passphrase
                                if [ "$(zpool list -H -o feature@encryption 
"${REAL_ROOT%%/*}" 2>/dev/null)" = 'active' ]
                                then
-                                       ZFS_KEYSTATUS="$(zfs get -H -o value 
keystatus "${REAL_ROOT}" 2>/dev/null)"
-                                       ZFS_ENCRYPTIONROOT="$(zfs get -H -o 
value encryptionroot "${REAL_ROOT}" 2>/dev/null)"
-                                       if ! [ "${ZFS_ENCRYPTIONROOT}" = '-' ] 
|| [ "${ZFS_KEYSTATUS}" = 'available' ]
+                                       ZFS_KEYSTATUS="$(get_zfs_property 
"${REAL_ROOT}" keystatus)"
+                                       ZFS_ENCRYPTIONROOT="$(get_zfs_property 
"${REAL_ROOT}" encryptionroot)"
+                                       if [ "${ZFS_ENCRYPTIONROOT}" != '-' ] 
&& [ "${ZFS_KEYSTATUS}" = 'unavailable' ]
                                        then
                                                good_msg "Detected ZFS 
encryption, asking for key"
-                                               zfs load-key 
"${ZFS_ENCRYPTIONROOT}"
-                                               retval=$?
-                                               # if the key loaded fine, 
confirm got_good_root to exit second while loop
-                                               if [ ${retval} -ne 0 ]
+                                               run zfs load-key 
"${ZFS_ENCRYPTIONROOT}"
+
+                                               # Get new key status to check 
if load-key was successful
+                                               # or dataset has been opened by 
someone else in the meantime (through SSH for instance)
+                                               
ZFS_KEYSTATUS="$(get_zfs_property "${REAL_ROOT}" keystatus)"
+
+                                               if [ "${ZFS_KEYSTATUS}" != 
'available' ]
                                                then
-                                                       bad_msg "${ROOT_DEV} is 
encrypted and not mountable without key"
+                                                       bad_msg "${REAL_ROOT} 
is encrypted and not mountable without key"
                                                        got_good_root=0
                                                        break
                                                fi
+
+                                               if [ -f 
"${ZFS_ENC_OPENED_LOCKFILE}" ]
+                                               then
+                                                       good_msg "${REAL_ROOT} 
device meanwhile was opened by someone else."
+                                               else
+                                                       run touch 
"${ZFS_ENC_OPENED_LOCKFILE}"
+                                               fi
                                        fi
                                fi
                        else
@@ -849,7 +867,7 @@ do
 
                if [ "${ROOTFSTYPE}" = 'zfs' ]
                then
-                       if [ "$(zfs get -H -o value mountpoint "${REAL_ROOT}")" 
= 'legacy' ]
+                       if [ "$(get_zfs_property "${REAL_ROOT}" mountpoint)" = 
'legacy' ]
                        then
                                MOUNT_STATE=rw
                        else

diff --git a/defaults/login-remote.sh b/defaults/login-remote.sh
index 588504f..94ee014 100644
--- a/defaults/login-remote.sh
+++ b/defaults/login-remote.sh
@@ -105,6 +105,11 @@ else
                good_msg "${NORMAL}To remote unlock LUKS-encrypted swap device, 
run '${BOLD}unlock-luks swap${NORMAL}'."
        fi
 
+       if [ -e "${ZFS_ENC_ENV_FILE}" ] && [ ! -f "${ZFS_ENC_OPENED_LOCKFILE}" ]
+       then
+               good_msg "${NORMAL}To remote unlock ZFS root device, run 
'${BOLD}unlock-zfs${NORMAL}'."
+       fi
+
        echo
 
        [ -x /bin/sh ] && SH=/bin/sh || SH=/bin/ash

diff --git a/defaults/unlock-zfs.sh b/defaults/unlock-zfs.sh
new file mode 100644
index 0000000..c22a214
--- /dev/null
+++ b/defaults/unlock-zfs.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+. /etc/initrd.defaults
+. /etc/initrd.scripts
+
+GK_INIT_LOG_PREFIX=${0}
+if [ -n "${SSH_CLIENT_IP}" ] && [ -n "${SSH_CLIENT_PORT}" ]
+then
+       GK_INIT_LOG_PREFIX="${0}[${SSH_CLIENT_IP}:${SSH_CLIENT_PORT}]"
+fi
+
+if [ -f "${ZFS_ENC_ENV_FILE}" ]
+then
+       . "${ZFS_ENC_ENV_FILE}"
+else
+       bad_msg "${ZFS_ENC_ENV_FILE} does not exist! Did you boot without 
'dozfs' kernel command-line parameter?"
+       exit 1
+fi
+
+main() {
+       if ! hash zfs >/dev/null 2>&1
+       then
+               bad_msg "zfs program is missing. Was initramfs built without 
--zfs parameter?"
+               exit 1
+       elif ! hash zpool >/dev/null 2>&1
+       then
+               bad_msg "zpool program is missing. Was initramfs built without 
--zfs parameter?"
+               exit 1
+       elif [ -z "${ROOTFSTYPE}" ]
+       then
+               bad_msg "Something went wrong. ROOTFSTYPE is not set!"
+               exit 1
+       elif [ "${ROOTFSTYPE}" != "zfs" ]
+       then
+               bad_msg "ROOTFSTYPE of 'zfs' required but '${ROOTFSTYPE}' 
detected!"
+               exit 1
+       elif [ -z "${REAL_ROOT}" ]
+       then
+               bad_msg "Something went wrong. REAL_ROOT is not set!"
+               exit 1
+       fi
+
+       if [ "$(zpool list -H -o feature@encryption "${REAL_ROOT%%/*}" 
2>/dev/null)" != 'active' ]
+       then
+               bad_msg "Root device ${REAL_ROOT} is not encrypted!"
+               exit 1
+       fi
+
+       local ZFS_ENCRYPTIONROOT="$(get_zfs_property "${REAL_ROOT}" 
encryptionroot)"
+       if [ "${ZFS_ENCRYPTIONROOT}" = '-' ]
+       then
+               bad_msg "Failed to determine encryptionroot for ${REAL_ROOT}!"
+               exit 1
+       fi
+
+       local ZFS_KEYSTATUS=
+       while true
+       do
+               if [ -e "${ZFS_ENC_OPENED_LOCKFILE}" ]
+               then
+                       good_msg "${REAL_ROOT} device meanwhile was opened by 
someone else."
+                       break
+               fi
+
+               zfs load-key "${ZFS_ENCRYPTIONROOT}"
+
+               ZFS_KEYSTATUS="$(get_zfs_property "${REAL_ROOT}" keystatus)"
+               if [ "${ZFS_KEYSTATUS}" = 'available' ]
+               then
+                       run touch "${ZFS_ENC_OPENED_LOCKFILE}"
+                       good_msg "ZFS device ${REAL_ROOT} opened"
+                       break
+               else
+                       bad_msg "Failed to open ZFS device ${REAL_ROOT}"
+
+                       # We need to stop here with a non-zero exit code to 
prevent
+                       # a loop when invalid keyfile was sent.
+                       exit 1
+               fi
+       done
+
+       if [ "${ZFS_KEYSTATUS}" = 'available' ]
+       then
+               # Kill any running load-key prompt.
+               run pkill -f "load-key" >/dev/null 2>&1
+       fi
+}
+
+main
+
+exit 0

diff --git a/gen_initramfs.sh b/gen_initramfs.sh
index 676b86d..8620414 100755
--- a/gen_initramfs.sh
+++ b/gen_initramfs.sh
@@ -1342,6 +1342,9 @@ append_dropbear() {
        cp -a "${GK_SHARE}"/defaults/unlock-luks.sh 
"${TDIR}"/usr/sbin/unlock-luks \
                || gen_die "Failed to copy 
'${GK_SHARE}/defaults/unlock-luks.sh' to '${TDIR}/usr/sbin/unlock-luks'"
 
+       cp -a "${GK_SHARE}"/defaults/unlock-zfs.sh 
"${TDIR}"/usr/sbin/unlock-zfs \
+               || gen_die "Failed to copy '${GK_SHARE}/defaults/unlock-zfs.sh' 
to '${TDIR}/usr/sbin/unlock-zfs'"
+
        cp -aL "${DROPBEAR_AUTHORIZED_KEYS_FILE}" "${TDIR}"/root/.ssh/ \
                || gen_die "Failed to copy '${DROPBEAR_AUTHORIZED_KEYS_FILE}'!"
 
@@ -1369,6 +1372,9 @@ append_dropbear() {
        chmod 0755 "${TDIR}"/usr/sbin/unlock-luks \
                || gen_die "Failed to chmod of '${TDIR}/usr/sbin/unlock-luks'!"
 
+       chmod 0755 "${TDIR}"/usr/sbin/unlock-zfs \
+               || gen_die "Failed to chmod of '${TDIR}/usr/sbin/unlock-zfs'!"
+
        chmod 0640 "${TDIR}"/etc/shadow \
                || gen_die "Failed to chmod of '${TDIR}/etc/shadow'!"
 

Reply via email to