How to setup Network Bound Disk Encryption using LUKS on a Raspberry Pi
Motivation
There’s a couple of tutorials on how to setup full LUKS disk encryption on the Raspberry Pi out there, but none instantly worked for me. I did not find any tutorials on network bound disk encryption for the RPI either, hence why I’m making this post.
Network bound disk encryption is used to remotely unlock encrypted devices. It separates the decryption key from the encrypted device, and binds it to a device, usually in the same network.
The Raspberry Pi does not contain a TPM module, so it has no way of storing the decryption key securely. That is why NBDE makes a lot of sense with a device like this.
It also allows scaling for encrypted devices since they don’t need to be manually unlocked.
Another major advantage is the ‘binding’ within a network. This ensures that the device will only be allowed to unlock within the network that has the decryption server. When the device gets stolen, it wont be in the network anymore, and without the server it cannot decrypt itself.
Requirements
We will be using Clevis as the decryption framework on the client side. Clevis allows for unlocking at boot time, in the initial ramdisk. This makes it possible to encrypt the entire root partition.
To provide the device its decryption key, we will use Tang. Simply said, this is a server that binds data to network presence. It provides our machine with the information needed to generate its decryption key.
A Hypriot image will be used as the operating system, but this has been tested on Raspbian and Kali as well. Hypriot is a small version of Raspbian that includes the docker engine. It allows for fast deployment of containers on the device.
Most of the installation will be done within a chroot environment, before booting up. This lets us perform actions on the filesystem as if we were actually logged into the device itself.
Let’s get started!
To start off, we copy the image to our sd card. For this we will use dd.
dd if=hypriot.img of=/dev/sdb bs=4M status=progress
After this, we make the filesystem as small as possible.
resize2fs -fM /dev/sdb2
Because we encrypt the entire root partition (/dev/sdb2 in our case), the smaller it is, the faster the encryption process will be.
Next up, we’ll encrypt the partition. Many tutorials copy all the files on the partition to a temporary folder, then format a new partition as LUKS and copy all the files back. I’ve actually found that it is possible to just use cryptsetup’s re-encrypt.
cryptsetup-reencrypt –new –reduce-device-size 4M -v -q /dev/sdb2
We expand the partition to use the maximum amount of space available on the sd card.
fdisk /dev/sdb
Delete the partition, and add a new one using recommended options.
This expanded the partition, but we still need to let the filesystem know that its size has changed.
cryptsetup -q luksOpen /dev/sdb2 sdcard
e2fsck -fp /dev/mapper/sdcard
resize2fs /dev/mapper/sdcard
Once the disk is ready, we chroot into it so we can start configuring.
mkdir -p /mnt/chroot/boot
mount /dev/mapper/sdcard /mnt/chroot/
mount /dev/sdb1 /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
Since the ARM architecture of the Raspberry Pi differs from our machine, we need a QEMU emulator. This can be installed with ‘apt install qemu-user-static’.
cp /usr/bin/qemu-arm-static /mnt/chroot/usr/bin/
Enter the chroot using:
LANG=C chroot /mnt/chroot/
Install necessary packages. This might vary depending on the operating system you installed.
apt update
DEBIAN_FRONTEND=noninteractive apt install -y cryptsetup pkg-config libcryptsetup-dev clevis clevis-luks dracut clevis-dracut meson cmake luksmeta tang libluksmeta-dev jose libjose-dev libhttp-parser-dev ncat git
Enable LUKS decryption in initramfs
echo “CRYPTSETUP=y” >> /etc/cryptsetup-initramfs/conf-hook
Edit fstab and crypttab to use point at the right partitions.
cat <<EOF > /etc/fstab
# <file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 2
/dev/mapper/sdcard / ext4 defaults,noatime 0 1
EOF
echo “sdcard /dev/mmcblk0p2 none luks” >> /etc/crypttab
Let the Raspberry Pi ‘BIOS’ know to use our initramfs.
echo “initramfs initramfs.gz followkernel” >> /mnt/chroot/boot/config.txt
Build tang from source
git clone https://github.com/latchset/tang.git
cd tang
mkdir build && cd build
meson .. –prefix=/usr
ninja
ninja install
Build clevis from source
git clone https://github.com/latchset/clevis.git
cd clevis
meson build
ninja -C build -j$(nproc)
ninja -C build install
To build our initramfs, we need to find our what kernel version the OS uses. We cannot check this using uname -r, since this will show the kernel version of our host operating system. We can find it using:
ls -l /lib/modules
Finally, build the initramfs using initramfs. I haven’t got this working using the normal initramfs, but Dracut works just fine.
dracut -f /boot/initramfs.gz 4.19.75-v7+
We have not yet setup our Tang server, so we can’t yet bind our LUKS partition. I haven’t gotten this to work inside chroot either, so sadly we’ll have to boot up for this. But first, setup a Tang server on any computer of your choice. It is included in most package managers, and should be as simple as this:
apt install tang
systemctl enable tangd.socket –now
Now we can bind our LUKS partition to the previously setup tang server. Boot up the Pi, enter the decryption password manually and issue the following command. Change parameters accordingly:
clevis luks bind -d /dev/mmcblk0p2 tang ‘{“url”: “192.168.0.248”}’
And voila!
Upon rebooting, when the device receives its IP-address from the DHCP server it’ll contact our tang server and automatically decrypt itself. If you have any issues with this tutorial, please leave a comment and I will get to you as soon as I can. Thank you for reading.