Blob Blame Raw
From 3250784e99016d9f920892dbb1438b9e76fb210b Mon Sep 17 00:00:00 2001
From: Sergio Correia <scorreia@redhat.com>
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 <http://www.gnu.org/licenses/>.
 #
 
-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