Hello everyone!

I'm currently trying to find a method to decrypt my rootfs at boot time with a 
keyfile on debian stretch.

I've successfully implemented a method ([1] and [2]) which uses a custom script 
to read the first 2048bit
from the usbkey memory to decrypt the disk, but I actually need to store the 
keyfile on a filesystem.

Someone [3] told me about an old post on debian-user mailing list: 
https://lists.debian.org/debian-user/2017/12/msg00523.html
That's exactly what I need. I don't mind patching cryptroot script (appended 
[4]) to do it, so I decided to test it.
I've create a VM (kvm/qemu), partitioned the disks this way (a classic crypted 
debian LVM partitioning):


   root@debian:~ $ lsblk
   NAME                    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
   sda                       8:0    0   1.2G  0 disk 
   vdb                       8:16   0  20.8G  0 disk
   ├─vdb1                    8:17   0   243M  0 part  /boot
   ├─vdb2                    8:18   0     1K  0 part 
   └─vdb5                    8:21   0  20.5G  0 part 
        └─vdb5_crypt         254:0    0 20.5G  0 crypt
    ├─debian--vg-root   254:1    0   18G  0 lvm   /
         └─debian--vg-swap_1 254:2    0  2.3G  0 lvm   [SWAP]


Configured as in the mailing list tutorial, updated initramfs, updated grub, 
rebooted.
I've followed the tutorial step by step, but still at boot time the system hung:


    Reading all physical volumes. This may take a while...
   WARNING: Failed to connect to lvmetad. Falling back to device scanning.
   WARNING: Failed to connect to lvmetad. Falling back to device scanning.
   Reading all physical volumes. This may take a while...
   WARNING: Failed to connect to lvmetad. Falling back to device scanning.
   ...
   ALERT! /dev/vdb5 does not exist.
                Check cryptopts=source bootarg: cat /proc/cmdline
                or missing modules. devices: cat /proc/modules: ls /dev
   -r Dropping to a shell. Will skip /dev/vdb5 if you can't fix.
   Busybox v1.22.1 (Debian 1:1.22.0-19+b3) built-in shell (ash)
   Enter 'help' fro a list of built-int commands.
   (initramfs)
   (initramfs) _


It seems unable to find the disk to decrypt (/dev/vdb5).
I was so sad, I've thought I've found the right way...
So I digged more and suddenly I've thought maybe it could be my LVM partition 
scheme.
With no real expectations I've created another VM, with the following partition 
scheme:


NAME      MAJ:MIN      RM      SIZE     RO     TYPE     MOUNTPOINT
sr0                     11:0           1    1024M      0        rom
vda                  254:0           0         20G     0         disk
|_vda1             254:0           0      953M      0        part       /boot
|_vda2             254:0           0           1K      0        part
|_vda5             254:0           0         17G      0       part
  |_vda5_crypt   254:0           0         17G      0       crypt       /
|_vda6             254:0           0         2G        0       part
   |_vda6_crypt   254:0           0         2G        0      crypt        [SWAP]


Configured, updated, rebooted.
And it works!

So I'm a bit stuck here and I've decided to write to this mailing list hoping 
someone may illuminate me,
why does it hung with LVM partitioning?
Could there it be some tricks to make it work?

Now to my point of view I think it could be a missing or erroneous cmdline 
parameter, or/and the cryptroot script in the tutorial (appended [4]) maybe is 
causing this. But I don't know yet how to fix this.

The cmdline I used with LVM partitioning is:
    BOOT_IMAGE=/vmlinuz-4.9.0-7-amd64  root=/dev/mapper/debian--vg-root ro 
cryptopts=target=vda5_crypt,source=/dev/disk/by-uuid/b59bb85d-abec-4562-90c3-1f3d0ab34036

The cmdline used without LVM:
     BOOT_IMAGE=/vmlinuz-4.9.0-7-amd64  
root=UUID=b59bb85d-abec-4562-90c3-1f3d0ab34036 ro quiet

I also tried to decrypt LVM partitioned device from initramfs panic shell, but 
I succeeded only on an actual machine, on a virtual machine the devices didn't 
appear (no /dev/vda).

A partitioning scheme without LVM may be ok to my usecase, but I would like to 
understand why it does not work and if it can be fixed.
Thank you for your time,

Lorenzo

REF:
[1]  
https://www.oxygenimpaired.com/debian-lenny-luks-encrypted-root-hidden-usb-keyfile
[2]  
https://www.oxygenimpaired.com/ubuntu-with-grub2-luks-encrypted-lvm-root-hidden-usb-keyfile
[3]  
https://unix.stackexchange.com/questions/471854/decrypt-root-device-at-boot-with-keyfile-on-usb-debian-stretch
[4]  cryptroot script:


#!/bin/sh

PREREQ="cryptroot-prepare"

#
# Standard initramfs preamble
#
prereqs()
{
        # Make sure that cryptroot is run last in local-top
        for req in $(dirname $0)/*; do
                script=${req##*/}
                if [ $script != cryptroot ]; then
                        echo $script
                fi
        done
}

case $1 in
prereqs)
        prereqs
        exit 0
        ;;
esac

# source for log_*_msg() functions, see LP: #272301
. /scripts/functions

# define $askpass
askpass="/lib/cryptsetup/askpass"

#
# Helper functions
#
message()
{
        if [ -x /bin/plymouth ] && plymouth --ping; then
                plymouth message --text="$@"
        else
                echo "$@" >&2
        fi
        return 0
}

udev_settle()
{
        # Wait for udev to be ready, see https://launchpad.net/bugs/85640
        if command -v udevadm >/dev/null 2>&1; then
                udevadm settle --timeout=30
        elif command -v udevsettle >/dev/null 2>&1; then
                udevsettle --timeout=30
        fi
        return 0
}

parse_options()
{
        local cryptopts
        cryptopts="$1"

        if [ -z "$cryptopts" ]; then
                return 1
        fi

        # Defaults
        cryptcipher=aes-cbc-essiv:sha256
        cryptsize=256
        crypthash=ripemd160
        crypttarget=cryptroot
        cryptsource=""
        cryptheader=""
        cryptlvm=""
        cryptkeyscript=""
        cryptkey="" # This is only used as an argument to an eventual keyscript
        cryptkeyslot=""
        crypttries=3
        crypttcrypt=""
        cryptveracrypt=""
        cryptrootdev=""
        cryptdiscard=""
        cryptaskpassfallback="yes"
        CRYPTTAB_OPTIONS=""

        local IFS=" ,"
        for x in $cryptopts; do
                case $x in
                hash=*)
                        crypthash=${x#hash=}
                        ;;
                size=*)
                        cryptsize=${x#size=}
                        ;;
                cipher=*)
                        cryptcipher=${x#cipher=}
                        ;;
                target=*)
                        crypttarget=${x#target=}
                        export CRYPTTAB_NAME="$crypttarget"
                        ;;
                source=*)
                        cryptsource=${x#source=}
                        if [ ${cryptsource#UUID=} != $cryptsource ]; then
                                
cryptsource="/dev/disk/by-uuid/${cryptsource#UUID=}"
                        elif [ ${cryptsource#LABEL=} != $cryptsource ]; then
                                cryptsource="/dev/disk/by-label/$(printf '%s' 
"${cryptsource#LABEL=}" | sed 's,/,\\x2f,g')"
                        fi
                        export CRYPTTAB_SOURCE="$cryptsource"
                        ;;
                header=*)
                        cryptheader=${x#header=}
                        if [ ! -e "$cryptheader" ] && [ -e 
"/conf/conf.d/cryptheader/$cryptheader" ]; then
                                
cryptheader="/conf/conf.d/cryptheader/$cryptheader"
                        fi
                        export CRYPTTAB_HEADER="$cryptheader"
                        ;;
                lvm=*)
                        cryptlvm=${x#lvm=}
                        ;;
                keyscript=*)
                        cryptkeyscript=${x#keyscript=}
                        ;;
                key=*)
                        if [ "${x#key=}" != "none" ]; then
                                cryptkey=${x#key=}
                        fi
                        export CRYPTTAB_KEY="$cryptkey"
                        ;;
                keyslot=*)
                        cryptkeyslot=${x#keyslot=}
                        ;;
                tries=*)
                        crypttries="${x#tries=}"
                        case "$crypttries" in
                          *[![:digit:].]*)
                                crypttries=3
                                ;;
                        esac
                        ;;
                tcrypt)
                        crypttcrypt="yes"
                        ;;
                veracrypt)
                        cryptveracrypt="--veracrypt"
                        ;;
                rootdev)
                        cryptrootdev="yes"
                        ;;
                discard)
                        cryptdiscard="yes"
                        ;;
                askpassfallback)
                        cryptaskpassfallback="yes"
                        ;;
                esac
                PARAM="${x%=*}"
                if [ "$PARAM" = "$x" ]; then
                        VALUE="yes"
                else
                        VALUE="${x#*=}"
                fi
                CRYPTTAB_OPTIONS="$CRYPTTAB_OPTIONS $PARAM"
                eval export CRYPTTAB_OPTION_$PARAM="\"$VALUE\""
        done
        export CRYPTTAB_OPTIONS

        if [ -z "$cryptsource" ]; then
                message "cryptsetup ($crypttarget): source parameter missing"
                return 1
        fi
        return 0
}

activate_vg()
{
        # Sanity checks
        if [ ! -x /sbin/lvm ]; then
                message "cryptsetup ($crypttarget): lvm is not available"
                return 1
        fi

        # Detect and activate available volume groups
        /sbin/lvm vgscan
        /sbin/lvm vgchange -a y --sysinit
        return $?
}

setup_mapping()
{
        local opts count cryptopen cryptremove NEWROOT
        opts="$1"

        if [ -z "$opts" ]; then
                return 0
        fi

        parse_options "$opts" || return 1

        # disable cryptkeyscript - fall back to askpass.
        if [ -n "$do_fallback" ]; then
                cryptkeyscript=""
        fi

        if [ -z "$cryptkeyscript" ]; then
                if [ ${cryptsource#/dev/disk/by-uuid/} != $cryptsource ]; then
                        # UUIDs are not very helpful
                        diskname="$crypttarget"
                else
                        diskname="$cryptsource ($crypttarget)"
                fi
                cryptkeyscript=$askpass
                cryptkey="Please unlock disk $diskname: "
        elif ! type "$cryptkeyscript" >/dev/null; then
                message "cryptsetup ($crypttarget): error - script 
\"$cryptkeyscript\" missing"
                return 1
        fi

        if [ "$cryptkeyscript" = "cat" ] && [ "${cryptkey#/root/}" != 
"$cryptkey" ]; then
                # skip the mapping if the root FS is not mounted yet
                sed -rn 's/^\s*[^#]\S*\s+(\S+)\s.*/\1/p' /proc/mounts | grep 
-Fxq "$rootmnt" || return 1
                # substitute the "/root" prefix by the real root FS mountpoint 
otherwise
                cryptkey="${rootmnt}/${cryptkey#/root/}"
        fi

        if [ -n "$cryptheader" ] && ! type "$cryptheader" >/dev/null; then
                message "cryptsetup ($crypttarget): error - LUKS header 
\"$cryptheader\" missing"
                return 1
        fi

        # The same target can be specified multiple times
        # e.g. root and resume lvs-on-lvm-on-crypto
        if [ -e "/dev/mapper/$crypttarget" ]; then
                return 0
        fi

        modprobe -q dm_crypt

        # Make sure the cryptsource device is available
        if [ ! -e $cryptsource ]; then
                activate_vg
        fi

        # If the encrypted source device hasn't shown up yet, give it a
        # little while to deal with removable devices

        # the following lines below have been taken from
        # /usr/share/initramfs-tools/scripts/local, as suggested per
        # https://launchpad.net/bugs/164044
        if [ ! -e "$cryptsource" ]; then
                log_begin_msg "Waiting for encrypted source device..."

                # Default delay is 180s
                if [ -z "${ROOTDELAY}" ]; then
                        slumber=180
                else
                        slumber=${ROOTDELAY}
                fi

                slumber=$(( ${slumber} * 10 ))
                while [ ! -e "$cryptsource" ]; do
                        # retry for LVM devices every 10 seconds
                        if [ ${slumber} -eq $(( ${slumber}/100*100 )) ]; then
                                activate_vg
                        fi

                        /bin/sleep 0.1
                        slumber=$(( ${slumber} - 1 ))
                        [ ${slumber} -gt 0 ] || break
                done

                if [ ${slumber} -gt 0 ]; then
                        log_end_msg 0
                else
                        log_end_msg 1 || true
                fi
        fi
        udev_settle

        # We've given up, but we'll let the user fix matters if they can
        if [ ! -e "${cryptsource}" ]; then

                echo "  ALERT! ${cryptsource} does not exist."
                echo "  Check cryptopts=source= bootarg: cat /proc/cmdline"
                echo "  or missing modules, devices: cat /proc/modules; ls /dev"
                panic -r "Dropping to a shell. Will skip ${cryptsource} if you 
can't fix."
        fi

        if [ ! -e "${cryptsource}" ]; then
                return 1
        fi


        # Prepare commands
        cryptopen="/sbin/cryptsetup -T 1"
        if [ "$cryptdiscard" = "yes" ]; then
                cryptopen="$cryptopen --allow-discards"
        fi
        if [ -n "$cryptheader" ]; then
                cryptopen="$cryptopen --header=$cryptheader"
        fi
        if [ -n "$cryptkeyslot" ]; then
                cryptopen="$cryptopen --key-slot=$cryptkeyslot"
        fi
        if /sbin/cryptsetup isLuks ${cryptheader:-$cryptsource} >/dev/null 
2>&1; then
                cryptopen="$cryptopen open --type luks $cryptsource 
$crypttarget --key-file=-"
        elif [ "$crypttcrypt" = "yes" ]; then
                cryptopen="$cryptopen open --type tcrypt $cryptveracrypt 
$cryptsource $crypttarget"
        else
                cryptopen="$cryptopen -c $cryptcipher -s $cryptsize -h 
$crypthash open --type plain $cryptsource $crypttarget --key-file=-"
        fi
        cryptremove="/sbin/cryptsetup remove $crypttarget"
        NEWROOT="/dev/mapper/$crypttarget"

        # Try to get a satisfactory password $crypttries times
        count=0
        while [ $crypttries -le 0 ] || [ $count -lt $crypttries ]; do
                export CRYPTTAB_TRIED="$count"
                count=$(( $count + 1 ))

                if [ ! -e "$NEWROOT" ]; then
                        if ! crypttarget="$crypttarget" 
cryptsource="$cryptsource" \
                             $cryptkeyscript "$cryptkey" | $cryptopen; then
                                message "cryptsetup ($crypttarget): cryptsetup 
failed, bad password or options?"

                                # if not askpass, fall back to askpass on fail.
                                if [ -z "$cryptaskpassfallback" ]; then
                                        continue
                                elif [ "$cryptkeyscript" = "$askpass" ]; then
                                        continue
                                else
                                        export 
do_fallback="$cryptaskpassfallback"
                                        setup_mapping "$1"
                                        return
                                fi
                        fi
                fi

                if [ ! -e "$NEWROOT" ]; then
                        message "cryptsetup ($crypttarget): unknown error 
setting up device mapping"
                        return 1
                fi

                #FSTYPE=''
                #eval $(fstype < "$NEWROOT")
                FSTYPE="$(/sbin/blkid -s TYPE -o value "$NEWROOT")"

                # See if we need to setup lvm on the crypto device
                #if [ "$FSTYPE" = "lvm" ] || [ "$FSTYPE" = "lvm2" ]; then
                if [ "$FSTYPE" = "LVM_member" ] || [ "$FSTYPE" = "LVM2_member" 
]; then
                        if [ -z "$cryptlvm" ]; then
                                message "cryptsetup ($crypttarget): lvm fs 
found but no lvm configured"
                                return 1
                        elif ! activate_vg; then
                                # disable error message, LP: #151532
                                #message "cryptsetup ($crypttarget): failed to 
setup lvm device"
                                return 1
                        fi

                        # Apparently ROOT is already set in /conf/param.conf for
                        # flashed kernels at least. See bugreport #759720.
                        if [ -f /conf/param.conf ] && grep -q "^ROOT=" 
/conf/param.conf; then
                                NEWROOT=$(sed -n 's/^ROOT=//p' /conf/param.conf)
                        else
                                NEWROOT=${cmdline_root:-/dev/mapper/$cryptlvm}
                                if [ "$cryptrootdev" = "yes" ]; then
                                        # required for lilo to find the root 
device
                                        echo "ROOT=$NEWROOT" >>/conf/param.conf
                                fi
                        fi
                        #eval $(fstype < "$NEWROOT")
                        FSTYPE="$(/sbin/blkid -s TYPE -o value "$NEWROOT")"
                fi

                #if [ -z "$FSTYPE" ] || [ "$FSTYPE" = "unknown" ]; then
                if [ -z "$FSTYPE" ]; then
                        message "cryptsetup ($crypttarget): unknown fstype, bad 
password or options?"
                        udev_settle
                        $cryptremove
                        continue
                fi

                # decrease $count by 1, apparently last try was successful.
                count=$(( $count - 1 ))

                message "cryptsetup ($crypttarget): set up successfully"
                break
        done

        failsleep=60 # make configurable later?

        if [ "$cryptrootdev" = "yes" ] && [ $crypttries -gt 0 ] && [ $count -ge 
$crypttries ]; then
                message "cryptsetup ($crypttarget): maximum number of tries 
exceeded"
                message "cryptsetup: going to sleep for $failsleep seconds..."
                sleep $failsleep
                exit 1
        fi

        udev_settle
        return 0
}

#
# Begin real processing
#

# Do we have any kernel boot arguments?
cmdline_cryptopts=''
unset cmdline_root
for opt in $(cat /proc/cmdline); do
        case $opt in
        cryptopts=*)
                opt="${opt#cryptopts=}"
                if [ -n "$opt" ]; then
                        if [ -n "$cmdline_cryptopts" ]; then
                                cmdline_cryptopts="$cmdline_cryptopts $opt"
                        else
                                cmdline_cryptopts="$opt"
                        fi
                fi
                ;;
        root=*)
                opt="${opt#root=}"
                case $opt in
                /*) # Absolute path given. Not lilo major/minor number.
                        cmdline_root=$opt
                        ;;
                *) # lilo major/minor number (See #398957). Ignore
                esac
                ;;
        esac
done

if [ -n "$cmdline_cryptopts" ]; then
        # Call setup_mapping separately for each possible cryptopts= setting
        for cryptopt in $cmdline_cryptopts; do
                setup_mapping "$cryptopt"
        done
        exit 0
fi

# Do we have any settings from the /conf/conf.d/cryptroot file?
if [ -r /conf/conf.d/cryptroot ]; then
        while read mapping <&3; do
                setup_mapping "$mapping" 3<&-
        done 3< /conf/conf.d/cryptroot
fi

exit 0

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to