EDIT : The procedure described here won’t work on Bookworm. I just posted an updated version of this guide for Bookworm here : Updated guide for Bookworm : 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
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
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