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.