Blob Blame History Raw
From 37502d4c8974370a38496f44e279d56061fe67c3 Mon Sep 17 00:00:00 2001
From: Antz <antzz@protonmail.ch>
Date: Sat, 29 Aug 2020 14:54:19 +0200
Subject: [PATCH] 90crypt: make `rd.luks.key` usable with encrypted keydev.

Introduce prefix `keysource:` for the values of `rd.luks.partuuid`,
`rd.luks.serial` and `rd.luks.uuid`.
If specified, ask for passphrase instead of waiting for keydevs to come
online.
---
 dracut.cmdline.7.asc               | 45 ++++++++++++++++++++++++++++++++++++++
 modules.d/90crypt/cryptroot-ask.sh | 18 +++++++++++++--
 modules.d/90crypt/parse-crypt.sh   | 43 ++++++++++++++++++++++++++----------
 3 files changed, 92 insertions(+), 14 deletions(-)

diff --git a/dracut.cmdline.7.asc b/dracut.cmdline.7.asc
index c37d427e..dbbbfed0 100644
--- a/dracut.cmdline.7.asc
+++ b/dracut.cmdline.7.asc
@@ -302,6 +302,8 @@ crypto LUKS
     The comparisons also matches, if _<luks uuid>_ is only the beginning of the
     LUKS UUID, so you don't have to specify the full UUID.
     This parameter can be specified multiple times.
+    _<luks uuid>_ may be prefixed by the keyword `keysource:`, see
+    _rd.luks.key_ below.
 
 **rd.luks.allow-discards=**__<luks uuid>__::
     Allow  using  of discards (TRIM) requests for LUKS partitions with the given
@@ -397,6 +399,49 @@ head -32c /dev/urandom > rootkey.key
 cryptsetup --batch-mode --key-file rootkey.key \
            luksFormat /dev/sda47
 --
+
+You can also use regular key files on an encrypted _keydev_.
+
+Compared to using GPG encrypted keyfiles on an unencrypted
+device this provides the following advantages:
+
+- you can unlock your disk(s) using multiple passphrases
+- better security by not loosing the key stretching mechanism
+
+To use an encrypted _keydev_ you *must* ensure that it becomes
+available by using the keyword `keysource`, e.g.
+`rd.luks.uuid=keysource:aaaa`
+_aaaa_ being the uuid of the encrypted _keydev_.
+
+Example:
+
+Lets assume you have three disks _A_, _B_ and _C_ with the uuids
+_aaaa_, _bbbb_ and _cccc_. +
+You want to unlock _A_ and _B_ using keyfile _keyfile_. +
+The unlocked volumes be _A'_, _B'_ and _C'_ with the uuids
+_AAAA_, _BBBB_ and _CCCC_. +
+_keyfile_ is saved on _C'_ as _/keyfile_.
+
+One luks keyslot of each _A_, _B_ and _C_ is setup with a
+passphrase. +
+Another luks keyslot of each _A_ and _B_ is setup with _keyfile_.
+
+To boot this configuration you could use:
+[listing]
+--
+rd.luks.uuid=aaaa
+rd.luks.uuid=bbbb
+rd.luks.uuid=keysource:cccc
+rd.luks.key=/keyfile:UUID=CCCC
+--
+Dracut asks for the passphrase for _C_ and uses the
+keyfile to unlock _A_ and _B_. +
+If getting the passphrase for _C_ fails it falls back to
+asking for the passphrases for _A_ and _B_.
+
+If you want _C'_ to stay unlocked, specify a luks name for
+it, e.g. `rd.luks.name=cccc=mykeys`, otherwise it gets closed
+when not needed anymore.
 ===============================
 
 MD RAID
diff --git a/modules.d/90crypt/cryptroot-ask.sh b/modules.d/90crypt/cryptroot-ask.sh
index 97047ae9..19d2bcb4 100755
--- a/modules.d/90crypt/cryptroot-ask.sh
+++ b/modules.d/90crypt/cryptroot-ask.sh
@@ -20,8 +20,11 @@ fi
 # default luksname - luks-UUID
 luksname=$2
 
+# is_keysource - ask for passphrase even if a rd.luks.key argument is set
+is_keysource=${3:-0}
+
 # number of tries
-numtries=${3:-10}
+numtries=${4:-10}
 
 # TODO: improve to support what cmdline does
 if [ -f /etc/crypttab ] && getargbool 1 rd.luks.crypttab -d -n rd_NO_CRYPTTAB; then
@@ -137,6 +140,8 @@ if [ -n "$luksfile" -a "$luksfile" != "none" -a -e "$luksfile" ]; then
     if cryptsetup --key-file "$luksfile" $cryptsetupopts luksOpen "$device" "$luksname"; then
         ask_passphrase=0
     fi
+elif [ "$is_keysource" -ne 0 ]; then
+    info "Asking for passphrase because $device is a keysource."
 else
     while [ -n "$(getarg rd.luks.key)" ]; do
         if tmp=$(getkey /tmp/luks.keys $device); then
@@ -151,7 +156,7 @@ else
             info "No key found for $device.  Will try $numtries time(s) more later."
             initqueue --unique --onetime --settled \
                 --name cryptroot-ask-$luksname \
-                $(command -v cryptroot-ask) "$device" "$luksname" "$(($numtries-1))"
+                $(command -v cryptroot-ask) "$device" "$luksname" "$is_keysource" "$(($numtries-1))"
             exit 0
         fi
         unset tmp
@@ -178,6 +183,15 @@ if [ $ask_passphrase -ne 0 ]; then
     unset _timeout
 fi
 
+if [ "$is_keysource" -ne 0 -a ${luksname##luks-} != "$luksname" ]; then
+    luks_close="$(command -v cryptsetup) close"
+    {
+        printf -- '[ -e /dev/mapper/%s ] && ' "$luksname"
+        printf -- '%s "%s"\n' "$luks_close" "$luksname"
+    } >> "$hookdir/cleanup/31-crypt-keysource.sh"
+    unset luks_close
+fi
+
 unset device luksname luksfile
 
 # mark device as asked
diff --git a/modules.d/90crypt/parse-crypt.sh b/modules.d/90crypt/parse-crypt.sh
index f6911cc8..4e899fed 100755
--- a/modules.d/90crypt/parse-crypt.sh
+++ b/modules.d/90crypt/parse-crypt.sh
@@ -49,6 +49,12 @@ else
     if [ -n "$PARTUUID" ]; then
         for uuid in $PARTUUID; do
 
+            is_keysource=0
+            _uuid=$uuid
+            uuid=${uuid#keysource:}
+            [ $uuid != $_uuid ] && is_keysource=1
+            unset _uuid
+
             uuid=${uuid##luks-}
             if luksname=$(_cryptgetargsname "rd.luks.name=$uuid="); then
                 luksname="${luksname#$uuid=}"
@@ -61,7 +67,7 @@ else
                     printf -- 'ENV{ID_PART_ENTRY_UUID}=="*%s*", ' "$uuid"
                     printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)"
                     printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)"
-                    printf -- '$env{DEVNAME} %s %s"\n' "$luksname" "$tout"
+                    printf -- '$env{DEVNAME} %s %s"\n' "$luksname" "$is_keysource" "$tout"
                 } >> /etc/udev/rules.d/70-luks.rules.new
             else
                 luksname=$(dev_unit_name "$luksname")
@@ -81,6 +87,12 @@ else
     elif [ -n "$SERIAL" ]; then
         for serialid in $SERIAL; do
 
+            is_keysource=0
+            _serialid=$serialid
+            serialid=${serialid#keysource:}
+            [ $serialid != $_serialid ] && is_keysource=1
+            unset _serialid
+
             serialid=${serialid##luks-}
             if luksname=$(_cryptgetargsname "rd.luks.name=$serialid="); then
                 luksname="${luksname#$serialid=}"
@@ -93,7 +105,7 @@ else
                     printf -- 'ENV{ID_SERIAL_SHORT}=="*%s*", ' "$serialid"
                     printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)"
                     printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)"
-                    printf -- '$env{DEVNAME} %s %s"\n' "$luksname" "$tout"
+                    printf -- '$env{DEVNAME} %s %s"\n' "$luksname" "$is_keysource" "$tout"
                 } >> /etc/udev/rules.d/70-luks.rules.new
             else
                 luksname=$(dev_unit_name "$luksname")
@@ -113,6 +125,12 @@ else
     elif [ -n "$LUKS" ]; then
         for luksid in $LUKS; do
 
+            is_keysource=0
+            _luksid=$luksid
+            luksid=${luksid#keysource:}
+            [ $luksid != $_luksid ] && is_keysource=1
+            unset _luksid
+
             luksid=${luksid##luks-}
             if luksname=$(_cryptgetargsname "rd.luks.name=$luksid="); then
                 luksname="${luksname#$luksid=}"
@@ -126,7 +144,7 @@ else
                     printf -- 'ENV{ID_FS_UUID}=="*%s*", ' "$luksid"
                     printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)"
                     printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)"
-                    printf -- '$env{DEVNAME} %s %s"\n' "$luksname" "$tout"
+                    printf -- '$env{DEVNAME} %s %s %s"\n' "$luksname" "$is_keysource" "$tout"
                 } >> /etc/udev/rules.d/70-luks.rules.new
             else
                 luksname=$(dev_unit_name "$luksname")
@@ -143,15 +161,16 @@ else
                 fi
             fi
 
-            uuid=$luksid
-            while [ "$uuid" != "${uuid#*-}" ]; do uuid=${uuid%%-*}${uuid#*-}; done
-            printf -- '[ -e /dev/disk/by-id/dm-uuid-CRYPT-LUKS?-*%s*-* ] || exit 1\n' $uuid \
-                >> "$hookdir/initqueue/finished/90-crypt.sh"
-
-            {
-                printf -- '[ -e /dev/disk/by-uuid/*%s* ] || ' $luksid
-                printf -- 'warn "crypto LUKS UUID "%s" not found"\n' $luksid
-            } >> "$hookdir/emergency/90-crypt.sh"
+            if [ $is_keysource -eq 0 ]; then
+                uuid=$luksid
+                while [ "$uuid" != "${uuid#*-}" ]; do uuid=${uuid%%-*}${uuid#*-}; done
+                printf -- '[ -e /dev/disk/by-id/dm-uuid-CRYPT-LUKS?-*%s*-* ] || exit 1\n' $uuid \
+                    >> "$hookdir/initqueue/finished/90-crypt.sh"
+                {
+                    printf -- '[ -e /dev/disk/by-uuid/*%s* ] || ' $luksid
+                    printf -- 'warn "crypto LUKS UUID "%s" not found"\n' $luksid
+                } >> "$hookdir/emergency/90-crypt.sh"
+            fi
         done
     elif getargbool 0 rd.auto; then
         if [ -z "$DRACUT_SYSTEMD" ]; then