β οΈ This guide is only tested on the Raspberry Pi 5 with Raspberry Pi OS Lite 64-bit 2023-12-11.
Other platforms and distributions may work, but there may be unexpected issues or side effects.
This guide will show you how to encrypt your Raspberry Pi's root partition and set up an initramfs that will prompt for the password, decrypt the partition and gracefully resume boot. You will also learn how to enable SSH during this pre-boot stage, allowing you to unlock the partition remotely. There are also optional steps for WiFi setup.
This guide operates directly on an image file and therefore does not require an SD card for the setup. The resulting image can be flashed to an SD card as usual.
-
A Raspberry Pi Linux image (e.g. Raspberry Pi OS Lite 64-bit 2023-12-11)
-
A computer (host) running Linux (e.g. Kali Linux 2023.2)
β οΈ NOTE: Your host's Linux should be as similar as possible to the Raspberry Pi's Linux. If you are preparing Debian 12/kernel 6.1 for the Raspberry Pi, use similar versions on the host, otherwise you may encounter issues inside the chroot.
Install dependencies:
- You can skip
binfmt-support
andqemu-user-static
if your host Linux's architecture matches that of the Raspberry Pi's Linux image. - If your host Linux doesn't use systemd, such as WSL on Windows, you need to manually run
update-binfmts --enable
after installingbinfmt-support
.
apt update
apt install -y parted kpartx cryptsetup-bin rsync binfmt-support qemu-user-static
Create two copies of the Raspberry Pi's Linux image β one to read from (base), and one to write to (target):
- pi-base.img
- pi-target.img
Increase the size of the target image or you may run into issues:
dd if=/dev/zero bs=1G count=1 >> pi-target.img
parted pi-target.img resizepart 2 100%
Map both images as devices, ensuring the base is readonly:
kpartx -ar "$PWD/pi-base.img"
kpartx -a "$PWD/pi-target.img"
If your system automatically mounted any partitions, unmount them:
umount /media/**/*
Run lsblk and verify the process was successful β you should see two loopback devices, each with two partitions:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT # COMMENT
loop0 7:0 0 3.3G 0 loop # pi-base.img
ββloop0p1 253:0 0 256M 0 part # ββ boot
ββloop0p2 253:1 0 3G 0 part # ββ root
loop1 7:1 0 3.3G 1 loop # pi-target.img
ββloop1p1 253:2 0 256M 1 part # ββ boot
ββloop1p2 253:3 0 3G 1 part # ββ root
Mount the base image's root partition:
mkdir -p /mnt/original/
mount /dev/mapper/loop0p2 /mnt/original/
Replace the target image's root partition with a new, encrypted partition:
β οΈ NOTE:The default encryption algorithm is
aes-xts-plain64
, which is fast only on the Raspberry Pi 5 due to its hardware AES acceleration. If you have an older generation, then use aes-adiantum instead via-c xchacha20,aes-adiantum-plain64
. It is much faster than AES in software.By default cryptsetup will benchmark your host and use a memory-hard PBKDF algorithm that can require up to 4GB of RAM. If these settings exceed your Raspberry Pi's available RAM, it will make it impossible to unlock the partition. To work around this, set the --pbkdf-memory and --pbkdf-parallel arguments so when you multiply them, the result is less than your Pi's total RAM. For example:
--pbkdf-memory 512000 --pbkdf-parallel=1
cryptsetup luksFormat /dev/mapper/loop1p2
Open (decrypt) the new partition:
cryptsetup open /dev/mapper/loop1p2 crypted
Then format and mount it:
mkfs.ext4 /dev/mapper/crypted
mkdir -p /mnt/chroot/
mount /dev/mapper/crypted /mnt/chroot/
Copy the base image's root partition files to the target image's new, encrypted root partition. You can use dd, but rsync is faster:
rsync --archive --hard-links --acls --xattrs --one-file-system --numeric-ids --info="progress2" /mnt/original/* /mnt/chroot/
Set up a chroot by mounting the target image's boot partition and required virtual filesystems from the host:
mkdir -p /mnt/chroot/boot/
mount /dev/mapper/loop1p1 /mnt/chroot/boot/
mount -t proc none /mnt/chroot/proc/
mount -t sysfs none /mnt/chroot/sys/
mount -o bind /dev /mnt/chroot/dev/
mount -o bind /dev/pts /mnt/chroot/dev/pts/
Enter the chroot:
LANG=C chroot /mnt/chroot/ /bin/bash
Install the dependencies:
apt update
apt install -y busybox cryptsetup dropbear-initramfs
Edit /etc/fstab and replace the root entry with your decrypted (virtual) partition's device name:
# Original:
PARTUUID=e8af6eb2-02 / ext4 defaults,noatime 0 1
# Replace with:
/dev/mapper/crypted / ext4 defaults,noatime 0 1
Run blkid and note the details of your encrypted partition:
blkid | grep crypto_LUKS
/dev/mapper/loop1p2: UUID="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" TYPE="crypto_LUKS" PARTUUID="cccccccc-cc"
Edit /etc/crypttab and add an entry with your encrypted (raw) partition's UUID:
crypted UUID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa none luks,initramfs
Edit /boot/cmdline.txt
and update the root entry:
# Original:
root=PARTUUID=21e60f8c-02
# Replace with:
root=/dev/mapper/crypted cryptdevice=UUID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:crypted
Edit the cryptsetup initramfs hook to ensure cryptsetup ends up in the initramfs:
echo "CRYPTSETUP=y" >> /etc/cryptsetup-initramfs/conf-hook
The initramfs-tools cryptroot
hook will resolve any UUIDs to device names during initramfs generation. This is a problem because the device names will likely differ between the host and the Raspberry Pi, resulting in failure to boot. To work around this, apply the following patch:
patch --no-backup-if-mismatch /usr/share/initramfs-tools/hooks/cryptroot << 'EOF'
--- cryptroot
+++ cryptroot
@@ -33,7 +33,7 @@
printf '%s\0' "$target" >>"$DESTDIR/cryptroot/targets"
crypttab_find_entry "$target" || return 1
crypttab_parse_options --missing-path=warn || return 1
- crypttab_print_entry
+ printf '%s %s %s %s\n' "$_CRYPTTAB_NAME" "$_CRYPTTAB_SOURCE" "$_CRYPTTAB_KEY" "$_CRYPTTAB_OPTIONS" >&3
fi
}
EOF
The default timeout when waiting for decryption (10 seconds) may be too short and result in a timeout error. To work around this, bump the value:
sed -i 's/^TIMEOUT=.*/TIMEOUT=100/g' /usr/share/cryptsetup/initramfs/bin/cryptroot-unlock
Write your SSH public key inside dropbear's and your decrypted OS's authorized_keys
and fix permissions:
mkdir -p /root/.ssh && chmod 0700 /root/.ssh
echo "/REDACTED/" | tee /etc/dropbear/initramfs/authorized_keys /root/.ssh/authorized_keys
chmod 0600 /etc/dropbear/initramfs/authorized_keys /root/.ssh/authorized_keys
This step is optional. If you want the Raspberry Pi to be decryptable over WiFi, check out the guides below. Note that the differences between distros is very small, so you can easily adapt any particular guide.
Note your kernel version. If there are multiple, choose the one you want to run. The 2712
suffix is for Raspberry Pi 5, while v8
is for all previous generations:
ls /lib/modules/
β οΈ NOTE:Starting with the Raspberry Pi 5, the default kernel page size is 16K instead of 4K. This breaks some software, but more importantly, is experimental in btrfs with 4K sector size disks:
BTRFS warning (device dm-1): read-write for sector size 4096 with page size 16384 is experimental
For this reason, you may want to switch back to the old kernel by adding the following to your
/boot/config.txt
:echo "kernel=kernel8.img" >> /boot/config.txt echo "initramfs initramfs8 followkernel" >> /boot/config.txt
Build the new initramdisk using the kernel version from above. The initramfs-tools
package parses the compression method from a file that doesn't exist, so we need to create it:
kversion="6.1.0-rpi7-rpi-v8" # "6.1.0-rpi7-rpi-2712" for 16k pages
echo "CONFIG_RD_ZSTD=y" > /boot/config-$kversion
mkinitramfs -o /boot/initramfs8 $kversion # "initramfs_2712" for 16K pages
rm /boot/config-$kversion
Customize first run/init in /boot/cmdline.txt
as it will break the encrypted setup and prevent booting:
# Original:
[...] quiet init=/usr/lib/raspberrypi-sys-mods/firstboot
# Replace with:
[...] systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target
Create /boot/firstrun.sh
and customize it for your needs. Most of these scripts allow additional configuration, so feel free to check their source:
#!/bin/bash
set +e
/usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh
/usr/lib/raspberrypi-sys-mods/regenerate_ssh_host_keys
/usr/lib/userconf-pi/userconf 'pi'
/usr/lib/raspberrypi-sys-mods/imager_custom set_wlan 'MyWiFi' 'pass1234' 'GB'
rm -f /boot/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt
exit 0
Make sure the newly created file is executable:
chmod +x /boot/firstrun.sh
Sync and exit the chroot:
sync
history -c && exit
Unmount everything and clean up any remaining artifacts:
umount /mnt/chroot/boot
umount /mnt/chroot/sys
umount /mnt/chroot/proc
umount /mnt/chroot/dev/pts
umount /mnt/chroot/dev
umount /mnt/chroot
cryptsetup close crypted
umount /mnt/original
rm -d /mnt/chroot
rm -d /mnt/original
kpartx -d "$PWD/pi-base.img"
kpartx -d "$PWD/pi-target.img"
You are now ready to flash pi-target.img
to an SD card.
Boot the Raspberry Pi with the new SD card. It will obtain an IP address from the DHCP server and start listening for SSH connections. To decrypt the root partition and continue boot, from any shell, simply run cryptroot-unlock
.
The first time only:
- Once decrypted, the system will power off. The reason for this is currently unknown. Restart the device and decrypt again.
- The decrypted system will run the init script from before and reboot back into the initramfs. Proceed to decrypt one last time.
Once booted into the decrypted system, you will notice that the root partition is still sized at ~3GB, no matter how much space you have on the SD card. To fix this, resize the partition:
parted /dev/mmcblk0 resizepart 2 100%
cryptsetup resize crypted
resize2fs /dev/mapper/crypted
Finally, reboot the system one last time for good measure:
reboot
To avoid host key collisions you can configure a separate trusted hosts store in the ~/.ssh/config
of your client:
Host box
Hostname 192.168.0.30
User root
Host box-initramfs
Hostname 192.168.0.30
User root
UserKnownHostsFile ~/.ssh/known_hosts.initramfs
- https://www.kali.org/docs/arm/raspberry-pi-with-luks-disk-encryption/
- https://wiki.archlinux.org/index.php/Dm-crypt/Specialties
- https://wiki.gentoo.org/wiki/Custom_Initramfs
- https://www.raspberrypi.org/forums/viewtopic.php?t=252980
- https://thej6s.com/articles/2019-03-05__decrypting-boot-drives-remotely/
- https://www.pbworks.net/ubuntu-guide-dropbear-ssh-server-to-unlock-luks-encrypted-pc/
- https://raspberrypi.stackexchange.com/questions/92557/how-can-i-use-an-init-ramdisk-initramfs-on-boot-up-raspberry-pi/
- https://www.raspberrypi.com/documentation/computers/configuration.html#setting-up-a-headless-raspberry-pi