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
orkeyfile-size
options. Unfortunately, specifying those options will also affect howcryptsetup
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 thecrypttab
options it should set totries=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.