Linux disk encryption with LUKS and LVM

An unreasonable number of steps to get reasonable disk security on Linux

Seattle skyline Seattle, Washington

I don’t let lack of knowledge stop me from trying to run and maintain a few Linux machines. Just like writing bash scripts, I’m always forgetting how I did things previously so I’m writing it all down this time so I can copy and paste it again later in a few years.

I don’t reach tinfoil hat level paranoia, but I do like having some level of data security so thieves can’t read my journal and figure out which BTS member I’m currently crushing on (Jungkook). Full disk encryption is pretty easy on Windows or Mac, and it seems easy on Linux, but you always have to be your toes in the land of the penguin. Specifically, the following two requirements are typically at odds:

  1. Full disk encryption
  2. Ability to boot without being physically present

I’ve only ever used LUKS for disk encryption because it’s the default with Debian and I’m too lazy to research alternatives. The default setup has a tiny unencrypted boot partition that prompts you to unlock the main disk so you can actually use the computer. This is fine for a desktop or laptop, but not really tenable for situations where you can’t physically type in the password on the machine.

Fortunately, there’s a nifty package called dropbear-initramfs which adds a barebones SSH server to that tiny boot partition which allows you to unlock the machine remotely. Installation is pretty straightforward, you’ll need to set up your SSH key by creating a /etc/dropbear-initramfs/authorized_keys file and adding your public key:

no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="/bin/cryptroot-unlock" ssh-rsa <SSH public key goes here>

The command=/bin/cryptroot-unlock part is optional, but very convenient since it means you’ll automatically be prompted to unlock the drive whenever you connect to the server. It’s also a way to lock down the machine a bit more. Once you’ve added your key, make sure to set the correct permissions and make another offering to the boot gods:

# File is ignored if permissions aren't strict
chmod 400 /etc/dropbear-initramfs/authorized_keys
# Changes don't take effect if you don't update!
sudo update-initramfs -u

You can now reboot your machine and (assuming it can get network access) it will open up an SSH server so you can connect and input the encryption passphrase. Note that the server key for this miniserver is gonna be different than your server once booted. So I recommend doing the following in your ~/.ssh/config file:

Hostname *_unlock
  UserKnownHostsFile /home/fortes/.ssh/known_hosts_unlock

This will make sure SSH doesn’t freak out and worry that your server is being hijacked all the time.

So now the machine can boot without a sweet human caress, congratulations you might be done!

Adding encrypted drives

If you have more than one drive, you still have a little more work left. This will set up what is apparently called LVM on LUKS, a popular alternative is called LUKS on LVM and it might be better for you? The Arch Wiki has a lot of information that I definitely did not read.

Make sure your drive is connected, and then you’ll need to figure out what device it’s been assigned. This is typically something like /dev/sdb or whatever. The best way to find out which disk you want is via lsblk (or possibly sudo blkid, I can never remember). If the drive was previously formatted, you may need wipe out the existing partitions via fdisk /dev/sdb. The interface is (of course) a bit cryptic, you’ll need to keep on hitting d at the prompt to delete all the partitions. I highly recommend double and triple checking which drive you’re operating on, since recovering from deleting all those partitions is not very fun. Or you can just YOLO, whatever I’m not your dad.

Now that the drive is wiped, it’s time to set up encryption. You should generate a nice long passphrase and save it in your password manager. The partition can also be unlocked with the contents of a static file, and we’ll use that for the automatic unlocking:

# You should be _very_ confident about this choice
export DRIVE_TO_ADD="/dev/sdc"

# Create a file full of random noise used
sudo dd if=/dev/random bs=32 count=1 of=/root/.data-keyfile
# Unless you trust everyone/everything with access to your machine, you should
# lock down permissions for the keyfile
sudo chmod 400 /root/.data-keyfile

# This will prompt you for a passphrase, pick a secure one and store it in
# your password manager (you should rarely, if ever, need to type it in)
sudo cryptsetup luksFormat ${DRIVE_TO_ADD}
# Add the generated file as a second encryption key (will prompt you for the
# passphrase you just generated)
sudo cryptsetup luksAddKey ${DRIVE_TO_ADD} /root/.data-keyfile

The disk is now encrypted. We still need to do a few things to get it mounted automatically though. First, add an entry to /etc/crypttab in order to make the disk decrypt on boot:

# Append to /etc/crypttab

# Find the disk UUID via `sudo blkid`, which will output something like:
# /dev/sdc: UUID="YOUR-DISK-UUID" TYPE="crypto_LUKS"
sdc_crypt UUID=YOUR-DISK-UUID /root/.data-keyfile luks,discard

This file gets used by the system to automatically decrypt drives at boot. Although the decryption key is a file on the system, it’s sitting the main (encrypted) drive that’s only accessible after unlocking the entire machine at boot.

Now test to make sure it gets automatically decrypted via:

sudo cryptdisks_start sdc_crypt

Assuming no errors, you should have the unencrypted disk available at /dev/mapper/sdc_crypt. In theory, you should now be able to format this directly via mkfs.ext4 or your favorite disk format.

On my machines, I still go ahead and set up my partitions via LVM since that’s what Debian does by default and I’m just cargo culting my way through this whole thing. In theory there is a bunch of flexibility to be gained via LVM, but I’ve never actually learned how it all works.

# You might need to do this LVM will complain about device filter
sudo wipefs -a ${DRIVE_TO_ADD}

# Create a bunch of LVM stuff that I don't understand
sudo pvcreate /dev/mapper/sdc_crypt # confirm with `pvdisplay /dev/mapper/sdc_crypt`
sudo vgcreate data-vg /dev/mapper/sdc_crypt # confirm with `vgdisplay data`
sudo lvcreate -n data -l100%FREE data-vg # confirm with `lvdisplay data`

Now we’re ready to actually format the drive:

sudo mkfs.ext4 -m 0 /dev/data-vg/data

In order to automatically mount the drive, we need to edit /etc/fstab:

# Add into /etc/fstab
/dev/mapper/data--vg-data /mnt/data   ext4    defaults,user,noatime,nofail 0  2

Test that the /etc/fstab setup worked:

sudo mount -a

VoilĂ , you should now see your disk mounted at /mnt/data, and all it took was a crazy number of error-prone steps written by a total stranger with, at best, a passing understanding of how it all works.