Fedora Encrypted Root With Automatic Key Unlocking

Fedora’s installer will happily set up an encrypted install with root-on-lvm-on-luks (/boot is still unencrypted. Secure Boot might be handy here still). This is supported and works out of the box.

However, while I’m present when I reboot this machine, it is also headless (no keyboard or monitor), so typing a passphrase at boot is problematic. But no problem, you can have up-to ten key slots for a LUKS partition, right? And you can use a keyfile for one of those slots, right? So it should just be a matter of updating the crypttab, right?

While crypttab does allow you to set a keyfile, and it does indeed work for all other filesystems, it doesn’t work for /. It expects a (mounted) path to the keyfile, and the USB token won’t be mounted until after /. So there’s a bit of a chicken and egg situation: You can’t mount the keys to decrypt root until after root is decrypted.

But luckily we’re using systemd, and it actually does handle this scenario. It allows us to say which device contains the key, without having to expect it to be mounted. We just can’t use crypttab to do this. The documentation exists (and is generally well written), but it still took a lot of fiddling to get this to work correctly.

Use man systemd-cryptsetup-generator for the details.

Note: the crypttab(8) manpage appears to indicate crypttab supports this as well, but I just couldn’t get it to work. That said, I also had issues with a too-short timeout that caused a lot of headaches…

Set up Kernel Command Line

Have keys working

Before we begin, you should have your keys created and added to your LUKS partition. Create your key file the “normal” way (random data into text file with no newline at the end). There’s plenty of articles and howtos about this part, so I won’t get into this here.

Add parameters to grub’s configuration

We’ll need to edit the GRUB_CMDLINE_LINUX in /etc/default/grub to add all the decryption instructions for systemd to process at boot time:

$ cat /etc/default/grub | grep GRUB_CMDLINE_LINUX

GRUB_CMDLINE_LINUX="resume=/dev/mapper/vgsystem-swap rd.luks.uuid=luks-926ee4d1-4cd8-43d7-97a3-07d41ed2a742 rd.lvm.lv=vgsystem/root rd.lvm.lv=vgsystem/swap rd.luks.key=926ee4d1-4cd8-43d7-97a3-07d41ed2a742=/my.luks.key:LABEL=keys rd.luks.options=926ee4d1-4cd8-43d7-97a3-07d41ed2a742=keyfile-timeout=30s rhgb quiet"

Here’s the luks-specific details I needed to have:

rd.luks.uuid

rd.luks.uuid=luks-926ee4d1-4cd8-43d7-97a3-07d41ed2a742

This tells systemd to open this container. The format is luks + crypt uuid, and it seems to have special hardcoding for that format. It probably works with just the bare UUID. Alternatively, you can probably try using rd.luks.name to set a different name once it’s opened. This line was actually here from the installer, so I left it alone.

You can figure out your UUID from either the command line (it may already be here), /etc/crypttab, or cryptsetup luksDump /dev/sdXX | grep UUID

rd.luks.key

rd.luks.key=926ee4d1-4cd8-43d7-97a3-07d41ed2a742=/my.luks.key:LABEL=keys

This is the magic bit, which tells systemd where the key is. The key parts (aside from the UUID again):

  • /my.luks.key

    This is the path to the key file relative to the USB stick, not the full path when mounted. So /my.luks.key instead of /mnt/keys/my.luks.key.

  • LABEL=keys

    This is an identifier for the filesystem that contains the keys. I labelled the filesystem on my drive as “keys”.

Be careful of the changing delimiters. It changes from = to : toward the end.

rd.luks.options

rd.luks.options=926ee4d1-4cd8-43d7-97a3-07d41ed2a742=keyfile-timeout=30s

This simply lengthens the timeout to probe the keyfile. This is fairly important, as the default timeout seems fairly short, and there’s a 75% chance that my USB device will not be probed yet. I really expected early boot to be far more deterministic, and this caused a lot of problems while troubleshooting. 30 seconds is far more time than needed (occasionally 10s was not enough).

Update Grub:

$ sudo grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg

Remove from crypttab

At this point, you’d expect it to work, but it doesn’t. You’ll still get prompted for a passphrase. The manpage for systemd-cryptsetup-generator actually tells us why in the rd.luks.uuid description:

If /etc/crypttab contains entries with the same UUID, then the name, keyfile and options specified there will be used. Otherwise, the device will have the name “luks-UUID”.

You could also set rd.luks.crypttab=no on the command line, but I still want to use crypttab for my non-root filesystems. So comment out the entry for this device in /etc/crypttab, and rebuild your initrd…

$ sudo dracut -f

Now you should be able to boot and have your disk unlocked automagically at boot.

Future investigation

It would be nicer to have this configured in /etc/crypttab. Perhaps it does work, and it was merely my USB timeout issues that prevented it.