In place Raspberry Pi encryption

I recently had to figure out a way to encrypt a raspberry pi which was being used as a server. The catch? It has to be headless from boot. This meant I had to set up a way to ssh in during the boot process in order to input the decryption phrase.

This is not entirely uncommon on non-pi setups, and can be accomplished using dropbear ssh in the initramfs, but since raspberry pis use specialised boot files not all existing instructions apply.

Luckily, a bit of research led me to the repo gitlbs/sdm who conveniently already had a script to automate installing dropbear and even to automatically encrypt the rootfs in a safe way.
So let's follow along with the documentation page here: gitlbs/sdm/Docs/Disk-Encryption.md


What you will need:

  • a second disk at least bigger than the "used" space of your pi's root filesystem
  • public and private key for the ssh connection (password login is disabled)

Disclaimer: Please PLEASE take a backup of your pi's filesystem before attempting this, or test it out on a machine you can afford to lose data from. The best way to do this is to simply make an image of the drive using dd(Arch Wiki). We are working with low level disk commands and root access, so if something goes wrong it can go very wrong indeed.

To begin, let's download the script from their repo:

sudo curl -L https://github.com/gitbls/sdm/raw/master/sdm-cryptconfig -o /usr/local/bin/sdm-cryptconfig

# mark file as executable
sudo chmod 755 /usr/local/bin/sdm-cryptconfig

Now we can run it with a few flags. I have listed only the relevant ones to us below, check the documentation file if you want to see them all.

SSH config flags

  • --ssh - Enable SSH in initramfs. Requires --authorized-keys to provide an authorized keys file for SSH security
  • --authorized-keys authkeyfile - Specifies an SSH authorized_keys file to use in the initramfs. Required with --ssh
  • --sshport portnum - Use the specified port rather than the Default 22

Network flags for the initramfs environment

By default, the initramfs will obtain IP settings with DHCP. If for any reason you are unable to see what the IP is, you can set a static one using these

  • --ipaddr ipaddr - Set IP address to use in initramfs
  • --mask netmask - Set network mask for initramfs
  • --gateway gatewayaddr - Set IP address of gateway
  • --dns dnsaddr - Set IP Address of DNS server
  • --hostname hostname - Set hostname

Taking the above, our command will be the following:

sdm-cryptconfig --ssh --sshport <number> --authorized-keys </path/to/authorized_keys>

Once the script does its thing, we need to reboot. At this reboot, it will pause and start dropbear, at which point we can login via ssh to perform the encryption.

ssh root@<pi.ip.add.r> -p <port> (-i /path/to/private/key)

Plug in your extra drive that will be used as part of the encryption step. Run

parted -l

to list the drives and take note of which is the root drive (usually something like /dev/nvme0n1 or /dev/mmcblk0) and which is the external (usually something like /dev/sda).
Now we run sdmcryptfs which is a script the previous cryptconfig script installed into the initramfs for us. The arguments are:

sdmcryptfs rootdev scratchdev

so if our root (OS) drive was /dev/nvme0n1, and our external scratch drive was /dev/sda, we would run:

sdmcryptfs /dev/nvme0n1 /dev/sda

Be warned, this will ERASE anything on the scratch drive!

sdmcryptfs will now do a few things:

  • shrink the filesystem on the rootfs to its minimum size
  • dd copy it to the scratch disk
  • ask for a passphrase to setup a LUKS encrypted partiton table on the rootfs (Losing this key means losing all data on the partition!)
  • unlock the LUKS partition and map it to /dev/mapper/cryptroot (you will need to enter the passphrase you just created)
  • copy the filesystem from the scratch drive back to the new encrypted partition, and resize
  • ask you to wipe the scratch drive, since its an unencrypted copy of what was on the disk

At this point, it gets a little bit weird:
The scripts are meant to allow you to simply exit and continue booting from this point, however in my experience, using Ubuntu server 24.04 LTS pi images, the boot fails from here, probably due to differences between ubuntu and the rpi os the script is built against.
What I had to do at this point, is reboot again, login with ssh again, and then run a few commands

# required to mount new root (i think)
mkdir /mnt
# unlock and map LUKS partition to cryptroot 
# (you will be asked for the passphrase)
#
# find your root partition with parted -l
cryptsetup luksOpen /dev/nvme0n1p2 cryptroot

Ubuntu will boot, but don't do anything yet. It is important to wait until it reboots itself, as there are cleanup scripts running in the background finalizing the setup.
Once it starts up again, you will now be able to ssh in using the key we added earlier, and enter the LUKS passphrase to boot.

Congratulations, you now have an encrypted headless pi!


Thanks for reading!

C