Skip to content

Commit

Permalink
systemd: rework clevis-luks-askpass for improved reliability
Browse files Browse the repository at this point in the history
clevis-luks-askpass has been refactored so that it becomes both
simpler and more reliable.

We now get the list of devices to be unlocked from crypttab, which
makes it simpler to verify whether there are any devices pending to
be unlocked. This improves the reliability in the situation when we
want to unlock multiple devices.

Also, remove the suggestion to add _netdev to crypttab/fstab, as that
is no longer required and in practice could be problematic in many
situations, as it would create dependencies from units to be mounted
during the boot process.

To set up multiple LUKS devices to be unlocked during the boot process,
do the following:
1) create clevis bindings for all the devices
2) run dracut -f to update the initramfs
3) enable clevis-luks-askpass.path unit (systemctl enable
   clevis-luks-askpass.path), so that devices that are not unlocked in
   early boot will be unlocked after switch-root.
   There is no harm in enabling this unit even if there are no devices
   to be unlocked after switch root, so it might be a good idea to
   simply enable it always.
4) if using tang, network needs to be setup, as since c52caeb (dracut:
   drop rd.neednet=1 injection), we do not add `rd.neednet=1`
   automatically anymore, in order to better support generic initrds
   and work similar to other root-on-{NFS,iSCSI,NBD,...} schemes, where
   one must explicitly configure networking, when required.
   If using DHCP, passing rd.neednet=1 -- via e.g. grub or dracut's
   --kernel-cmdline option -- should be enough.
  • Loading branch information
sergio-correia committed Aug 28, 2020
1 parent 3f9deb1 commit 7c17448
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 19 deletions.
63 changes: 63 additions & 0 deletions src/luks/clevis-luks-common-functions
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,66 @@ clevis_luks_unlock_device() {

return 1
}

# clevis_map_device() tries to map the device received as a parameter to a
# block device. As per crypttab(5), we support /path/to/encrypted/blockdev
# or UUID=<uuid>.
clevis_map_device() {
local CDEV="${1}"

if [[ "${CDEV}" == UUID=* ]]; then
CDEV=/dev/disk/by-uuid/${CDEV#UUID=}
fi

if [[ "${CDEV}" == /* ]] && [ -b "${CDEV}" ]; then
echo "${CDEV}"
else
# Invalid crypttab entry.
return 1
fi
}

# clevis_is_luks_device_by_uuid_open() checks whether the LUKS device whose
# UUID was passed as a parameter is already open.
clevis_is_luks_device_by_uuid_open() {
local dev_luks_uuid="${1}"
[ -z "${dev_luks_uuid}" ] && return 1
dev_luks_uuid="$(echo "${dev_luks_uuid}" | sed -e 's/-//g')"
test -b /dev/disk/by-id/dm-uuid-*"${dev_luks_uuid}"*
}

# clevis_devices_to_unlock() returns a list of devices to be unlocked, as per
# the info from crypttab.
clevis_devices_to_unlock() {
[ ! -r /etc/crypttab ] && return 1

local dev clevis_devices crypt_device dev_uuid bindings
clevis_devices=

# Build list of devices to unlock.
while read -r _ crypt_device _; do
if ! dev=$(clevis_map_device "${crypt_device}") \
|| [ -z "${dev}" ]; then
# Unable to get the device - maybe it's not available, e.g. a
# device on a volume group that has not been activated yet.
# Add it to the list anyway, since it's a pending device.
clevis_devices="${clevis_devices} ${dev}"
continue
fi

# Check if this device has clevis bindings.
if ! bindings="$(clevis luks list -d "${dev}" 2>/dev/null)" \
|| [ -z "${bindings}" ]; then
continue
fi

# Check if this device is already open.
dev_uuid="$(cryptsetup luksUUID "${dev}")"
if clevis_is_luks_device_by_uuid_open "${dev_uuid}"; then
continue
fi

clevis_devices="${clevis_devices} ${dev}"
done < /etc/crypttab
echo "${clevis_devices}" | sed -e 's/^ //'
}
5 changes: 2 additions & 3 deletions src/luks/clevis-luks-unlockers.7.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ You can enable late boot unlocking by executing the following command:

$ sudo systemctl enable clevis-luks-askpass.path

After a reboot, Clevis will attempt to unlock all *_netdev* devices listed in
*/etc/crypttab* when systemd prompts for their passwords. This implies that
systemd support for *_netdev* is required.
After a reboot, Clevis will attempt to unlock all devices listed in
*/etc/crypttab* that have clevis bindings when systemd prompts for their passwords.

== DESKTOP UNLOCKING

Expand Down
36 changes: 21 additions & 15 deletions src/luks/systemd/clevis-luks-askpass
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/bash -e
#!/bin/bash
set -eu
# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
#
# Copyright (c) 2016 Red Hat, Inc.
Expand All @@ -21,25 +22,24 @@

. clevis-luks-common-functions

shopt -s nullglob

loop=
path=/run/systemd/ask-password
while getopts ":lp:" o; do
case "$o" in
case "${o}" in
l) loop=true;;
p) path="$OPTARG";;
p) path="${OPTARG}";;
*) ;;
esac
done

while true; do
todo=0
for question in "${path}"/ask.*; do
# question will expand to itself, in case no files match, so we verify
# whether it actually exists, before proceeding.
[ ! -e "${question}" ] && continue

for question in "$path"/ask.*; do
unlocked=false
d=
s=

while read -r line; do
case "$line" in
Id=cryptsetup:*) d="${line##Id=cryptsetup:}";;
Expand All @@ -50,16 +50,22 @@ while true; do
[ -b "${d}" ] || continue
[ -S "${s}" ] || continue

if pt="$(clevis_luks_unlock_device "${d}")"; then
echo -n "+${pt}" | ncat -U -u --send-only "${s}"
unlocked=true
if ! pt="$(clevis_luks_unlock_device "${d}")" || [ -z "${pt}" ]; then
continue
fi

uuid="$(cryptsetup luksUUID "${d}")"
if ! printf '+%s' "${pt}" | ncat -U -u --send-only "${s}"; then
echo "Unable to unlock ${d} (UUID=${uuid}) with recovered passphrase" >&2
continue
fi

[ "$unlocked" == true ] && continue
((todo++))
echo "Unlocked ${d} (UUID=${uuid}) successfully" >&2
done

if [ "$todo" -eq 0 ] || [ "$loop" != true ]; then
[ "${loop}" != true ] && break
# Checking for pending devices to be unlocked.
if remaining=$(clevis_devices_to_unlock) && [ -z "${remaining}" ]; then
break;
fi

Expand Down
3 changes: 2 additions & 1 deletion src/luks/systemd/dracut/clevis/module-setup.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ install() {
/etc/services \
@libexecdir@/clevis-luks-askpass \
clevis-luks-common-functions \
head grep sed \
head grep sed cut \
clevis-decrypt \
clevis-luks-list \
cryptsetup \
luksmeta \
clevis \
Expand Down

0 comments on commit 7c17448

Please sign in to comment.