LUKS Encrypted Drives in Ubuntu

There are many creative and different ways to encrypt a linux system. This guide covers setup on Ubuntu 20.04.6. See the Arch Linux documentation on dm-crypt for an overview of the various options.

Table of Contents

Prerequisites

  • Ubuntu Desktop/Server 20.04.6 with SSH access and LVM and XFS filesystem.
  • FIDO U2F Security key such as a yubikey, anykey, or nitrokey.

Note: The XFS file system is used for XenFi hubs because of its support for parallel I/O operations.

WARNINGS

  • In any scenario, never use file system repair software such as fsck directly on an encrypted volume, or it will destroy any means to recover the key used to decrypt your files. Such tools must be used on the decrypted (opened) device instead.

  • GRUB’s support for LUKS2 is limited. Use LUKS1 (cryptsetup luksFormat –type luks1) for partitions that GRUB will need to unlock.

  • The LUKS2 format has a high RAM usage per design, defaulting to 1GB per encrypted mapper. Machines with low RAM and/or multiple LUKS2 partitions unlocked in parallel may error on boot. See the –pbkdf-memory option to control memory usage.

  • For LUKS1, only PBKDF2 is accepted. PBKDF2 is the default for both Luks 1 and 2.

Encrypted Root and Boot Drive, Unlock with Password at Boot

Why encrypt a boot partition? Isnt encrypting the root partition with LVM enough? Encrypting just the root partition that has LVM on it is sufficient in most cases and would likely work for us too. However, encrypting the boot partition adds an additional layer of obfuscation. It prevents an attacker who is in possession of the device from being able to start the boot process and identify metadata such as OS name and version. That metadata could be useful for identifying an attack vector to break past the encryption.

  1. Boot using a Ubuntu Desktop installer. Choose the option to try ubuntu and then open a terminal. We must first create the partitions for ubuntu and configure LUKS before installing Ubuntu itself to the drive and partitions.

  2. In the terminal session, use fdisk or gparted to create the following drive partitions:

    Mountpoint Partition Name Partition Type Partition Size Encrypted
    /dev/sda1 Boot Linux reserved 800 MB Yes
    /dev/sda2 Grub BIOS boot partition 5 MB No
    /dev/sda3 EFI EFI System 200 MB No
    /dev/sda4 Rootfs Linux filesystem The rest of the available drive space Yes
  3. Encrypt the boot and rootfs partitions. Use LUKSv1 for boot and LUKSv2 for rootfs. Provide a password to decrypt each drive.

    cryptsetup --type luks1 --cipher aes-xts-plain64 --key-size 512 --hash sha512 --use-random luksFormat /dev/sda1
    cryptsetup --type luks2 --cipher aes-xts-plain64 --key-size 512 --hash sha512 --use-random luksFormat /dev/sda4
  4. Open the newly encrypted partitions:

    cryptsetup open /dev/sda1 LUKS_BOOT
    cryptsetup open /dev/sda4 LUKS_ROOT
  5. Create the file system for the boot partition:

    mkfs.ext4 -L boot /dev/mapper/LUKS_BOOT
  6. Create the EFI file system:

    mkfs.vfat -F 16 -n EFI /dev/sda3
  7. Create the logical volumes in /dev/sda4. These commands create a swap partition thats 4GB and then uses 80% of the remaining free space to create the root partition to install the OS on. This leaves a little extra storage space in case you need to expand/create more partitions later on. See the swap chart for recommended swap partition sizes.

    pvcreate /dev/mapper/LUKS_ROOT
    vgcreate vg-ubuntu /dev/mapper/LUKS_ROOT
    lvcreate -L 4G -n swap "vg-ubuntu"
    lvcreate -l 80%FREE -n root "vg-ubuntu"

    Swap Chart

    Amount of RAM in System Recommended Swap space Recommended Swap space if allowing for hibernation
    <2gb 2 times the amount of RAM 3 times the amount of RAM
    >2gb - 8gb Equal to the amount of RAM 2 times the amount of RAM
    >8gb - 64gb At least 4gb 1.5 times the amount of RAM
    >64gb At least 4gb Hibernation not recommended
  8. Run the following command to configure GRUB to use an encrypted filesystem. The command will wait in the background while you run the installer and will update the configuration file once it detects its creation. Do not reboot or shutdown. This has to be done before the installer reaches the Install Bootloader stage at the end of the installation process.

    while [ ! -d /target/etc/default/grub.d ]; do sleep 1; done; echo "GRUB_ENABLE_CRYPTODISK=y" > /target/etc/default/grub.d/local.cfg
  9. Choose to do a minimal installation. On Installation Type, choose Something else and click continue. You should see the partitions and LVM that was setup.

  10. Locate the entry for /dev/mapper/vg--ubuntu-root and change it to XFS and the mount point to /.

  11. Select the swap entry next and change it to swap area.

  12. Select the entry for /dev/mapper/LUKS_BOOT and change it to EXT4 and mount point /boot.

  13. Select /dev/sda as the boot device as this is where all of our partitions have been configured.

  14. Click install now to start installing ubuntu to the encrypted drive.

  15. Once installation has finished, click continue testing and create a chroot environment for running the next commands and mount all of the configured drives to it.

    mount /dev/mapper/vg--ubuntu-root /target
    for n in proc sys dev etc/resolv.conf; do mount --rbind /$n /target/$n; done 
    chroot /target
    mount -a
  16. Make sure that cryptsetup-initramfs is installed.

    sudo apt update && sudo apt install -y cryptsetup-initramfs
  17. Configure initramfs to use a keyfile stored inside the encrypted boot partition to decrypt and use the root partition. This is safe because these files are themselves stored in the encrypted /boot/ which is unlocked by the GRUB boot-loader (which asks you to type the pass-phrase) which then loads the kernel and initrd.img into RAM before handing execution over to the kernel.

    echo "KEYFILE_PATTERN=/etc/luks/*.keyfile" >> /etc/cryptsetup-initramfs/conf-hook
    echo "UMASK=0077" >> /etc/initramfs-tools/initramfs.conf 
  18. Create a random key of 4096 bits (512 bytes) and add it to the LUKS volumes.

    mkdir /etc/luks
    dd if=/dev/random of=/etc/luks/boot_os.keyfile bs=512 count=1
    chmod u=rx,go-rwx /etc/luks
    chmod u=r,go-rwx /etc/luks/boot_os.keyfile
    cryptsetup luksAddKey /dev/sda1 /etc/luks/boot_os.keyfile
    cryptsetup luksAddKey /dev/sda4 /etc/luks/boot_os.keyfile 
    echo "LUKS_BOOT UUID=$(blkid -s UUID -o value /dev/sda1) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
    echo "LUKS_ROOT UUID=$(blkid -s UUID -o value /dev/sda4) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
  19. Update initramfs and reboot, you should be presented with a password to unlock the drives and continue the boot process.

    update-initramfs -u -k all

Use a FIDO U2F Yubi Key to Unlock at Boot

The following steps setup a Ubuntu system with LUKS encryption across the entire OS. Unlocking of the drive at boot is done using a FIDO U2F yubi key.

Follow the steps in the section Encrypted Root and Boot Drive, Unlock with Password at Boot. The steps in this section assume you already have a Ubuntu instance with Full Disk Encryption (FDE) thats unlocked with a password.

  1. Install ykman:

    sudo apt-add-repository ppa:yubico/stable
    sudo apt update && sudo apt install yubikey-manager yubikey-luks
  2. Check if the yubikey supports hmac-secret extension:

    ykman --diagnose 2>&1 | grep hmac-secret

    hmac-secret will be printed to the screen if supported.

  3. Backup the existing LUKS header for the boot partition:

    cryptsetup luksHeaderBackup /dev/sda1 --header-backup-file ~/luks_boot.bak
    cryptsetup luksHeaderBackup /dev/sda4 --header-backup-file ~/luks_root.bak
  4. Initialize yubikey slot for hmac. We are using slot 2 so that slot 1 can be reserved for OTP or other use cases.

    ykpersonalize -2 -ochal-resp -ochal-hmac -ohmac-lt64 -oserial-api-visible -ochal-btn-trig
  5. Enroll the yubikey with luks for each encrypted partition. This command writes to slot 7 of the LUKS configuration and allows using the yubikey to decrypt the boot partition when the OS is started. Do not use the same slot, you can only have one enrollment per slot.

    yubikey-luks-enroll -d /dev/sda1 -s 6
    yubikey-luks-enroll -d /dev/sda4 -s 7
  6. Update /etc/crypttab and add keyscript=/usr/share/yubikey-luks/ykluks-keyscript to the line item for LUKS_BOOT. It should look something like this at the end:

    LUKS_BOOT UUID=0ed8cdc2-bbd6-4579-a9e2-7ebaba348eff /etc/luks/boot_os.keyfile luks,keyscript=/usr/share/yubikey-luks/ykluks-keyscript,discard 
    LUKS_ROOT UUID=dde8d7ce-41c3-42e6-855c-3b50d5c7cbab /etc/luks/boot_os.keyfile luks,keyscript=/usr/share/yubikey-luks/ykluks-keyscript,discard
  7. Update initramfs and reboot, you should be prompted to tap the yubikey to decrypt the system.

    sudo update-initramfs -u

Automatic Decryption at Boot Using TPM and Secure Boot

If you are using Ubuntu 21.10 or later, then you need to use systemd unlock instead of clevis.

  1. Create a Ubuntu machine that has Secure Boot, UEFI/EFI, and TPM 2.0 enabled.

  2. Boot using a Ubuntu Desktop installer. Choose the option to try ubuntu and then open a terminal. We must first create the partitions for ubuntu and configure LUKS before installing Ubuntu itself to the drive and partitions.

  3. In the terminal session, use fdisk or gparted to create the following drive partitions:

    Mountpoint Partition Name Partition Type Partition Size Encrypted
    /dev/sda1 EFI EFI System 512 MB Yes
    /dev/sda2 boot Linux filesystem 1.5 GB No
    /dev/sda3 rootfs Linux filesystem The rest of the available drive space Yes
  4. Encrypt and mount the root filesystem:

    cryptsetup --type luks2 --cipher aes-xts-plain64 --key-size 512 --hash sha512 --use-random luksFormat /dev/sda3
    cryptsetup open /dev/sda3 root
  5. Create the logical volumes in /dev/sda3. These commands create a swap partition thats 4GB and then uses 80% of the remaining free space to create the root partition to install the OS on. This leaves a little extra storage space in case you need to expand/create more partitions later on. See the swap chart for recommended swap partition sizes.

    pvcreate /dev/mapper/root
    vgcreate vg-ubuntu /dev/mapper/root
    lvcreate -L 4G -n swap "vg-ubuntu"
    lvcreate -l 80%FREE -n root "vg-ubuntu"

    Swap Chart

    Amount of RAM in System Recommended Swap space Recommended Swap space if allowing for hibernation
    <2gb 2 times the amount of RAM 3 times the amount of RAM
    >2gb - 8gb Equal to the amount of RAM 2 times the amount of RAM
    >8gb - 64gb At least 4gb 1.5 times the amount of RAM
    >64gb At least 4gb Hibernation not recommended
  6. Create the EFI file system:

    mkfs.vfat -F 16 -n EFI /dev/sda1
  7. Start the ubuntu installer and choose to do a minimal installation. On Installation Type, choose Something else and click continue. You should see the partitions that was setup.

  8. Locate the entry for /dev/mapper/root and change it to XFS and the mount point to /. Mark the partition for formatting. Then locate the entry for /dev/sda2 and update it to ext4 and mount point to /boot.

  9. Complete installation and reboot. Start the machine and login to your new Ubuntu instance.

  10. Check that secure boot and tpm are enabled:

    dmesg | grep TPM; echo -e "\nSecureBoot Status:\n$(mokutil --sb-state)"
  11. Install the tpm-tools and clevis libraries:

    sudo apt update && sudo apt -y install tpm2-tools clevis clevis-tpm2 clevis-luks clevis-udisks2 clevis-systemd clevis-initramfs 
  12. Check the PCR values for your systems TPM:

    sudo udevadm trigger
    sudo tpm2_pcrread

    You should get some output listing different hash algorithms. If there are no numbers next to the hash, you can’t use it for your key. SHA256 is a good hash algorithm and should be supported in most cases.

    List of PCR Registers:

    PCR Use
    0 Core System Firmware executable code (aka Firmware)
    1 Core System Firmware data (aka UEFI settings)
    2 Extended or pluggable executable code
    3 Extended or pluggable firmware data
    4 Boot Manager
    5 GPT/Partition Table
    6 Resume from S4 and S5 Power State Events
    7 Secure Boot State
    8 Hash of the kernel command line
    9-10 Reserved for future use
    11 Bitlocker Access Control
    12 Data events and highly volatile events
    13 Boot Module Details
    14 Boot Authorities
    15-23 Reserved for Future Use
  13. Add a key to the LUKS partition from clevis thats tied to the TPM. Generally speaking, most secure boot implementations seem to use PCR registers 0, 1, and 7.

    clevis luks bind -d /dev/sda3 tpm2 '{"pcr_bank":"sha256","pcr_ids":"0,1,7"}'
  14. Update initramfs and reboot:

    update-initramfs -u -k 'all'
    reboot now