Unlocking LUKS with a USB key

This guide offers a method for unlocking a Red Hat Enterprise Linux / Rocky Linux LUKS encrypted partition with a USB key, that to the casual observer, appears blank. This guide is offered with no warranty and I accept no liability if you turn your computer in to a brick!

Due to the bleeding-edge nature of changes in Fedora, this guide is only recommended for use on Red Hat Enterprise Linux 8 and its clones (Rocky Linux / Alma Linux). Use on Fedora at your own risk!

You will need a spare USB memory stick that you are happy to sacrifice. This method fills the stick with random data to ensure a lost stick simply appears as blank (i.e. no partition tables), or as random data - if someone knows what they are looking for. They will still be unable to extract your key if you vary some of the settings outlined below. This method also ties the unlocking to the ID of a particular USB key, thus providing an extra layer of security.

It is assumed you have a single disc with a single LUKS encrypted partition already present that you are unlocking with a passphrase. Unlocking a second (or third) LUKS volume requires the script modifications outlines in section 5.


1: Create a key file

1.1 Fill a USB memory stick with random data via this command: dd if=/dev/urandom of=/dev/sdb bs=1

This assumes your memory stick is /dev/sdb. CAUTION: The 'dd' command will happily wipe your hard drive if you set the wrong /dev/sd?.

It is noted that the entropy pool for creating random data is not particularly large on some computers. A laptop with a TPM and rngd running will have a larger pool than a server.

1.2 Extract a "key" from the random data on the USB stick with this command: dd if=/dev/sdb of=luks-secret.key bs=1 count=4096

The above line will extract 4096 bytes of random data from the USB stick. This could be changed to bs=1024 count=2 (for example) to extract data from a different part of the random noise. If you make a change like this, keep a note of it for step 2.3

1.3 Add the key to your LUKS partition with this command: cryptsetup luksAddKey /dev/sda3 luks-secret.key --key-slot 1

Note: If you have a laptop/desktop with UEFI, the LUKS partition may be on /dev/sda4, depending on how you partitioned the drive.

The command above adds the newly generated key to slot 1 (assuming slot 0 contains your LUKS passphrase) of your LUKS partition. It is assumed your encrypted partition is on /dev/sda3. Check before applying the changes.

You will be prompted for your usual LUKS passphrase before the key is added. You can check the key has been added to slot 1 with the following command: cryptsetup luksDump /dev/sda3

1.4 Delete the key from your file system with the following command: shred --remove --zero luks-secret.key


2: Modify Dracut

Fedora 12 onwards introduced a new boot system called dracut. It builds the 'initramfs', used to boot the system, from modules which can be modified to carry your own customisations.

2.1 Create a file using your favourite text-editor called usb-unlock.conf in the directory /etc/dracut.conf.d and add the following contents:

# dracut modules to omit
omit_dracutmodules+=" systemd "

# dracut modules to add to the default
add_dracutmodules+=" crypt "

Note: If you have a laptop with an external keyboard, Dracut may not load the necessary drivers to ensure they are running at boot. You can add the driver needed as in this example for Logitech wireless USB keyboards and mice:

# dracut modules to omit
omit_dracutmodules+=" systemd "

# dracut modules to add to the default
add_dracutmodules+=" crypt "

# additional kernel modules to the default
add_drivers+=" hid-logitech-dj "

Note 2: Adding systemd to the above stops it from trying to handle the unlock process.

2.2 I recommend creating a directory in /root called archive; and inside that, a directory called orig with the command mkdir -p /root/archive/orig. Into both directories, copy cryptroot-ask.sh and module-setup.sh from /usr/lib/dracut/modules.d/90crypt/ The orig copies are just in case you need to revert the changes.

2.2 Locate the "by-id" name of your USB key with the following command: ls -l /dev/disk/by-id | grep usb

e.g. usb-LaCie_iamaKey_60f2f4441dc104-0:0 -> ../../sdb

Remember to escape the : when you add the ID to the script in the next step.

2.3 Modify /root/archive/cryptroot-ask.sh using your favourite text-editor.

Scroll down the script to:

#
# Open LUKS device
#

...and underneath the #, and before info "luksOpen $device $luksname $luksfile", create some space to enter the text below:

# Unlock with USB key
sleep 5
udevsettle
usbkey=/dev/disk/by-id/usb-Generic_Flash-Disk_9207025320229027493-0\:0
if [ -e $usbkey ]; then
ask_passphrase=0
echo "USB Key detected - unlocking partition $device ..."
dd if=$usbkey bs=1 count=4096 | cryptsetup $cryptsetupopts luksOpen "$device" "$luksname" --key-file=-
else
ask_passphrase=1
fi

crytrootask

Scroll down the script to find the line # fallback to passphrase and comment-out the ask_passphrase=1 as in this example:

crytrootask

Save the changes to your file.

Notes: usbkey= should match the by-id discovered in 2.2 and do not forget to adjust bs=1 count=4096 if you changed them in step 1.2. You can add a delay to the script if your system does not see the USB key in time, although udevsettle should stop that. If you experience problems and need to slow things down, add sleep 5 before udevsettle for a 5 second pause. This can be increased to 10, or more, if needed.

2.3 Modify /root/ardhive/module-setup.sh using your favourite text-editor.

At the bottom of the file, locate the text dracut_need_initqueue and add underneath inst /bin/dd as in the example below:

crytrootask

Save the changes to your file.


3: Create new initramfs

3.1 In the /root directory create a scripts folder with the command: mkdir scripts

3.2 Create a script in /root/scripts/ called update-dracut and copy the following in to it:

#!/bin/bash

cp /root/archive/cryptroot-ask.sh /usr/lib/dracut/modules.d/90crypt/
cp /root/archive/module-setup.sh /usr/lib/dracut/modules.d/90crypt/

dracut -f -v

exit 0

Save the changes to your file.

3.3 Give the file execute permissions with the command: chmod 755 update-dracut

3.4 Run the script with the command /root/scripts/update-dracut or ./update-dracut if you are already in the directory. The script will replace the existing dracut 90crypt files and create a new initramfs of the running kernel in /boot

Note 1: Future kernel updates should take the above changes and continue to operate with the USB key unlock method.

Note 2: If dracut is updated, it will remove the modified files in 90crypt and your USB unlock method will stop working. If you see both kernel and dracut updates, use dnf to apply the new dracut first, then re-run the update-dracut script to re-apply your changes. After that you can use dnf to update the kernel, and when you reboot, the new kernel should contain the USB key unlock changes.


4: Reboot

With the key in place, the LUKS volume should automatically unlock. Without the key, the system will prompt for the passphrase.

What if it went horribly wrong? You can always boot to a previous kernel, or the rescue kernel, as they will not contain the modifications. If you need to back-out of the changes, simply copy the files in /root/archive/orig back to /usr/lib/dracut/modules.d/90crypt/, then re-run dracut with the extra option of building for a different kernel.

You can find out what kernel option to add by running the command ls -al /lib/modules, then using that reference with dracut; i.e. dracut -f -v --kver 4.18.0-425.3.1.el8.x86_64 Dracut will build a new initramfs for kernel version 4.18.0-425.3.1.el8.x86_64. The highest number should be the latest kernel applied to your system.


5: Two LUKS

You may come across the situation of two separate LUKS partitions that you want to unlock at the same time with the same USB key. For example, you may have a server with two 500 GiB spinning-rust drives set-up in RAID 1, and two 1.0 TiB spinning-rust drives set-up in RAID 0, as that is all you had to hand to create a spare data back-up server. How to unlock them?

You will need to complete step 1 and add your USB key file to both LUKS partitions; e.g. /dev/sda3 and /dev/sdb1.

For step 2, the best method is to create two manual unlock commands based on the contents of /etc/crypttab as in the example below:

crytrootask

Complete the rest of steps 2 and 3 before rebooting.


6: Debug

If you experience problems and want to debug the boot process, at the grub screen, press 'a' and remove the "rhgb" and "quiet" from the kernel command line and replace them with rd.debug. You will be able to follow the boot process with lots of verbose output. This should show you if the USB subsystem is taking a really long time to load. If the boot process fails, it will drop to a shell. You may need a rescue DVD if things have gone horribly wrong!

You may need to view dmesg to look for clues as not everything is printed to stdout, even when you are debug mode. It is also a good idea to reboot without the debug mode set once you have cleared any errors.

This page lists the debugging options: https://fedoraproject.org/wiki/Dracut/Debugging


Page updated: 29th December 2022