Raspberry PI 4 with encrypted root partition, LVM and remote unlock
This how-to describes the necessary steps to set up a Raspberry PI 4 with a LUKS encrypted root partition, LVM and remote unlock via Dropbear SSH server in initramfs.
I’ve compiled this from various other tutorials, most notably Secure Kali PI 2018 and Raspberry Pi Encrypt Root Patition. The main differences are:
- Device UUIDs instead of paths in
fstab
,crypttab
and kernel command line. - LVM inside the encrypted partition.
cryptroot-unlock
as Dropbear command.- No modification of
/usr/share/initramfs-tools/scripts/init-premount/dropbear
.
Note: because of a problem when creating the initramfs for the first time and the encrypted root partition is no yet fully set up, a screen and keyboard need to be connected to the Raspberry PI for one step.
Prepare storage device
The following steps are executed on another computer, rather the Raspberry PI.
Download the image from the Raspberry PI website.
Connect your storage device (either a SD card or a USB storage device) and run lsblk
to determine the device path. Be absolutely sure about this because you might destroy your system when picking the wrong one. I’ll write /dev/sdX
for the rest of this how-to.
Copy the downloaded image onto the storage device:
dd if=raspios.img of=/dev/sdX bs=4M status=progress
Run fdisk
to get some information about the storage device:
fdisk -l /dev/sdXDisk /dev/sdX: 1.73 GiB, 1858076672 bytes, 3629056 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x067e19d7Device Boot Start End Sectors Size Id Type
/dev/sdX1 8192 532479 524288 256M c W95 FAT32 (LBA)
/dev/sdX2 532480 3629055 3096576 1.5G 83 Linux
The important values are the sector size (512) and the total number of sectors (3629056).
The goal is to have a partition of 2 GB at the end of the device for a temporary unencrypted root and use all the space in between for the encrypted LVM partition.
Calculate the first sector of the new partition 2 GB from the end:
$NUMBER_OF_SECTORS - 2 * 1024^3 / $SECTOR_SIZE
Run fdisk
again and create the partition:
fdisk /dev/sdXn # create new partition
p # primary partition
<enter> # use default partition number
XXX # the calculated start sector from above
<enter> # use suggested end sector
w # write
Now copy the data from partition 2 to partition 3 and resize it to use the full 2 GB:
dd if=/dev/sdX2 of=/dev/sdX3 bs=4M status=progressresize2fs /dev/sdX3
Run fdisk
again and recreate the second partition.
fdisk /dev/sdXp # note start sector of partition 2
d # delete partition
2 # select partition 2
n # create new partition
p # primary partition
2 # partition number 2
XXX # start sector from original partition 2
<enter> # use suggested end sector
w # write
Run blkid /dev/sdX3
and copy the UUID.
Mount the boot partition, edit cmdline.txt
and replace the value of the root
parameter with the UUID from above. E. g. replace root=PARTUUID=XXXX-2
with root=UUID=XXX-XXX
.
mkdir -p /mnt/rpi/bootmount /dev/sdX1 /mnt/rpi/bootvim /mnt/rpi/boot/cmdline.txt
If you want SSH to be enabled right from the start, create a file named ssh
in the boot directory:
touch /mnt/rpi/boot/ssh
Unmount and clean up
umount /dev/sdX1
rm -r /mnt/rpi
Create the encrypted volume. I’ll use the cipher aes-xts
with a key size of 256 and sha256 for the best performance on the Raspberry PI. Once it becomes available in the default Raspberry PI OS, aes-adiantum might be an interesting choice.
cryptsetup luksFormat \
--type=luks2 \
--pbkdf=pbkdf2 \
--cipher=aes-xts-plain64 \
--key-size=256 \
--hash=sha256 \
/dev/sdX2
Open the encrypted volume and setup LVM. I’ll use 50G for the root file system. Adapt this to your needs and the size of your storage medium.
cryptsetup open /dev/sdX2 rpipvcreate /dev/mapper/rpivgcreate vgrpi /dev/mapper/rpilvcreate -L 50G vgrpi -n rootmkfs.ext4 /dev/mapper/vgrpi-rootlvchange -an vgrpicryptsetup close /dev/mapper/rpi
Raspberry PI configuration
Now connect your storage device with the Raspberry PI and power up.
Update and install the required software
apt-get update
apt-get install \
dropbear \
lvm2 \
cryptsetup \
busybox \
dropbear-initramfs \
cryptsetup-initramfs
Open the encrypted volume
cryptsetup open /dev/sdX2 crypt
Run blkid
and note the UUID of /dev/sdX2
and /dev/mapper/vgrpi-root
. They will be required in the next steps.
Edit /etc/fstab
and replace the file system for /
with the UUID of /dev/mapper/vgrpi-root
. It should look like this:
proc /proc proc defaults 0 0
UUID=XXXXXX /boot vfat defaults 0 2
UUID=XXXXXX / ext4 defaults,noatime 0 1
Edit /etc/crypttab
and add an entry with the UUID of /dev/sdX2
. Make sure to use tabs as separator between the values.
crypt UUID=XXXXXX none luks
Open /boot/cmdline.txt
in an editor. Change the value of the root
parameter to the UUID of /dev/mapper/vgrpi-root
and add a new parameter cryptdevice
with the UUID from /dev/sdX2
followed by :crypt
. Append rootdelay=2
at the very end.
[...] root=UUID=XXXXXX cryptdevice=UUID=XXXXXX:crypt [...] rootdelay=2
Uncomment and change the following line in /etc/cryptsetup-initramfs/conf-hook
:
CRYPTSETUP=y
Create /etc/dropbear-initramfs/authorized_keys
and insert your SSH public key (usually from ~/.ssh/id_rsa.pub
on your other machine).
Uncomment and edit the following line in /etc/dropbear-initramfs/config
:
DROPBEAR_OPTIONS="-sgjkc cryptroot-unlock"
The options here mean:
-s
Disable password logins
-g
Disable password logins for root
-j
Disable local port forwarding
-k
Disable remote port forwarding
-c
Force command to be executed
Add the following line at the end of /boot/config.txt
:
initramfs initramfs.gz followkernel
Create the initramfs. This might show some errors from cryptsetup you can ignore for the moment.
mkinitramfs -o /boot/initramfs.gz
Copy root file system to encrypted volume
Power off your Raspberry PI and connect the storage device to your other computer again.
Mount the unencrypted temporary root partition, the encrypted root partition and copy the data.
mkdir -p /mnt/rpi/{default,crypt}mount /dev/sdb3 /mnt/rpi/defaultcryptsetup open /dev/sdb2 rpimount /dev/mapper/vgrpi-root /mnt/rpi/cryptrsync -avh /mnt/rpi/default/* /mnt/rpi/crypt/umount /mnt/rpi/cryptumount /mnt/rpi/defaultlvchange -an vgrpicryptsetup close /dev/mapper/rpirm -r /mnt/rpi
Boot Raspberry PI from encrypted volume
The first boot from the encrypted volume will probably fail, so you need to connect a screen and keyboard to fix it manually. I’m happy for any hints how to solve this in a more elegant way.
Wait until the errors stop and you are dropped into the initramfs shell
Enter the following commands and hit CTRL-D
afterwards:
cryptsetup open /dev/sdX2 crypt
vgchange -ay
Now the boot process should continue and you can login.
As a final step, recreate the initramfs to fix the errors from above. It should now execute without any errors.
mkinitramfs -o /boot/initramfs.gz
Now that everything is set up, you can erase the temporary root file system on partition 3 or keep it as a recovery system.
Please note that you have to recreate the initramfs after every kernel upgrade!
Resources
- https://www.kali.org/tutorials/secure-kali-pi-2018/
- https://github.com/NicoHood/NicoHood.github.io/wiki/Raspberry-Pi-Encrypt-Root-Partition-Tutorial
- https://www.kali.org/docs/arm/raspberry-pi-full-encryption/
- https://raspberrypi.stackexchange.com/questions/7159/can-the-raspberry-boot-to-an-lvm-root-partition
- https://www.torstens-buecherecke.de/raspberry-pi-mit-ubuntu-20-04-lts-btrfs-und-luks-verschluesselung-des-root-verzeichnisses-remote-login-kommentar/
- https://thej6s.com/articles/2019-03-05__decrypting-boot-drives-remotely/