Skip to content

Commit 7c17448

Browse files
systemd: rework clevis-luks-askpass for improved reliability
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.
1 parent 3f9deb1 commit 7c17448

File tree

4 files changed

+88
-19
lines changed

4 files changed

+88
-19
lines changed

src/luks/clevis-luks-common-functions

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,66 @@ clevis_luks_unlock_device() {
312312

313313
return 1
314314
}
315+
316+
# clevis_map_device() tries to map the device received as a parameter to a
317+
# block device. As per crypttab(5), we support /path/to/encrypted/blockdev
318+
# or UUID=<uuid>.
319+
clevis_map_device() {
320+
local CDEV="${1}"
321+
322+
if [[ "${CDEV}" == UUID=* ]]; then
323+
CDEV=/dev/disk/by-uuid/${CDEV#UUID=}
324+
fi
325+
326+
if [[ "${CDEV}" == /* ]] && [ -b "${CDEV}" ]; then
327+
echo "${CDEV}"
328+
else
329+
# Invalid crypttab entry.
330+
return 1
331+
fi
332+
}
333+
334+
# clevis_is_luks_device_by_uuid_open() checks whether the LUKS device whose
335+
# UUID was passed as a parameter is already open.
336+
clevis_is_luks_device_by_uuid_open() {
337+
local dev_luks_uuid="${1}"
338+
[ -z "${dev_luks_uuid}" ] && return 1
339+
dev_luks_uuid="$(echo "${dev_luks_uuid}" | sed -e 's/-//g')"
340+
test -b /dev/disk/by-id/dm-uuid-*"${dev_luks_uuid}"*
341+
}
342+
343+
# clevis_devices_to_unlock() returns a list of devices to be unlocked, as per
344+
# the info from crypttab.
345+
clevis_devices_to_unlock() {
346+
[ ! -r /etc/crypttab ] && return 1
347+
348+
local dev clevis_devices crypt_device dev_uuid bindings
349+
clevis_devices=
350+
351+
# Build list of devices to unlock.
352+
while read -r _ crypt_device _; do
353+
if ! dev=$(clevis_map_device "${crypt_device}") \
354+
|| [ -z "${dev}" ]; then
355+
# Unable to get the device - maybe it's not available, e.g. a
356+
# device on a volume group that has not been activated yet.
357+
# Add it to the list anyway, since it's a pending device.
358+
clevis_devices="${clevis_devices} ${dev}"
359+
continue
360+
fi
361+
362+
# Check if this device has clevis bindings.
363+
if ! bindings="$(clevis luks list -d "${dev}" 2>/dev/null)" \
364+
|| [ -z "${bindings}" ]; then
365+
continue
366+
fi
367+
368+
# Check if this device is already open.
369+
dev_uuid="$(cryptsetup luksUUID "${dev}")"
370+
if clevis_is_luks_device_by_uuid_open "${dev_uuid}"; then
371+
continue
372+
fi
373+
374+
clevis_devices="${clevis_devices} ${dev}"
375+
done < /etc/crypttab
376+
echo "${clevis_devices}" | sed -e 's/^ //'
377+
}

src/luks/clevis-luks-unlockers.7.adoc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ You can enable late boot unlocking by executing the following command:
4747

4848
$ sudo systemctl enable clevis-luks-askpass.path
4949

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

5453
== DESKTOP UNLOCKING
5554

src/luks/systemd/clevis-luks-askpass

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#!/bin/bash -e
1+
#!/bin/bash
2+
set -eu
23
# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
34
#
45
# Copyright (c) 2016 Red Hat, Inc.
@@ -21,25 +22,24 @@
2122

2223
. clevis-luks-common-functions
2324

24-
shopt -s nullglob
25-
25+
loop=
2626
path=/run/systemd/ask-password
2727
while getopts ":lp:" o; do
28-
case "$o" in
28+
case "${o}" in
2929
l) loop=true;;
30-
p) path="$OPTARG";;
30+
p) path="${OPTARG}";;
3131
*) ;;
3232
esac
3333
done
3434

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

38-
for question in "$path"/ask.*; do
39-
unlocked=false
4041
d=
4142
s=
42-
4343
while read -r line; do
4444
case "$line" in
4545
Id=cryptsetup:*) d="${line##Id=cryptsetup:}";;
@@ -50,16 +50,22 @@ while true; do
5050
[ -b "${d}" ] || continue
5151
[ -S "${s}" ] || continue
5252

53-
if pt="$(clevis_luks_unlock_device "${d}")"; then
54-
echo -n "+${pt}" | ncat -U -u --send-only "${s}"
55-
unlocked=true
53+
if ! pt="$(clevis_luks_unlock_device "${d}")" || [ -z "${pt}" ]; then
54+
continue
55+
fi
56+
57+
uuid="$(cryptsetup luksUUID "${d}")"
58+
if ! printf '+%s' "${pt}" | ncat -U -u --send-only "${s}"; then
59+
echo "Unable to unlock ${d} (UUID=${uuid}) with recovered passphrase" >&2
60+
continue
5661
fi
5762

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

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

src/luks/systemd/dracut/clevis/module-setup.sh.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ install() {
3838
/etc/services \
3939
@libexecdir@/clevis-luks-askpass \
4040
clevis-luks-common-functions \
41-
head grep sed \
41+
head grep sed cut \
4242
clevis-decrypt \
43+
clevis-luks-list \
4344
cryptsetup \
4445
luksmeta \
4546
clevis \

0 commit comments

Comments
 (0)