nilFM

filesystem surgery

2021-08-12

intro

Encrypting sensitive data on your computer is pretty important, especially if it's a laptop (and therefore easier to steal than a desktop), and doubly so if you have anything extra sensitive like legal documents or work data on it.

On my laptop, which is my daily driver, I used to use an XFS home partition with ecryptfs on top of it, but there were a couple deficiencies in this setup that ultimately led me to abandon it:

So, in March, after a few kernel panics on shutdown related to the third of the above bullet points, I decided to favor stability over security temporarily and setup a vanilla unencrypted home directory (this was pretty painless thanks to kyanite and my cold backup drive) while I weighed my options. Fast forward to late July, and my laptop still wasn't stolen but I wanted to protect the data therein, so I devised a new scheme:

The rationale behind this is that for my backups and my home directory, if I have some kind of hardware failure on my main device and need to access my data, I can do it from any operating system since OpenZFS is available on every major platform (XFS has been Linux-only for a good while). I would have used a ZFS ZVol for my swap, but it's apparently not a stable setup so I went with the LUKS encrypted swap (which doesn't need to be portable anyways). One could gain additional security in terms of the operating system itself being immune to snooping and manipulation with full-disk encryption, but the opportunities for error are a bit more numerous on that path, as well as the necessary time to complete the system migration, so this middle-ground works for me.

moving bits

I run Void Linux on my machines so your mileage may vary, but here goes.

First step is to install the zfs package, which includes the utilties and the dynamic kernel module:

# # Optionally, you can purge your old kernels, since building the kernel module can take a while
# vkpurge all
# xbps-install -S zfs

After rebooting and verifying that ZFS is working (zpool status), I made sure all my data was sound so I could start fresh on my backups. After meticulously verifying that my live data was stable and complete, I wiped my backup drive and started fresh: # zpool create -f -o feature@encryption=enabled -O compression=on -O encryption=on -O keyformat=passphrase -O keylocation=prompt -m /media/cryonix cryonix /dev/sdb

I was prompted to enter the password, and it automounted upon creation. So far so good. Next, I used kyanite to make a new generation of backups (I also backup my server and media drive, but here is just my home directory): # kyanite full /home/nilix/ /media/cryonix/ --exclude .cache

After the backup completed, next was to setup the USB key. After doing a zpool export cryonix (I only have one USB3 port on my laptop, oh well), I ran cfdisk and added a 1GB partition to the tail end of my keychain USB drive. It's a 128GB drive and I previously only had around half of it allocated as a FAT32 partition, so that was pretty painless. Next, to create the LUKS partition to hold the keyfiles for the other encrypted filesystems: # cryptsetup luksFormat /dev/sdb3

Again, it asks for a decryption passphrase. Once confirmed, you can create the filesystem and mount it:

# cryptsetup luksOpen /dev/sdb3 enclave
# mkfs.vfat /dev/mapper/enclave
# mount /dev/mapper/enclave /mnt

Ok, next is to generate the keyfiles for the other filesystems:

# head -c 32 /dev/urandom > /mnt/zhomekey
# head -c 32 /dev/urandom > /mnt/swapkey

Now comes the fun part. I already had the partitions created for home and swap, so after rebooting into single-user mode, unmounting /home and doing swapoff -a, I created new filesystems:

# zpool create -f -o feature@encryption=enabled -O encryption=on -O keyformat=keyfile -O keylocation=/mnt/zhomekey -m /home zhome /dev/sda4
# cryptsetup luksFormat /dev/sda3
# cryptsetup luksAddKey /dev/sda3 /mnt/swapkey

In the above commands, the first cryptsetup line creates the LUKS swap device with a backup passphrase, and the second one adds the keyfile we made earlier.

At this point, you can restore your home partition like so (assuming your backup drive is connected/mounted/imported):

# mkdir /home/nilix
# chmod 0744 /home/nilix
# chown -R nilix:nilix /home/nilix
# kyanite restore /media/cryonix/ksatrya/_full/ /home/nilix/

finishing touches

To bring it all together, we need to edit /etc/crypttab and /etc/fstab as well as (in my case, at least) do some platform-specific mojo. First is /etc/crypttab. We have to get the USB key to ask for the passphrase, decrypt, and automount. Do a ls -la /dev/disk/by-uuid/ to see what UUID corrseponds to the encrypted partition on the USB drive, and then edit /etc/crypttab. I added a line that looks like this: enclave /dev/disk/by-uuid/037a3483-4f3f-4345-b306-046e908bce4b none

Now in /etc/fstab I commented out the lines that corresponded to my old home and swap partitions and added this one (after the /boot/efi entry and before the tmpfs entry): /dev/mapper/enclave /mnt vfat defaults 0 2

And last but not least, I added a custom script to the runit core services at /etc/runit/core-services/03-filesystems_custom.sh (to ensure it runs right after the fstab is processed):

#!/bin/sh

cryptsetup luksOpen --key-file=/mnt/swapkey /dev/sda3 cryptswap
swapon /dev/mapper/cryptswap
zfs mount -l zhome
umount /mnt
cryptsetup luksClose enclave

This decrypts and activates the swap partition, decrypts and mounts the ZFS root dataset, and then unmounts and closes the encrytped USB partition. On other Linux distributions you would probably create a systemd unit file to replicate this part. There are also hooks into PAM for decrypting ZFS filesystems but the documentation is (at the time of writing) ethereal, so I went with this method.

reboot!

So, if all went well, rebooting with your USB key plugged into the machine, you will see it ask for the passphrase to unlock it, and after that it should proceed as normal (taking an extra second or two to decrypt the partitions). If you happen to boot the machine without the USB key present, in my setup it drops you into an emergency shell after trying to mount the nonexistent /dev/mapper/enclave.

So there it is. A caveat to the setup as I have it is that hibernation to disk doesn't seem to work, but the only time I ever used it was inadvertently when the machine would automatically hibernate if the battery was about to run out. Not a dealbreaker for me. Now I can rest easy that all my sensitive documents and work data is encrypted at rest, and even if a bad actor were to snatch my messenger bag at the coffee shop or airport (not that I'm out much these days!), they wouldn't be able to do a damn thing with it!