I like to encrypt everything. Since almost all modern processors have hardware support for AES, there's almost no downside to encrypting every storage device you own. One potential downside on a headless system, however, is having to type in a passphrase on boot. Today, we're going to mitigate this without compromising our root partition.
When I set up my home server, I didn't want to keep a display and a keyboard plugged in for every time I wanted to reboot. Luckily, the Arch Wiki has a section on remote unlocking. The main method there involves running an SSH server in the initramfs which sounds like a terrible idea. The wiki page also mentions a script called passless-boot, but it's light on documentation. This is the method we'll use today.
LUKS has a key slot system. First, a key is generated which will be used to encrypt and decrypt the data on the volume. Then, that key is encrypted with another key of your choice. This can take the form of a passphrase or a keyfile. You can have up to 8 keys that can be used to decrypt the same volume, and keys can be added as long as you have another valid key. The encrypted keys and other important information are contained in the LUKS header.
passless-boot consists of two shell scripts. The first generates a keyfile, places it in the unencrypted boot partition, and adds it to a key slot in the LUKS header of the root partition. Once the system is rebooted, another script, activated by a Systemd service, securely erases the key from the boot partition and removes it from the LUKS header. As an extra touch, reboot-guard can also be used to block shutdowns unless passless-boot has run successfully.
Due to the way LUKS works, an attacker can get access to the volume forever if he has both a valid key and the LUKS header (with that key registered) at any point in time. However, the key on the unencrypted partition is only valid for the time it takes to reboot, assuming we reboot as soon as the script completes. The attacker would have to time it perfectly, cutting the power after the script completes and getting a copy of the key and the LUKS header before the cleanup can take place. For a server located in my bedroom closet, I consider this to be an acceptable compromise.
You can read more about the threat model and security implications of passless-boot in the README.
Note: It may be easier for an attacker to target your initramfs which is also on that unencrypted partition. See de-LUKS for a demonstration of this type of attack. For mitigation strategies, see Securing the unencrypted boot partition.
Note: Before proceeding, you may want to make a backup of your LUKS header in case something goes wrong. I have done this on two separate systems without any problems, but a backup can't hurt. See cryptsetup-luksHeaderBackup(8) and cryptsetup-luksHeaderRestore(8) for more information.
This guide will assume you are on Arch Linux. Adjust as needed for other distributions.
Both passless-boot and reboot-guard can be installed from the AUR using a helper of your choice. reboot-guard is optional, but I recommend it as it has already saved me from having to plug in a monitor and a keyboard. The cleanup script will start reboot-guard and it can be started manually, but it's best to let Systemd start it on each boot.
$ yay -S passless-boot reboot-guard # systemctl enable --now rguard
Now, we need to add a kernel option to our bootloader to point it to the right keyfile. If the keyfile is unavailable, it will fall back to a passphrase. First, get the UUID of your boot partition. In my case, the partition is at /dev/sda1.
# blkid /dev/sda1 /dev/sda2: UUID="B7E6-6DC1" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="a2961e31-0021-4994-a55b-9b04b193eb5a" ^^^^^^^^^ Copy this UUID
Then, append the following to your kernel options, replacing YOUR_UUID with the one for your partition. If you're using GRUB, remember to update your config after you change /etc/default/grub.
cryptkey=/dev/disk/by-uuid/YOUR_UUID:vfat:/crypto_key
vfat in the middle represents the format of the boot partition. If you followed the Arch Linux install guide, your boot partition should be formatted as VFAT like mine. /crypto_key is the location of the key relative to the root of the partition. This is a hard-coded location in passless-boot and should not be changed.
We also need to add the VFAT module to the initramfs so the key can be found. Add vfat to the MODULES option in /etc/mkinitcpio.conf and regenerate the initramfs.
# mkinitcpio -P
Finally, run passless-boot.sh. The script requires elevated privileges. On first run, you'll have to type the location of your boot partition. Use the same location you used for the kernel options, /dev/disk/by-uuid/YOUR_UUID. This won't be necessary on subsequent runs.
Type in your LUKS passphrase when prompted. Pay attention to the output of the script, it should resemble the following. If everything completes successfully, you should be able to reboot without typing in your passphrase.
✅ the current kernel was booted on cryptdevice /dev/disk/by-uuid/a6726d6f-18b1-4ea3-a88b-363d6764d497 ⚠ the kernel command line DOES NOT contains a cryptkey=<cryptkey_device>... directive! ⚠ this probably means your setup is incomplete, and password-less reboot might fail. ⚠ however, it could also mean you have just made the change and not rebooted yet. ⚠ if you understand what is going on, and want to attempt password-less reboot setup anyway ⚠ please copy the \"/dev/disk/by-uuid/<UUID>\" you want as cryptkey= kernel option: /dev/disk/by-uuid/B7E6-6DC1 ✅ /dev/disk/by-uuid/B7E6-6DC1 is a block device. ✅ cryptkey device /dev/disk/by-uuid/B7E6-6DC1 is mounted on /boot ⚠ adding keyfile to cryptdevice, please type password... Enter any existing passphrase: ✅ keyfile /boot/crypto_key is now valid for unlocking of /dev/disk/by-uuid/a6726d6f-18b1-4ea3-a88b-363d6764d497 ✅ keyfile in place and valid for unlocking root cryptdevice. ✅ password-less boot should be possible. ✅ all cleanup files exist and seem functional. ✅ cleanup service enabled. ✅ cleanup setup successfully. ✅ reboot-guard deactivated. 🎉 you may now reboot without needing to enter a root device password!
In the future, if you want to bypass reboot-guard, run rguard -0 with elevated privileges to allow shutting down.
Update 2023-05-27: Minor word choice and grammar fixes, add de-LUKS section.