I want to encrypt the root filesystem on an Ubuntu Server 24.04 installation on my Raspberry Pi 5. I don't have any cables for the weird microHDMI connector they use on the new Pis, so it would be nice to do this headless. Root filesystem is located on an SD card, and I would like to use key files for encryption instead of passwords. There is this answer and this guide, but nothing about a way to do this headless, unfortunately.
1 Answers
I've been struggling to find any way to do this, until I was reminded about the existence of dropbear-initramfs by this answer. This seems to be the easiest solution by far. Just install an SSH server into initramfs. Here's the steps I followed, to make it work.
All commands are to be run as the superuser, sudo -s to open root shell.
Steps marked with █ may require changing commands or file contents with data specific to your situation.
Preparing initramfs
Install the system onto the SD card (I used Raspberry Pi Imager), insert into your RPi and power it on. Update it by running
apt update apt upgrade rebootInstall
dropbear-initramfsandbusybox:apt update apt install dropbear-initramfs busybox█ Add your SSH public key to
/etc/dropbear/initramfs/authorized_keysso that you're able to access the SSH server once it's running. You can copy the key from your machine:cat ~/.ssh/id_rsa.pubAnd paste it into the appropriate file on the RPi:
vi /etc/dropbear/initramfs/authorized_keys█ Edit
/etc/initramfs-tools/initramfs.confso that initramfs acquires an IP address from DHCP. Find the end of the file, and add:IP=::::<hostname>::dhcp:::Replace
<hostname>with the hostname the Pi should have.SSH may be mad about Pi constantly changing signature. Use this answer if desperate.
Edit
/etc/initramfs-tools/modulesto add the kernel modules necessary using for LUKS and encryption toinitramfs. Add the following at the end of the file:algif_skcipher aes_arm64 aes_ce_blk aes_ce_ccm aes_ce_cipher sha256_arm64 cbc dm-cryptAdd the tools for working with filesystems to
initramfsby creating/etc/initramfs-tools/hooks/luks_hookswith the following content:#!/bin/sh -e PREREQS="" case $1 in prereqs) echo "${PREREQS}"; exit 0;; esac. /usr/share/initramfs-tools/hook-functions
copy_exec /sbin/e2fsck /sbin copy_exec /sbin/resize2fs /sbin copy_exec /sbin/fdisk /sbin copy_exec /sbin/cryptsetup /sbin
Don't forget to make it executable:
chmod +x /etc/initramfs-tools/hooks/luks_hooksUpdate
initramfsto apply all of these changes:update-initramfs -u█ You can verify that
initramfshas all the necessary tools and modules by using the following commands.lsinitramfs /boot/initrd.img-<...> | grep -P "sbin/(cryptsetup|resize2fs|fdisk|e2fsck)"It should print:
usr/sbin/cryptsetup usr/sbin/e2fsck usr/sbin/fdisk usr/sbin/resize2fsAnd
lsinitramfs /boot/initrd.img-<...> | grep -P "(algif_skcipher|aes-arm64|sha256-arm64|cbc|dm-crypt)"should print:
usr/lib/modules/<...>/kernel/arch/arm64/crypto/aes-arm64.ko.zst usr/lib/modules/<...>/kernel/arch/arm64/crypto/sha256-arm64.ko.zst usr/lib/modules/<...>/kernel/crypto/algif_skcipher.ko.zst usr/lib/modules/<...>/kernel/crypto/pcbc.ko.zst usr/lib/modules/<...>/kernel/crypto/xcbc.ko.zst usr/lib/modules/<...>/kernel/drivers/md/dm-crypt.ko.zstFor me,
<...>is6.8.0-1020-raspi, which I suppose is the kernel version. It will probably be different for you, though. Take care to replace it in all commands above with your version.
Preparing boot
Generate a key to use for encryption and place it in
/boot/firmware, as this is a location that is easily accessible frominitramfs:apt update apt install uuid cd /boot/firmware dd if=/dev/random bs=512 count=4 of=$(uuid).lekEdit
/etc/fstabto use the encrypted volume which we will create in a few steps. Change the first line to:/dev/mapper/rootfs / ext4 defaults 0 0Update
initramfs, ignoring its dire warnings:update-initramfs -uEdit
/boot/firmware/cmdline.txt:- change
root=LABEL=writabletoroot=/dev/mapper/rootfs - add
cryptdevice=/dev/mmcblk0p2:rootfsat the end of the line
Some articles want you to also add
break=initat the end of the line, but if you do that, it'll drop you intoinitramfstwo times, first because it can't boot, and then because you asked for it. The thing is, dropbear won't start the second time. So don't do this if you're doing this headless.It also seems that Desktop versions of Ubuntu also have
splashargument incmdline.txt, which should be removed, as the splash screen will not at all aid you in this endeavor.- change
Now you can finally reboot:
reboot
Encrypting rootfs in-place from initramfs
█ Your Pi should now be booted into
initramfswith an IP address acquired from your router via DHCP. If you do not know what this IP address is, log into your router to check. After that, it's a matter of using SSH to log into initramfs:ssh -i ~/.ssh/id_rsa root@<Pi's IP address>(If your SSH private key is stored in some other location, be sure to adjust the command)
Once in
initramfs, mount thesystem-bootpartition:mkdir -p /mnt/boot mount /dev/mmcblk0p1 /mnt/bootCheck and repair
rootfswithe2fsck(this step is required):e2fsck -f /dev/mmcblk0p2Shrink
rootfsto make space for LUKS header:resize2fs -M /dev/mmcblk0p2█ Encrypt
rootfsin-place by usingcryptsetup reencrypt(this will take some time, depending on the size of the SD card and its performance class, for me it took 40 minutes for 64 GB):cryptsetup reencrypt --new --reduce-device-size=16M --type=luks2 -c aes-xts-plain64 -s 256 -h sha256 --use-urandom --key-file /mnt/boot/<key file name>.lek /dev/mmcblk0p2█ Once the encryption is finished, unlock the encrypted
rootfs:cryptsetup luksOpen --key-file /mnt/boot/<key file name>.lek /dev/mmcblk0p2 rootfsExpand the
rootfsto fill the entire partition:resize2fs /dev/mapper/rootfsAt this point, you can try and mount the
rootfsto see if everything is working correctly:mkdir -p /mnt/root mount /dev/mapper/rootfs /mnt/root cd /mnt/root ls -laRetrace your steps if the output of any of these commands isn't right.
█ At this point, despite
/dev/mapper/rootfsexisting, the Pi will make no attempt at switching to it. To do that, first reboot the Pi:reboot -fThen, without waiting too long, connect to it as you did in step 1 of this section, and unlock
rootfsagain:mkdir -p /mnt/boot mount /dev/mmcblk0p1 /mnt/boot cryptsetup luksOpen --key-file /mnt/boot/<key file name>.lek /dev/mmcblk0p2 rootfsYou should get something along the lines of
Connection to <IP> closed by remote host.in 2-3 seconds after unlocking
rootfs. If it doesn't work, try again, but quicker. After this, the Pi will boot intorootfs.
Restoring normal boot
Since Debian's implementation of crypttab doesn't seem to support reading key files directly from devices, you can use a 'keyscript' to do this for you. Create
/lib/cryptsetup/scripts/unlockwith the following content:#!/bin/sh set -e if [ ! -e /sdboot ]; then mkdir -p /sdboot sleep 3 fi if mount /dev/mmcblk0p1 /sdboot >/dev/null; then if [ -e /sdboot/$CRYPTTAB_KEY.lek ]; then cat /sdboot/$CRYPTTAB_KEY.lek umount /sdboot exit fi umount /sdboot fi /lib/cryptsetup/askpass "Insert key or enter passphrase: "This script will try and mount the
system-bootpartition of the SD card to/sdbootand use the key file name, which will be passed to it bycrypttab, to find the correct key on thesystem-bootpartition. Then, it will dump the contents of the file onto stdout, which will be interpreted as the contents of the key file. The/sdbootmount point will only exist withininitramfsand won't be a part ofrootfs.Don't forget to make it executable:
chmod a+x /lib/cryptsetup/scripts/unlock█ Add an entry to
/etc/crypttabto unlockrootfsautomatically at boot time:rootfs /dev/mmcblk0p2 <key file name (without the .lek extension)> luks,initramfs,keyscript=unlockUpdate
initramfsto apply changes:update-initramfs -uReboot to test:
reboot
At this point, you can modify the setup to your liking. For example, you might want to move the key file onto a USB drive and modify the script accordingly. I have a GitHub Gist with detailed instructions on how to do exactly this. Or you can switch to password-based unlock with the key as a backup. The point is, you now have a working system which boots and with which you can do anything that you wish.
My recommendation would be to change the encryption key if you switch to USB-drive-based unlocking (as the one written to the SD card could still be read if erased improperly), and to use device UUIDs (you can view them wuth blkid) instead of /dev/<whatever>-style block device specification, as UUIDs are not system-dependent.
- 406