It's Pi all the way down...

by

See Also: LVM+LUKS

Last time we had a look at using LVM on our root storage. This time around it’s LUKS‘ turn: we’re going to make an Ubuntu Desktop for Raspberry Pi card, with the root file-system encrypted with LUKS. Thankfully this is much simpler affair as LUKS can encrypt, decrypt, or for that matter, re-encrypt a block device non-destructively (even with interruptions in the middle!).

As Easy As …

We get off to the simplest possible start. You’ll need a machine running Ubuntu and some form of Pi-compatible storage (an SD card, or some form of USB mass storage).

  • Install the Raspberry Pi imaging utility (or use sudo snap install rpi-imager for the snap version if you’re on an architecture other than amd64)
  • Grab an SD card or some form of USB storage (if you’re intending to use the desktop image, I’d recommend something fast like an SSD)
  • Install the Ubuntu 21.10 desktop image (substitute server if you wish) on the storage with the imaging utility (I won’t bother detailing this process because, frankly, the pi’s imaging utility is about as simple as you can possibly get!)
  • Boot the installed image on your Pi

Wot, no cryptsetup?

It might seem a bit odd that we’re jumping straight to booting the image, but there is a reason for this: on earlier versions of Ubuntu, the cryptsetup-initramfs package isn’t installed by default. On Impish it is installed by default, but the initramfs that ships with the image still doesn’t incorporate the cryptsetup binary that’s actually necessary. Hence, we need to boot it at least once and re-generate the initramfs to include it.

Get to a usable terminal (if you’re on the desktop image just go through the initial setup), then install cryptsetup-initramfs (as mentioned, on Impish this shouldn’t be necessary but best to make sure) and re-generate the initramfs.

$ sudo apt install cryptsetup-initramfs
... usual apt stuff ...
$ sudo update-initramfs -u
... lots of flash-kernel output ...

That last step will take several minutes; here’s some hints to read first if you want to speed things up a bit. Now shut down the Pi and plug the SD card (or USB drive) back into your original machine.

Setec Astronomy

Time to perform the actual encryption. Below I’m assuming that your SD card (or whatever target storage you’re using) appears as the /dev/mmcblk0 on your machine. Adjust these instructions according to your setup!

We’re going to unmount the partitions of the SD card (or other storage) you’ve just inserted (this is only necessary if your Ubuntu machine is a desktop machine which will auto-mount the card), force a file-system check of the root partition, and shrink that partition to its minimal size:

$ sudo umount /dev/mmcblk0p1
$ sudo umount /dev/mmcblk0p2
$ sudo fsck -f /dev/mmcblk0p2
fsck from util-linux 2.34
e2fsck 1.45.5 (07-Jan-2020)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
writable: 154688/557520 files (0.2% non-contiguous), 1507014/2229303 blocks
$ sudo resize2fs -M /dev/mmcblk0p2
resize2fs 1.45.5 (07-Jan-2020)
Resizing the filesystem on /dev/sda2 to 1881115 (4k) blocks.
The filesystem on /dev/sda2 is now 1881115 (4k) blocks long.

Now for the actual encryption. First, pick a decent passphrase, then pick a decent encryption cipher. Below I’m going to use the Adiantum cipher as this performs reasonably well on the Pi. It’s well above the speed of most SD cards, but be aware it will probably hurt the performance of SSDs. Use cryptsetup benchmark --cipher xchacha12,aes-adiantum if you want an idea of its performance; compare this to the output of cryptsetup benchmark which runs through several of the more common algorithms.

At any rate, expect this next bit to take some considerable time!

$ sudo cryptsetup reencrypt --encrypt --reduce-device-size 16M --cipher xchacha12,aes-adiantum-plain64 /dev/mmcblk0p2

WARNING!
========
This will overwrite data on LUKS2-temp-dd54103b-d96c-4d1d-9e23-e3c375c81ecb.new irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase for LUKS2-temp-dd54103b-d96c-4d1d-9e23-e3c375c81ecb.new:
Verify passphrase:
Progress:   1.8%, ETA 09:51,  160 MiB written, speed  14.4 MiB/s
...
Progress:  17.7%, ETA 08:05, 1544 MiB written, speed  14.8 MiB/s
...
Finished, time 09:45.528, 8700 MiB written, speed  14.8 MiB/s
$ sudo cryptsetup open /dev/mmcblk0p2 rootfs
Enter passphrase for /dev/mmcblk0p2:

Rooting Around

At this point our encrypted container on /dev/mmcblk0p2 is open as a decrypted device called /dev/mapper/rootfs. We can now expand the partition back to its “normal” size (minus the 16M we shrunk it for metadata), and mount it (and the boot partition) at some appropriate location.

$ sudo resize2fs /dev/mapper/rootfs
resize2fs 1.45.5 (07-Jan-2020)
Resizing the filesystem on /dev/mapper/rootfs to 2227255 (4k) blocks.
The filesystem on /dev/mapper/rootfs is now 2227255 (4k) blocks long.
$ sudo mkdir -p /mnt/boot /mnt/root
$ sudo mount /dev/mmcblk0p1 /mnt/boot
$ sudo mount /dev/mapper/rootfs /mnt/root

Now we need to perform some surgery on the file-system tables. Firstly, we need to tell the system how to set up the “rootfs” node we just opened. This is the job of the /etc/crypttab file. This should look relatively familiar to anyone used to messing with /etc/fstab, but bear in mind that the target and source devices are “backwards” (target first, then source).

Another important thing to remember here is that in this file, /dev/mmcblk0p2 is the device for the encrypted partition from the Pi’s perspective. In other words, if your SD card appears as /dev/sdf on your PC and you’ve been using /dev/sdf2 as the card’s root partition in the instructions above you should not do that here: just use /dev/mmcblk0p2. However, if you’re using an SSD via a USB adapter as your storage, you may need to adjust the line below to use /dev/sda2 as that’s how the Pi will be referring to this partition at boot time.

$ cat /mnt/root/etc/crypttab
# <target name> <source device>         <key file>      <options>
$ echo "rootfs  /dev/mmcblk0p2  none  luks,discard" >> /mnt/root/etc/crypttab
$ cat /mnt/root/etc/crypttab
# <target name> <source device>         <key file>      <options>
rootfs  /dev/mmcblk0p2  none  luks,discard

Note

You may or may not wish to include the “discard” option above. It’s the default in Debian and Ubuntu for encrypted partitions but may carry security risks. See man crypttab for full details.

With the /dev/mapper/rootfs device configured, all that remains is to tell the kernel and systemd to look there for its root device via the /etc/fstab table, and the cmdline.txt file on the boot partition:

$ cat /mnt/root/etc/fstab
# UNCONFIGURED FSTAB FOR BASE SYSTEM
LABEL=writable    /     ext4    defaults,x-systemd.growfs    0 0
/swapfile         none  swap    sw    0 0
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
$ sudo sed -i -e 's,^LABEL=writable,/dev/mapper/rootfs,' /mnt/root/etc/fstab
$ cat /mnt/root/etc/fstab
# UNCONFIGURED FSTAB FOR BASE SYSTEM
/dev/mapper/rootfs    /     ext4    defaults,x-systemd.growfs    0 0
/swapfile         none  swap    sw    0 0
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
$ cat /mnt/boot/cmdline.txt
dwc_otg.lpm_enable=0 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash
$ sudo sed -i -e 's,LABEL=writable,/dev/mapper/rootfs,' /mnt/boot/cmdline.txt
$ cat /mnt/boot/cmdline.txt
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mapper/rootfs rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash

Note

Again, feel free to use an editor here instead of messing around with sed; I’ve illustrated the changes necessary by showing the before and after states of the file affected.

Fail! (and then succeed)

Time to clean up and test our card.

$ sudo umount /mnt/root /mnt/boot
$ sudo cryptsetup close rootfs

Eject the card (or mass storage) properly, shove it in your Pi and start it booting. This first boot will appear to take a very long time (several minutes, while it times out waiting for the root device to appear) and will then fail, dropping to the initramfs prompt. The reason for this is that, even though we re-generated the initramfs to include the cryptsetup tool, it still isn’t configured to actually unlock the correct partition. We’ll need to re-generate it again to do that, but first we need to get the system booted.

At the (initramfs) prompt run the following to get the system booting (if you’re booting an SSD drive, adjust /dev/mmcblk0p2 below to /dev/sda2):

(initramfs) cryptsetup open /dev/mmcblk0p2 rootfs
Enter passphrase for /dev/mmcblk0p2:
(initramfs) exit

Once the system has booted successfully, login and re-generate the initramfs one last time before rebooting to test your new setup:

$ sudo update-initramfs -u
$ sudo reboot

This time the initramfs should prompt you nicely to unlock your device before proceeding with the rest of the boot.

That’s all for this time. Let me know below if you run into any issues with this!