Script for LUKS auto keyfile decryption/fallback to passphrase

LUKS provides a utility, namely passdev, to allow users to use a keyfile from certain external drives to automatically decrypt LUKS drives. However, passdev does not provide a fallback-to-passphrase option when it failed to find the correct keyfile, instead it simply loops indefinitely. Such functionality needs to be implemented by users themselves.

Assuming that you have a working setup of LUKS decryption with passdev, which will have similar entries in /etc/crypttab:

sda3_crypt UUID=your-drive-uuid /dev/disk/by-label/SOMELABEL:/path/to/some/file:timeout luks,discard,keyscript=/lib/cryptsetup/scripts/passdev

The script below will allow to read entries from /etc/crypttab, having a timeout for detecting the flash drive hosting the keyfile, and if failed using keyfile for decryption for whatever reason, fall back to passphrase. After certain number of tries of unlocking failed, the script will reboot the host.

This has been tested on Debian bullseye stable, and not guaranteed to work on other distros (espeically with systemd-cryptsetup due to different syntax of /etc/crypttab). There are some ceveats of this script as well:

  • It does not support keyfile-offset or keyfile-size options. Unfortunately, specifying those options will also affect how cryptsetup reads passphrase from stdin, and it will attempt to truncate/seek passphrases with offsets specified by those options, which will fail.
  • Somehow when cryptsetup exceeds the number of failed tries (by default it’s 3), it will simply restart the keyscript, but also reset the failed attempts to 0. To overcome this, by default this script will reboot with 2 failed unlocking attempts. For example, if one wants to limit tries to 3, then in the crypttab options it should set to tries=4.
#!/bin/sh

keydev=$(echo $CRYPTTAB_KEY | cut -d':' -f1)
keyfile=$(echo $CRYPTTAB_KEY | cut -d':' -f2)
timeout=$(echo $CRYPTTAB_KEY | cut -d':' -f3)

[ ! -z $tries ] && $(( $CRYPTTAB_OPTION_tries - 1 )) || tries=2

if [ $CRYPTTAB_TRIED -ge $tries ]; then
        reboot -f
fi

[ -z $timeout ] && timeout=15
endtime=$(( $(date +%s) + timeout ))

tmppath="/tmp/lukspass-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c6)"
[ ! -d $tmppath ] && mkdir -p $tmppath

while [ ! -b $keydev ] && [ $(date +%s) -lt $endtime ]; do
        sleep 1
done

if mount -o ro $keydev $tmppath 2>/dev/null; then
        if [ -f $tmppath$keyfile ]; then
                cat $tmppath$keyfile
                umount $tmppath && rm -rf $tmppath
                exit
        fi
        umount $tmppath
fi

rm -rf $tmppath
/lib/cryptsetup/askpass "Please enter passphrase to unlock $CRYPTTAB_NAME: "

Save the script to somewhere (e.g., /bin/keyscript), chmod +x to it, change /etc/crypttab to point the keyscript to /bin/keyscript, and run update-initramfs -u to update image.

Also, if one uses FAT32 flash drives to store key files, likely those lines in /etc/initramfs-tools/modules are also needed:

vfat
nls_cp437
nls_ascii

After adding those lines, run update-initramfs -u again to update image.

标签:

更新时间: