Hi List! I spent sereval hours today figuring out how to do an useful RAID with full-disk encryption for headless servers that will use an usb stick to read the keyfile from and falling back to askpass to enter the pw from console if no stick is found and also managed to silence systemd. This post has the intention of being a reference post of the type "Im finally done after a day of doing stuff in a vm and hopefully this will save someone a few hours to get a similar setup set-up." Also included: a patch for cryptoroot to make it fall back to askpass, if no keyfile can be found on usb-sticks. There are many outdated and incomplete guides, mostly written for oldoldold-stable, which will make you do more than necessary. :P The fewer changes to a system, the better, IMO. So... I ran the default netinstall iso and partitioned the VM as follows: /dev/sda1: /boot /dev/sda2: RAID0: md0 /dev/md0: crypto_LUKS: md0_crypt (defaults + passphrase) /dev/mapper/md0_crypt: ext4: / Ill test a more useful raid level on the live setup, once the hardware is assembled, but I expect it to behave identically. After the installer finished, and rebooted the VM, I got to enter the passphrase for the first time, it booted fine. An encrypted root-fs on a raid seems to work out of the box by now - no need to fiddle with grub as some guides suggest! The next step was to get the passwordless usb-stick (/dev/sdb) auto-decryption working. (Again: it works nearly out of the boy by now, there are MANY outdated guides out there suggesting you to download some unnecessary scripts) It all boiled down to: # mkfs.ext4 -L keys /dev/sdb # mount LABEL=keys /mnt # echo -n "asdf" > /mnt/root.keys # cryptsetup luksAddKey /dev/md0 /mnt/root.key # umount /mnt Seting the following line in /etc/crypttab --- md0_crypt UUID=[...] /dev/disk/by-label/keys:/root.key:5 luks,initramfs,keyscript=/lib/cryptsetup/scripts/passdev,tries=2 --- # update-initramfs -tuck all # update-grub # reboot Now we are in a state that debian will boot automatically, unlocking the encrypted root filesystem on a raid without any user-interaction. Sweet? Somehow... systemd has a tendency to complain and slow down the boot process by 1:30 min because it fails to handle the new crypttab entry, but after the timeout, you get presented with a login. To silence systemd: # touch "/etc/systemd/system/systemd-cryptsetup@md0_crypt.service" # reboot This will override the generated systemd-cryptsetup services. After the reboot, we get booted instantly, without systemd complaining about anything. Now what happens, if you dont have your usb-stick connected to the machine when rebooting? Well... passdev will wait for 5 sec, then fail, retry, fail again, ... There doesnt seem to be any kind of fallback to console-pw-entry. This should not really matter, but what if for some reason the usb controller or usb-stick fails and renders the machine unbootable, because you dont have a replacement at hand? That would suck, wouldnt it? Therefore I extended/hacked the original /usr/share/initramfs-tools/scripts/local-top/cryptroot script a little bit. # cp /usr/share/initramfs-tools/scripts/local-top/cryptroot /etc/initramfs-tools/scripts/local-top/cryptroot *edit stuff* The patch is attached. The intention was to try to use the usb stick and if passdev times out, fall back to passphrase-entry in the console via askpass. I added the option "cryptaskpassfallback" that should have been settable via /etc/crypttab as another option, but for some reason it was not passed from /etc/crypttab and by that time, my motivation wasnt really that high anyomre to investigate it further, and i simply hardcoded the default to ="yes" instead of ="". The actual fallback feels a little hacky too and im not really happy about it, as Im not really sure, if I didnt create any unwanted side-effects, by calling setup_mapping() once more. It would be awesome, if some maintainer of the corresponding packages would consider including this functionality, maybe a little less hacky. :) Anyway. After applying the patch, all seems done. # update-initramfs -tuck all # reboot Will either insta-boot with the usb stick accessible, or ask for a passphrase, and systemd wont complain. Thats what I wanted for myself and it seems to work as expected... Just wanted to share this. Maybe this will help someone in the future. :) br, Jan -- I only read plaintext emails.
#!/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
--- /usr/share/initramfs-tools/scripts/local-top/cryptroot 2017-05-09 13:50:59.000000000 +0200 +++ /mnt/cryptroot 2017-12-16 02:26:34.401812974 +0100 @@ -26,6 +26,9 @@ # source for log_*_msg() functions, see LP: #272301 . /scripts/functions +# define $askpass +askpass="/lib/cryptsetup/askpass" + # # Helper functions # @@ -75,6 +78,7 @@ cryptveracrypt="" cryptrootdev="" cryptdiscard="" + cryptaskpassfallback="yes" CRYPTTAB_OPTIONS="" local IFS=" ," @@ -144,6 +148,9 @@ discard) cryptdiscard="yes" ;; + askpassfallback) + cryptaskpassfallback="yes" + ;; esac PARAM="${x%=*}" if [ "$PARAM" = "$x" ]; then @@ -188,6 +195,11 @@ 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 @@ -195,7 +207,7 @@ else diskname="$cryptsource ($crypttarget)" fi - cryptkeyscript="/lib/cryptsetup/askpass" + cryptkeyscript=$askpass cryptkey="Please unlock disk $diskname: " elif ! type "$cryptkeyscript" >/dev/null; then message "cryptsetup ($crypttarget): error - script \"$cryptkeyscript\" missing" @@ -308,7 +320,17 @@ if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \ $cryptkeyscript "$cryptkey" | $cryptopen; then message "cryptsetup ($crypttarget): cryptsetup failed, bad password or options?" - continue + + # 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
Attachment:
signature.asc
Description: OpenPGP digital signature