Mounting encrypted root filesystem

Yuhei Horibe
4 min readApr 19, 2020

--

Motivation

Recently, cyber security is getting more and more important, so that, many operating started supporting encryption for the root filesystem. However, because the “root” filesystem is encrypted, it’s not really easy to do this, because kernel itself doesn’t have those encryption libraries, drivers, etc… without mounting root.

To achieve this goal, we need to understand “initrd” or “initramfs”. In this article, the focus will be on “initramfs”.

initramfs

“initramfs” is a kind of ramdisk, which is used like a filesystem. This image archive is stored in boot partition, and loaded into RAM by bootloader, then, kernel can mount it as “root filesystem” resides in RAM (Or, since this is tiny image, it can be built into kernel as well). But since it’s in RAM, whatever written into it will be disappeared on next boot. The purpose of this “ramdisk” is to do something “before mounting root filesystem”, or, to recover actual root filesystem. Encryption is good example. To use encrypted root filesystem, we need to decrypt it “before” mounting it.

As soon as this initramfs is mounted by kernel, kernel will start running “init” in it, like the actual filesystem. But the purpose of this “init” script is just to mount root filesystem, and let the kernel switch into it.

Preparing initramfs

We can build it with “busybox” tools, but this time, I would use initramfs-tools in Ubuntu distribution. Other distributions have similar tools to create initramfs. NOTE this has to be done on “target system” because ramfs is tied to the kernel. If the kernel is updated, then initramfs has to be updated as well.

Before installing the tool on target system, make sure “cryptsetup” is installed. If you don’t have it, install it first.

sudo apt install cryptsetup

Then, install initramfs-tools.

suto apt install initramfs-tools

By default, initramfs-tools won’t include encryption related files. To let it include it, we need to add 2 lines into /usr/share/initramfs-tools/conf-hooks.d/cryptsetup

CRYPTSETUP=y 
export CRYPTSETUP=y

Then, you can simply run this command.

sudo mkinitramfs -o initramfs-$(uname -r).img

Because initramfs is kernel version specific, we usually add kernel version in file name.

Then, it’s ready to use. But, to know exactly how it works, let’s extract the contents and see what’s in there. This image is “gzipped cpio” image. To extract this, use this command.

mkdir initramfs/
cd initramfs/
zcat ../initramfs-$(uname -r).img | cpio -idmv

Then, it will be extracted into initfamfs directory. To see how it works, let’s look at “init”.

First, this script does some preparations like, binding /proc, /sys and so on, parse command line options, etc… But the important steps are like these;

. /scripts/local
...
mount_top

This executes local_top function, and this will call scripts in “local-top” folder. Encryption related scripts are in there.

Let’s take a look at scripts/local-top/cryptroot.

Important part is, “parse_options()”. This parses “cryptopts” options in kernel bootargs. You can see what you can add to cryptopts here. But important arguments are;

target … device will be mapped to /dev/mapper/<target>source … encrypted drive (/dev/sda1, or UUID=…)key … key file if you need it.keyscript … ‘cat’ should be enough

If you need to use key file, specify “key” option and keyscript.

As you may have seen in the script, if no “cryptopts” is found in kernel bootargs, it won’t try to decrypt drives. To mount encrypted drives, parameters should be added to kernel bootargs like below;

cryptopts=target=crypt_root,source=/dev/sda1,key=/root/crypt.key,keyscript=cat

If you don’t use “key” file, and will type the password, skip “key” and “keyscript” options.

If you modified scripts, or added some files into this filesystem, you would need to make image by doing this in initramfs directory.

find . | cpio -H newc -o | gzip > ../<updated initramfs file name>

Loading initramfs

Loading initramfs is bootloader’s responsibility. If you are using Raspberry pi, you just can add one line like below in “config.txt” in boot directory.

initramfs <initramfs file name> 0x04000000

I’m still working on getting u-boot running on Raspberry pi 4, I had to use this option for Raspberry pi 4. If you are using u-boot, first, you need to wrap the ramdisk image with u-boot image header.

<u-boot source directory>/tools/mkimage -A arm -O linux -T ramdisk -C gzip -n “Ramdisk Image” -d <initramfs file name> <output filename>

Then, put new image into boot partition, and write boot script like below (boot_scr.txt);

setenv ramdisk_addr_r 0x4000000 
mmc dev 0
fatload mmc 0:1 ${kernel_addr_r} Image
fatload mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb
fatload mmc 0:1 ${ramdisk_addr_r} initramfs.img
setenv bootargs “console=serial0,115200 console=tty1 cryptopts=source=UUID=fb6f861a-bbc7–499b-8827-cee8d655336a,target=crypt_root,key=/root/crypt.key,keyscript=cat root=UUID=71d610e3–07e0–4743-a73c-f4a36d1d2fc6 rw rootfstype=ext4 rootwait devtmpfs.mount=0”
booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}

You can use “bootm”, “bootz” and so on instead of “booti”. Make u-boot boot script, and add boot.scr to boot partition.

<u-boot source directory>/tools/mkimage -A arm -O linux -T script -a 0 -e 0 -C none -d boot_scr.txt boot.scr

Then, bootloader will load initramfs into RAM, and kernel will mount it, and executes “init” in initramfs. Finally, decrypted root filesystem will be mounted.

--

--

No responses yet