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
signature.asc
Description: OpenPGP digital signature