From 3250784e99016d9f920892dbb1438b9e76fb210b Mon Sep 17 00:00:00 2001 From: Sergio Correia Date: Sun, 10 May 2020 15:57:23 -0300 Subject: [PATCH 8/8] Use one clevis-luks-askpass per device This should improve the reliability of the boot unlocking, especially when unlocking multiple devices upon boot. It also greatly simplifies the configuration, as there is no need to enable any systemd units manually nor add _netdev to either fstab or crypttab. --- src/luks/clevis-luks-common-functions | 8 ++ src/luks/clevis-luks-unlockers.7.adoc | 16 +--- src/luks/systemd/clevis-luks-askpass | 81 ++++++------------- src/luks/systemd/clevis-luks-askpass.path | 10 --- .../systemd/clevis-luks-askpass.service.in | 8 -- src/luks/systemd/clevis-luks-askpass@.path | 12 +++ .../systemd/clevis-luks-askpass@.service.in | 8 ++ .../systemd/dracut/clevis/module-setup.sh.in | 23 ++++++ src/luks/systemd/meson.build | 6 +- 9 files changed, 80 insertions(+), 92 deletions(-) delete mode 100644 src/luks/systemd/clevis-luks-askpass.path delete mode 100644 src/luks/systemd/clevis-luks-askpass.service.in create mode 100644 src/luks/systemd/clevis-luks-askpass@.path create mode 100644 src/luks/systemd/clevis-luks-askpass@.service.in diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions index 5b515ad..c9d712a 100644 --- a/src/luks/clevis-luks-common-functions +++ b/src/luks/clevis-luks-common-functions @@ -555,3 +555,11 @@ clevis_luks_restore_dev() { fi return 0 } + +# clevis_is_luks_device_by_uuid_open() checks whether the LUKS device with +# given UUID is open. +clevis_is_luks_device_by_uuid_open() { + local LUKS_UUID="${1}" + [ -z "${LUKS_UUID}" ] && return 1 + test -b /dev/disk/by-id/dm-uuid-*"${LUKS_UUID//-/}"* +} diff --git a/src/luks/clevis-luks-unlockers.7.adoc b/src/luks/clevis-luks-unlockers.7.adoc index 161b73a..e8d47ba 100644 --- a/src/luks/clevis-luks-unlockers.7.adoc +++ b/src/luks/clevis-luks-unlockers.7.adoc @@ -26,7 +26,7 @@ You can unlock a LUKS volume manually using the following command: For more information, see link:clevis-luks-unlock.1.adoc[*clevis-luks-unlock*(1)]. -== EARLY BOOT UNLOCKING +== BOOT UNLOCKING If Clevis integration does not already ship in your initramfs, you may need to rebuild your initramfs with this command: @@ -34,23 +34,13 @@ rebuild your initramfs with this command: $ sudo dracut -f Once Clevis is integrated into your initramfs, a simple reboot should unlock -your root volume. Note, however, that early boot integration only works for the -root volume. Non-root volumes should use the late boot unlocker. +your clevis-bound volumes. Root volumes will be unlocked in early-boot, while the +remaining volumes will be unlocked after dracut switch-root. Dracut will bring up your network using DHCP by default. If you need to specify additional network parameters, such as static IP configuration, please consult the dracut documentation. -== LATE BOOT UNLOCKING - -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. - == DESKTOP UNLOCKING When the udisks2 unlocker is installed, your GNOME desktop session should diff --git a/src/luks/systemd/clevis-luks-askpass b/src/luks/systemd/clevis-luks-askpass index 9fea6aa..20294e5 100755 --- a/src/luks/systemd/clevis-luks-askpass +++ b/src/luks/systemd/clevis-luks-askpass @@ -19,96 +19,61 @@ # along with this program. If not, see . # -UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e +. clevis-luks-common-functions shopt -s nullglob path=/run/systemd/ask-password -while getopts ":lp:" o; do +while getopts ":lp:u:" o; do case "$o" in l) loop=true;; p) path="$OPTARG";; + u) device_uuid=$OPTARG;; + *) ;; esac done -luks1_decrypt() { - luksmeta load "$@" \ - | clevis decrypt - - local rc - for rc in "${PIPESTATUS[@]}"; do - [ $rc -eq 0 ] || return $rc - done - return 0 -} - -luks2_jwe() { - # jose jwe fmt -c outputs extra \n, so clean it up - cryptsetup token export "$@" \ - | jose fmt -j- -Og jwe -o- \ - | jose jwe fmt -i- -c \ - | tr -d '\n' - - local rc - for rc in "${PIPESTATUS[@]}"; do - [ $rc -eq 0 ] || return $rc - done - return 0 -} - while true; do todo=0 for question in "$path"/ask.*; do - metadata=false unlocked=false d= s= - while read line; do + while read -r line; do case "$line" in Id=cryptsetup:*) d="${line##Id=cryptsetup:}";; Socket=*) s="${line##Socket=}";; esac done < "$question" - [ "$d" ] && [ "$s" ] || continue + [ -b "${d}" ] || continue + [ -S "${s}" ] || continue - if cryptsetup isLuks --type luks1 "$d"; then - # If the device is not initialized, sliently skip it. - luksmeta test -d "$d" || continue - - while read -r slot state uuid; do - [ "$state" == "active" ] || continue - [ "$uuid" == "$UUID" ] || continue - metadata=true - - if pt="$(luks1_decrypt -d "$d" -s "$slot" -u "$UUID")"; then - echo -n "+$pt" | ncat -U -u --send-only "$s" - unlocked=true - break - fi - done < <(luksmeta show -d "$d") - elif cryptsetup isLuks --type luks2 "$d"; then - while read -r id; do - jwe="$(luks2_jwe --token-id "$id" "$d")" \ - || continue - metadata=true + if [ -n "${device_uuid}" ]; then + uuid="$(cryptsetup luksUUID "${d}")" + [ "${uuid}" != "${device_uuid}" ] && todo=1 && continue + fi - if pt="$(echo -n "$jwe" | clevis decrypt)"; then - echo -n "+$pt" | ncat -U -u --send-only "$s" - unlocked=true - break - fi - done < <(cryptsetup luksDump "$d" | sed -rn 's|^\s+([0-9]+): clevis|\1|p') + if pt="$(clevis_luks_unlock_device "${d}")"; then + echo -n "+$pt" | ncat -U -u --send-only "$s" + unlocked=true fi - [ "$metadata" == true ] || continue + [ -n "${device_uuid}" ] && [ "${unlocked}" == true ] && break [ "$unlocked" == true ] && continue ((todo++)) done - if [ $todo -eq 0 ] || [ "$loop" != true ]; then + if [ -n "${device_uuid}" ]; then + [ ! -b /dev/disk/by-uuid/"${device_uuid}" ] && break + if clevis_is_luks_device_by_uuid_open "${device_uuid}"; then + break + fi + fi + + if [ "$todo" -eq 0 ] || [ "$loop" != true ]; then break; fi diff --git a/src/luks/systemd/clevis-luks-askpass.path b/src/luks/systemd/clevis-luks-askpass.path deleted file mode 100644 index a4d01ba..0000000 --- a/src/luks/systemd/clevis-luks-askpass.path +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Clevis systemd-ask-password Watcher -Before=remote-fs-pre.target -Wants=remote-fs-pre.target - -[Path] -PathChanged=/run/systemd/ask-password - -[Install] -WantedBy=remote-fs.target diff --git a/src/luks/systemd/clevis-luks-askpass.service.in b/src/luks/systemd/clevis-luks-askpass.service.in deleted file mode 100644 index 2c6bbed..0000000 --- a/src/luks/systemd/clevis-luks-askpass.service.in +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Clevis LUKS systemd-ask-password Responder -Requires=network-online.target -After=network-online.target - -[Service] -Type=oneshot -ExecStart=@libexecdir@/clevis-luks-askpass -l diff --git a/src/luks/systemd/clevis-luks-askpass@.path b/src/luks/systemd/clevis-luks-askpass@.path new file mode 100644 index 0000000..3f23665 --- /dev/null +++ b/src/luks/systemd/clevis-luks-askpass@.path @@ -0,0 +1,12 @@ +[Unit] +Description=Clevis systemd-ask-password Watcher for %i +DefaultDependencies=no +Conflicts=shutdown.target +Before=basic.target shutdown.target + +[Path] +DirectoryNotEmpty=/run/systemd/ask-password +MakeDirectory=yes + +[Install] +WantedBy=basic.target diff --git a/src/luks/systemd/clevis-luks-askpass@.service.in b/src/luks/systemd/clevis-luks-askpass@.service.in new file mode 100644 index 0000000..4165ec5 --- /dev/null +++ b/src/luks/systemd/clevis-luks-askpass@.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Clevis LUKS systemd-ask-password Responder for luks-%i +DefaultDependencies=no +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +ExecStart=@libexecdir@/clevis-luks-askpass -u %i diff --git a/src/luks/systemd/dracut/clevis/module-setup.sh.in b/src/luks/systemd/dracut/clevis/module-setup.sh.in index abc79b3..1a0d6f7 100755 --- a/src/luks/systemd/dracut/clevis/module-setup.sh.in +++ b/src/luks/systemd/dracut/clevis/module-setup.sh.in @@ -23,6 +23,24 @@ depends() { return 255 } +configure_passwd_watchers() { + if ! command -v systemctl >/dev/null; then + return 1 + fi + + find /etc/systemd/system/ -name "clevis-luks-askpass*" -exec rm -f {} \; + + local uuid + for dev in $(lsblk -p -n -s -r \ + | awk '$6 == "crypt" { getline; print $1 }' | sort -u); do + uuid=$(cryptsetup luksUUID "${dev}") + + if clevis luks list -d "${dev}" >/dev/null 2>/dev/null; then + systemctl enable "clevis-luks-askpass@${uuid}.path" 2>/dev/null + fi + done +} + install() { inst_hook initqueue/online 60 "$moddir/clevis-hook.sh" inst_hook initqueue/settled 60 "$moddir/clevis-hook.sh" @@ -30,6 +48,10 @@ install() { inst_multiple \ /etc/services \ @libexecdir@/clevis-luks-askpass \ + clevis-luks-common-functions \ + head \ + grep \ + sed \ clevis-decrypt \ cryptsetup \ luksmeta \ @@ -38,5 +60,6 @@ install() { jose \ ncat + configure_passwd_watchers dracut_need_initqueue } diff --git a/src/luks/systemd/meson.build b/src/luks/systemd/meson.build index 369e7f7..334e84c 100644 --- a/src/luks/systemd/meson.build +++ b/src/luks/systemd/meson.build @@ -6,13 +6,13 @@ if systemd.found() unitdir = systemd.get_pkgconfig_variable('systemdsystemunitdir') configure_file( - input: 'clevis-luks-askpass.service.in', - output: 'clevis-luks-askpass.service', + input: 'clevis-luks-askpass@.service.in', + output: 'clevis-luks-askpass@.service', install_dir: unitdir, configuration: data, ) - install_data('clevis-luks-askpass.path', install_dir: unitdir) + install_data('clevis-luks-askpass@.path', install_dir: unitdir) install_data('clevis-luks-askpass', install_dir: libexecdir) else warning('Will not install systemd support due to missing dependencies!') -- 2.18.4