I boot my laptop probably ten times a day. Every single time, LUKS asks for my passphrase. It got old fast, especially in coworking spaces where someone is always sitting close enough to watch you type.
So I set up a USB stick as a physical key. Plug it in, machine unlocks. No stick, it falls back to the passphrase. Here is how.
What you need
- An encrypted root partition (e.g.
/dev/nvme0n1p3) that currently unlocks with a passphrase - A USB stick
- A system using initramfs-tools (Debian/Ubuntu default)
Keep at least one passphrase slot active. If you lose the USB, you still need a way in.
1) Format the USB
Find your stick and partition it:
lsblk -o NAME,SIZE,MODEL,MOUNTPOINT
sudo parted /dev/sdX --script mklabel gpt
sudo parted /dev/sdX --script mkpart primary ext4 0% 100%
sudo mkfs.ext4 -L KEYSTICK /dev/sdX1
Mount it:
sudo mkdir -p /mnt/keystick
sudo mount /dev/sdX1 /mnt/keystick
FAT works too (mkfs.vfat -n KEYSTICK /dev/sdX1), just swap ext4 for vfat
in the initramfs modules later.
2) Generate a keyfile
sudo dd if=/dev/urandom of=/mnt/keystick/luks-keyfile bs=4096 count=1 status=none
sudo chmod 0400 /mnt/keystick/luks-keyfile
sudo sync
sudo umount /mnt/keystick
4096 random bytes. That is your key.
3) Add the keyfile to LUKS
sudo mount /dev/sdX1 /mnt/keystick
sudo cryptsetup luksAddKey /dev/nvme0n1p3 /mnt/keystick/luks-keyfile
sudo umount /mnt/keystick
It will ask for your existing passphrase. To verify it worked:
sudo mount /dev/sdX1 /mnt/keystick
sudo cryptsetup open --test-passphrase --key-file /mnt/keystick/luks-keyfile /dev/nvme0n1p3
sudo umount /mnt/keystick
Exit code 0 means you are good.
4) Grab the UUIDs
You need two:
# the encrypted partition
sudo blkid -s UUID -o value /dev/nvme0n1p3
# the USB partition
sudo blkid -s UUID -o value /dev/sdX1
5) Edit /etc/crypttab
This is the part that took me a while to get right. For initramfs-tools, you want the passdev keyscript. The format is:
<name> UUID=<LUKS-UUID> /dev/disk/by-uuid/<USB-UUID>:/luks-keyfile luks,keyscript=passdev
With a 30-second timeout for slow USBs:
dm_crypt-0 UUID=80edd8a0-51ef-43ac-b2c1-62ababd1809a /dev/disk/by-uuid/09179c27-02cd-43a8-a59b-fd6eb8ef9e32:/luks-keyfile:30 luks,keyscript=passdev
Do not use keydev= or x-systemd.* options. Those are for systemd/dracut,
not initramfs-tools.
6) Add the initramfs modules
The initramfs needs to know about your USB filesystem and USB storage:
echo ext4 | sudo tee -a /etc/initramfs-tools/modules
echo usb_storage | sudo tee -a /etc/initramfs-tools/modules
echo uas | sudo tee -a /etc/initramfs-tools/modules
Replace ext4 with vfat if you used FAT.
7) Rebuild and check
sudo update-initramfs -u
ls -l /lib/cryptsetup/scripts/passdev
ls -l /dev/disk/by-uuid/<USB-UUID>
sudo mount /dev/disk/by-uuid/<USB-UUID> /mnt && ls -l /mnt/luks-keyfile && sudo umount /mnt
8) Reboot
- USB plugged in: unlocks automatically.
- USB not there: passphrase prompt as usual.
If it still asks for the passphrase with the USB in, boot normally and check:
journalctl -b -u systemd-cryptsetup@<name>.service
Common problems: wrong UUID, USB too slow (bump the timeout to 60), missing kernel modules.
Maintenance
Check your keyslots periodically:
sudo cryptsetup luksDump /dev/nvme0n1p3 | grep -A1 'Keyslot'
Back up the keyfile somewhere offline and encrypted. If you need to rotate the key:
sudo mount /dev/sdX1 /mnt/keystick
sudo dd if=/dev/urandom of=/mnt/keystick/luks-keyfile bs=4096 count=1 status=none
sudo chmod 0400 /mnt/keystick/luks-keyfile
sudo sync
sudo cryptsetup luksAddKey /dev/nvme0n1p3 /mnt/keystick/luks-keyfile
sudo cryptsetup luksRemoveKey /dev/nvme0n1p3 # prompts for the old key
sudo umount /mnt/keystick
Without a USB (local key in initramfs)
If you just want to skip the passphrase prompt without a physical key, you can embed the keyfile in the initramfs itself:
<name> UUID=<LUKS-UUID> /etc/keys/luks-keyfile luks
Put the file at /etc/keys/luks-keyfile, chmod 0400, rebuild with
sudo update-initramfs -u. No keyscript=passdev needed. But the key lives on
disk, not on a separate device.