public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Thomas Deutschmann" <whissi@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/genkernel:master commit in: defaults/, /
Date: Sun, 12 Jan 2020 14:59:27 +0000 (UTC)	[thread overview]
Message-ID: <1578772480.9e7b41cda3df648b509a0f8a2af29a683100a46f.whissi@gentoo> (raw)

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	other threads:[~2020-01-12 14:59 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-01-12 14:59 Thomas Deutschmann [this message]
  -- strict thread matches above, loose matches on Subject: below --
2020-08-28 20:18 [gentoo-commits] proj/genkernel:master commit in: defaults/, / Thomas Deutschmann
2020-08-28 20:18 Thomas Deutschmann
2020-08-01 21:41 Thomas Deutschmann
2020-07-23 23:57 Thomas Deutschmann
2020-07-16 15:03 Thomas Deutschmann
2019-11-24 20:00 Thomas Deutschmann
2017-01-02 20:14 Matt Thode
2016-01-05 18:51 Richard Farina
2015-04-12 21:04 Mike Frysinger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1578772480.9e7b41cda3df648b509a0f8a2af29a683100a46f.whissi@gentoo \
    --to=whissi@gentoo.org \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox