Guide : encrypted root partition on uConsole

I plan to use a my newly received uConsole a lot as an emacs org-mode machine on the move. I will also sync personal stuff and do mails on it, so I needed proper SD-card encryption in case it’s lost or stolen. Just achieved it tonight, so here are my notes on how to do it if you are interested.

Mostly taken out from this almost perfect guide: https://rr-developer.github.io/LUKS-on-Raspberry-Pi/

Prerequisites:

  • uConsole CM4
  • HDMI display and proper cable to connect to the uConsole
  • Your favorite Linux computer

I created a custom image with most of my everyday tools already installed, but it should also work with the stock uConsole image from here. My custom image is built from this one anyway.

If you are using the stock clockworkpi image, or a modified image that was never booted before, you may have to modify /boot/cmdline.txt to remove the following : init=/usr/lib/raspi-config/init_resize.sh from the kernel command line. You can do this by plugging your sdcard in any computer and edit the file with your favorite text editor. Thanks to @straypig for mentioning this!

Ensure you have the proper tools installed on your uConsole system:

sudo apt install busybox cryptsetup initramfs-tools

cryptsetup version should be 2.0.6 or later and benchmark should succeed.

cryptsetup --version
cryptsetup benchmark -c xchacha20,aes-adiantum-plain64

If clockworkpi publishes a new kernel for the uConsole through their apt repository, we want to have a new initramfs built. Disclaimer: I just followed the referenced guide on this part and did not test it by force pushing a kernel update in some way.

So we create the following file: /etc/kernel/postinst.d/initramfs-rebuild with this content:

#!/bin/sh -e

# Rebuild initramfs.gz after kernel upgrade to include new kernel's modules.
# https://github.com/Robpol86/robpol86.com/blob/master/docs/_static/initramfs-rebuild.sh
# Save as (chmod +x): /etc/kernel/postinst.d/initramfs-rebuild

# Remove splash from cmdline.
if grep -q '\bsplash\b' /boot/cmdline.txt; then
  sed -i 's/ \?splash \?/ /' /boot/cmdline.txt
fi

# Exit if not building kernel for this Raspberry Pi's hardware version.
version="$1"
current_version="$(uname -r)"
case "${current_version}" in
  *-v7+)
    case "${version}" in
      *-v7+) ;;
      *) exit 0
    esac
  ;;
  *+)
    case "${version}" in
      *-v7+) exit 0 ;;
    esac
  ;;
esac

# Exit if rebuild cannot be performed or not needed.
[ -x /usr/sbin/mkinitramfs ] || exit 0
[ -f /boot/initramfs.gz ] || exit 0
lsinitramfs /boot/initramfs.gz |grep -q "/$version$" && exit 0  # Already in initramfs.

# Rebuild.
mkinitramfs -o /boot/initramfs.gz "$version"  

And make it executable:

sudo chmod +x /etc/kernel/postinst.d/initramfs-rebuild

Next, we add a hook script to ensure we got LUKS tools on our initramfs. To do so create the following file: /etc/initramfs-tools/hooks/luks_hooks with 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/resize2fs /sbin
copy_exec /sbin/fdisk /sbin
copy_exec /sbin/cryptsetup /sbin

And make it executable:

sudo chmod +x /etc/initramfs-tools/hooks/luks_hooks

Let’s ensure that our initramfs also bundle the required kernel modules, add the following to /etc/initramfs-tools/modules:

algif_skcipher
xchacha20
adiantum
aes_arm
sha256
nhpoly1305
dm-crypt

You can now create your initramfs:

sudo -E CRYPTSETUP=y mkinitramfs -o /boot/initramfs.gz

There will be some errors regarding cryptsetup but they can be ignored safely.

Check that your initramfs has everything it needs:

lsinitramfs /boot/initramfs.gz | grep -P "sbin/(cryptsetup|resize2fs|fdisk)"
lsinitramfs /boot/initramfs.gz | grep -P "(algif_skcipher|chacha|adiantum|aes-arm|sha256|nhpoly1305|dm-crypt)"

Next, we change the way the uConsole boots by switching to our newly built initramfs. I highly recommend that you make backups of the files as you modify them, so you can recover easily in case something go wrong.

First, /boot/config.txt:

Append at the end of the file:

initramfs initramfs.gz followkernel

Second, /boot/cmdline.txt:

I have already removed from my image the reference to the script that auto-resize partition on first boot (init=/usr/lib/raspi-config/init_resize.sh). If your uConsole has already booted once, it should not be there anymore, otherwise just remove it. We will properly resize our partition at the end.

Next we must remove references to the current unencrypted partition to point instead to the encrypted one we will prepare. It should look just like this:

console=serial0,115200 console=tty1 root=/dev/mapper/sdcard cryptdevice=/dev/mmcblk0p2:sdcard fsck.repair=yes rootwait  

Third, /etc/fstab:

Remove the line mentionning the unencrypted partition, on my image it was this:

PARTUUID=50a67c3e-02  /               ext4    defaults,noatime  0       1

Instead add this line:

/dev/mapper/sdcard  /               ext4    defaults,noatime  0       1

Fourth and last, /etc/crypttab:

Add the following line:

sdcard	/dev/mmcblk0p2	none	luks

Everything should be OK for a reboot. BUT initramfs will fail because the encrypted partition is not there yet and the built-screen of the uConsole does not display the related initramfs shell to interact with in this case. Very annoying.

Let’s start by encrypting our partition. Plug the sdcard out of the uConsole and into your favorite Linux computer.

I use a SD-card to USB dongle, so my device is recognized as /dev/sda, but it might be something else for you (/dev/mmcblk0 for instance). Just check with sudo dmesg and fdisk -l to get the proper identifier. I assume /dev/sda from here.

I did not bother to shrink the partition because as explained before I disabled the script in cmdline.txt that automatically expands it during the first boot. Feel free to check the original guide if you have a huge SD-card with a mostly empty but also huge partition :slight_smile:

For me it was pretty straightforward, we store the raw data of the root partition to a file on the computer :

sudo dd bs=4k if=/dev/sda2 of=./data.img status=progress

With that done, we can format our SD-card partition as LUKS encrypted:

sudo cryptsetup --type luks2 --cipher xchacha20,aes-adiantum-plain64 --hash sha256 --iter-time 5000 --key-size 256 --pbkdf argon2i luksFormat /dev/sda2

If you know what you are doing (or just RTFMing), you can very well change the parameters as you please. But keeping this cipher is probably best for performance on CM4.

Let’s open our encrypted LUKS container:

cryptsetup luksOpen /dev/sda2 sdcard

Since I wanted to be sure that my backed-up data partition would fit in there, I resized it by expending it of 1G.
I did this the easy-way with gparted GUI, but it could also be done with parted command resizepart 2 1G.

Then we copy back our data on the partition:

sudo dd bs=4k if=./data.img of=/dev/mapper/sdcard status=progress

Do a quick e2fsck:

sudo e2fsck -f /dev/mapper/sdcard

And close the LUKS container:

sudo cryptsetup luksClose /dev/mapper/sdcard

That is it for encryption.

Now to address the display problem at boot, the only way I found was to modify /boot/config.txt by commenting some lines in order to view the initramfs shell prompt.

With the SD-card still plugged in your computer, mount the boot partition (remember, SD-card is /dev/sda for me):

mkdir /tmp/boot
sudo mount /dev/sda1 /tmp/boot

Then proceed to edit /tmp/boot/config.txt to make it look like this:

disable_overscan=1
dtparam=audio=on
[pi4]
# max_framebuffers=2

[all]
ignore_lcd=1
dtoverlay=dwc2,dr_mode=host
# dtoverlay=vc4-kms-v3d-pi4,cma-384
dtoverlay=devterm-pmu
dtoverlay=devterm-panel-uc
dtoverlay=devterm-misc
dtoverlay=audremap,pins_12_13

dtparam=spi=on
gpio=10=ip,np

initramfs initramfs.gz followkernel

Remember to unmount your SD-card boot partition:

sudo umount /dev/sda1

Unplug the SD-card from your computer, plug it into your uConsole, connect a display to HDMI and boot it up.

By the way, if someone finds a way to have the built-in uConsole screen to display the initramfs prompt/shell without connecting an external HDMI display, please let me know! This is the limit of my raspiboot-fu :smiley:

So, the device boots up, the uConsole screen is powered on but blank, but the HDMI display should show you that it failed to find the root filesystem and drop you in initramfs shell to tell it what to do.

There, just open the LUKS container:

cryptsetup luksOpen /dev/mmcblk0p2 sdcard

It can take a few seconds to decrypt and open. Then exit the initramfs shell:

exit

And the uConsole boots up on our encrypted filesystem!

To ensure that we can boot without going to initramfs shell every time, let’s rebuild a final initramfs :

sudo mkinitramfs -o /tmp/initramfs.gz
sudo cp /tmp/initramfs.gz /boot/initramfs.gz

Finally, restore /boot/config.txt to its original state :

disable_overscan=1
dtparam=audio=on
[pi4]
max_framebuffers=2

[all]
ignore_lcd=1
dtoverlay=dwc2,dr_mode=host
dtoverlay=vc4-kms-v3d-pi4,cma-384
dtoverlay=devterm-pmu
dtoverlay=devterm-panel-uc
dtoverlay=devterm-misc
dtoverlay=audremap,pins_12_13

dtparam=spi=on
gpio=10=ip,np

initramfs initramfs.gz followkernel

From now, when starting the uConsole, the screen will be blank at this stage (cannot display initramfs shell, remember). So just wait 5-10 seconds before entering your passphrase + Enter.

Then you can wait again a good 10 seconds, decrypting the partition is not instantaneous.

Root filesystem is mounted, startup resumes and you can see the usual messages, then X11.

At last, if you want to expand your encrypted partition to take all the remaining space on your SD-card, you can follow these last steps.

Maybe you can do it directly from the uConsole but I figured it would be much simpler to plug again the SD-card in my trusty Linux computer.
Remember, I use a SD-card to USB dongle, so my SD-card device identifier will be /dev/sda.

Let’s begin by resizing the hosting partition:

sudo parted /dev/sda

print free
resizepart 2 100%

Next let’s open and resize the encrypted partition:

sudo cryptsetup luksOpen /dev/sda2 sdcard
sudo cryptsetup resize sdcard
sudo e2fsck -f /dev/mapper/sdcard
sudo resize2fs -f /dev/mapper/sdcard

That’s all!

I hope these notes are not too messy and that they could prove useful to someone.

Next step : have my uConsole automatically unlock LUKS partition when the proper Yubikey is plugged in at boot time :slight_smile:

14 Likes

This is wonderful and extremely thorough. Greatly appreciated, thanks for putting in the work!

Thanks for the instructions. Does anyone have experience of whether this also works with a CM4 with emmc memory?

1 Like

Would this work with the A04/A06 boards?

I confirm that this procedure works 100%.

The only thing I’d add here is an initial step to mount the first partition of the SD-card once it is loaded with the stock uConsole image, and remove the call to the script in cmdline.txt that automatically expands the partition during the first boot.

Otherwise, you’ll be unable to copy back your data on the partition later in the procedure with “sudo dd bs=4k if=./data.img of=/dev/mapper/sdcard status=progress”.

Thanks (a lot) to @mclbn for these instructions! This is a game changer for me.

1 Like

@hans I don’t see why disk encryption would not work on emmc, but you will have to do some things differently. Notably backing up your data and restoring it after creating the encrypting partition. I did this the easy way by plugging my sdcard into my laptop, which you won’t be able to do with emmc. The original guide (LUKS on Raspberry Pi | LUKS-on-Raspberry-Pi) might help you with this.

@Schmitty I have absolutely no idea, again in principle I don’t see why not since LUKS encryption is Linux standard, but your kernel / initramfs must include the needed modules and userland utilities. Also performance might be worse (or better for all I know).

@straypig You are absolutely right ! Thank you for pointing this out. Since I started this process with my modified image, the cmdline.txt was already amended. I will update the original post to mention it :wink: