Voidlinux with encrypted root on Raspberry Pi

So I have decided to go Void Linux on all my machines. One of the advantages for me was the absence of systemd and the presence of musl libc, my admiration for which I shared in a recent blog post about Alpine Linux. I was trying to find a tutorial on how to make an encrypted root partition work with Raspberry Pi. I have a strong belief that all offline storage should be encrypted today. I found multiple tutorials, which I made work together in this one.

You can skip the first step if you have Void Linux already running on your Raspberry Pi.

Initial installation

Configuring dracut and modules

Initial installation

Install Void Linux like you normally would by following these steps on an existing Linux machine. Alternatively, you can follow the commands I used below. Remember to replace sdX with your SD card’s path in /dev which you can look up with the command fdisk -l.

# parted /dev/sdX
(parted) mktable msdos
(parted) mkpart primary fat32 2048s 256MB
(parted) toggle 1 boot
(parted) mkpart primary ext4 256MB -1
(parted) quit
# mkfs.vfat -F32 /dev/sdX1
# mkfs.ext4 -O ^has_journal /dev/sdX2
# mkdir rootfs
# mount /dev/sdX2 rootfs/
# mkdir rootfs/boot
# mount /dev/sdX1 rootfs/boot
# tar xvfJp void-rpi-rootfs-latest.tar.xz -C rootfs
# echo '/dev/mmcblk0p1 /boot vfat defaults 0 0' >> rootfs/etc/fstab

Insert the SD card to your Raspberry Pi and boot it up. Connect via ssh (user: root, password: voidlinux). Set your hostname with the command hostname your-hostname and also put this hostname in /etc/hostname. Void Linux wiki also tells you to start ntpd with sv start ntpd for https certificates to work (required for xbps to download packages). It was enabled by default on my system, so just check that it is running with sv status ntpd.

Configuring dracut and modules

You should now be logged in as root. Install the required packages for LUKS and ssh in initrd, as well as the crypt-ssh module:

# xbps-install -S cryptsetup dropbear dracut-crypt-ssh busybox

Let’s configure dracut now following the tutorial at the project’s github site. Change the values in crypt-ssh module’s configuration file /etc/dracut.conf.d/crypt-ssh.conf:

dropbear_port="22"
dropbear_rsa_key="/root/.dracut/ssh_dracut_rsa_key"
dropbear_ecdsa_key="/root/.dracut/ssh_dracut_ecdsa_key"
dropbear_acl="/root/.dracut/authorized_keys"

I also changed the port to 22, you can leave the default 222. Similarly, dropbear_acl can be left as is, but I strongly recommend creating a separate authorized_keys file because these can be easily extracted from initrd later on. We generate ssh keys like so:

# umask 0077
# mkdir /root/.dracut
# ssh-keygen -t rsa -f /root/.dracut/ssh_dracut_rsa_key
# ssh-keygen -t ecdsa -f /root/.dracut/ssh_dracut_ecdsa_key

Using passphrases made dropbear_convert fail for me, so do not use them. I do not think they are needed here. Now add the key(s) to authorized keys:

# touch /root/.dracut/authorized_keys
# chmod 700 /root/.dracut/authorized_keys
# cat /root/.dracut/ssh_dracut_rsa_key.pub >> /root/.dracut/authorized_keys
# cat /root/.dracut/ssh_dracut_ecdsa_key.pub >> /root/.dracut/authorized_keys

I also used the following dracut configuration, which you can place in 05-custom.conf:

omit_dracutmodules+="bash dash btrfs"
add_dracutmodules+="busybox"
install_items+=/etc/crypttab

I blacklisted bash and dash because I prefer busybox so why have them. We shouldn’t even need a shell in dracut, because there will only be one command to execute over ssh. I blacklisted btrfs because it was spamming my dmesg log.

Configuring LUKS

Before you generate a dracut image, we need to adjust fstab for this change. Put the following into /etc/fstab:

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

While /etc/crypttab says that you should not have the root partition listed, it is needed for crypt-ssh module to work:

crypt   /dev/mmcblk0p2   none   luks

Now let’s update the files on boot partition to reflect the changes: add this to cmdline.txt

rd.neednet=1 ip=dhcp 

And change the value of root and add a cryptdevice entry before it. I am not sure if the order matters or not.

cryptdevice=/dev/mmcblk0p2:crypt root=/dev/mapper/crypt

add this to config.txt:

initramfs initrd.img followkernel

You can finally generate a dracut image:

dracut /boot/initrd.img --force

Now poweroff the Raspberry Pi and insert the SD card into another Linux machine. We are going to backup the SD card, create the LUKS volume and then put the files back on the SD card. You will also need to have cryptsetup installed on this Linux machine.

# mount /dev/sdX2 rootfs
# mkdir backup
# cp -a rootfs/* backup
# umount rootfs
# wipefs -a /dev/sdX2
# cryptsetup luksFormat /dev/sdX2
# cryptsetup luksOpen /dev/sdX2 raspi
# mkfs.ext4 /dev/mapper/raspi
# mount /dev/mapper/raspi /rootfs
# cp -a backup/* rootfs

That’s all! Now, after booting Raspberry Pi, wait a few seconds and then connect to it via ssh using the dracut-specific ssh key and run the unlock command:

ssh -p 22 root@ip.of.your.pi -i /path/to/ssh_dracut_rsa_key unlock < your_luks_passphrase

It may show a warning but as long as it boots, it’s fine. If it doesn’t, open a ssh shell and kill all sh processes. Problems may occur when upgrading the kernel. I will update this article if such thing happens and I figure out how to fix the problem.