Skip to content

Instantly share code, notes, and snippets.

@rmackinnon
Created November 19, 2022 02:30
Show Gist options
  • Save rmackinnon/fbf47acba646d32ed026034860adcf2d to your computer and use it in GitHub Desktop.
Save rmackinnon/fbf47acba646d32ed026034860adcf2d to your computer and use it in GitHub Desktop.

Creating a Fully Encrypted Filesystem

with GRUB, LUKS, and LVM under the Gentoo Linux Distribution

Intended Audience

This document is intended to help others achieve a fully encrypted platform, with lowered attack surface for at-rest data. The following text is designed and tailored for the Framework laptop, but should be applicable to most platforms. We will not be covering nested decryption nor the security implications of using TRIM/Discard, there are numerous documents that cover this thuroughly. You should be comfortable with filesystem and storage management.

Requirements

  • General understanding of the Linux commandline interface.
  • General understanding of LUKS and LVM partitioning.
  • General operation of the GRUB bootloader.
  • A sense of selfworth and determination that is not easily crushed by hours of trial and error.

Required Packages

  • sys-fs/dosfstools
  • sys-fs/cryptsetup

Overview

In this document we will be configuring an NVMe drive with partions for UEFI, an encrypted boot filesystem, an encrypted LVM volume group, and swap.

Disk Preperation

  1. Create your partition scheme and define your partition usage:

    partition
    name
    fs size start end
    "EFI system partition" fat32 511MB 34s 512MB
    bootfs ext4[1] 512MB 512MB 1GB
    swapfs linux-swap(v1) 2GB 1GB 3GB
    rootfs ext4[1] * 3GB -1s[2]

    [1] The assignement of this filesystem type doesn't actually matter, as it is undefined when creating a LUKS partition.

    [2] This will assign the remaining storage to this partition.

  2. Using parted, create the partition table and partitions, here we are using the first NVMe disk:

     parted /dev/nmve0n1 mklabel gpt
     parted /dev/nmve0n1 mkpart "EFI system partition" fat32 34s 512MB
     parted /dev/nmve0n1 mkpart bootfs ext4 512MB 1GB
     parted /dev/nmve0n1 mkpart swapfs "linux-swap(v1)" 1GB 3GB
     parted /dev/nmve0n1 mkpart rootfs ext4 3GB -1s
    
  3. Toggle the required partition flags, EFI needs esp, and the encrypted boot filesystem needs bls_boot:

     parted /dev/nmve0n1 toggle 1 esp
     parted /dev/nmve0n1 toggle 2 bls_boot
    
  4. Create your encrypted boot and root paritions:

    NOTE: The usage of the pbkdf option is required for the boot partition. As of GRUB v2.06, support of the PBDKF algorithm Argon2id is unsupported without significant code patching. The upstream is said to have the patch applied.

     cryptsetup --label="crypt_boot" \
      -c aes-xts-plain64 -h sha512 \
      --pbkdf=pbkdf2 --s 256 \
      --use-random luksFormat /dev/disk/by-partlabel/bootfs
     cryptsetup luksDump /dev/disk/by-partlabel/bootfs | grep "^UUID"
    
     cryptsetup --label="crypt_root" \
      -c aes-xts-plain64 -h sha512 \
      --s 256 \
      --use-random luksFormat /dev/disk/by-partlabel/rootfs
     cryptsetup luksDump /dev/disk/by-partlabel/rootfs| grep "^UUID"
    

    Record the UUIDs for rootfs and bootfs, as we will need them later.

Unlock the New LUKS Paritions

As we are operating on an NVMe SSD, we can optionally use some options that are more effective and efficient. By enabling no-read-workqueue and no-write-workqueue flags, we can help negate some of the cpu work load from encryption.

SECUIRTY NOTE: The usage of allow-discards flag has security implecations. Understand them and use them at your own risk.

      cryptsetup open --persistent \
      --allow-discards \
      --perf-no_read_workqueue --perf-no_write_workqueue \
      /dev/disk/by-partlabel/bootfs luks_boot

     cryptsetup open --persistent \
      --allow-discards \
      --perf-no_read_workqueue --perf-no_write_workqueue \
      /dev/disk/by-partlabel/rootfs luks_root

Logical Volume Creation

This is outside of the scope of this document. Apply vgcreate to /dev/mapper/luks_root, and create your required logical volumes for your storage needs.

Partition Formating

  1. Format the UEFI partition.

    NOTE that we are using the mkfs.fat utility NOT mkfs.vfat. Many UEFI loaders are very particular about the exact type of FAT32 partition.

    mkfs.fat -F 32 "/dev/disk/by-partlabel/EFI\x20system\x20partition"

  2. Format the luks_boot partition.

    mkfs.ext4 -I 256 /dev/mapper/luks_boot

    We use the -I 256 option as the 128 value is deprercating soon.

  3. Continue formating the rest of the system logical volumes.

Mount The Partitions

  1. Nothing unusual here, mount up the root filesystem partitions, and new luks_boot partition to /boot.
  2. Create the path /boot/efi for the UEFI partition, and mount it.

System Configuration

Continue with your system configuration, kernel building. You do you.

Install GRUB Loader

This is covered in other tutorials, but here is it anyways:

      grub-install --target=x86_64-efi \
           --boot-directory=/boot \
           --efi-directory=/boot/efi /dev/nvme0n1

Filesystem Structure

Create the UEFI file tree structure.

     mkdir - /boot/efi/EFI/{gentoo,grub}

Create a Built-in GRUB Configuration Stub

In the next step we will build a custom boot image, but before we do that we need to setup the built-in configuration stub file. GRUB EFI images build in a config as the first stage, and follow it with the config that lives in the EFI partition. This can lead to some confusion on why, for example, you would get prompted twice for a passphrase even if you have entered it correctly the first time. We will use this built-in as a preperation to load the bare minimum to function. In this case, we setup the graphics terminal prior to handing off to the grub config file on the UEFI partition.

/boot/efi/EFI/grub/grub.cfg.built-in

      insmod part_gpt
      insmod ext2
      insmod fat
      insmod configfile
      
      insmod video
      insmod efi_gop
      insmod efi_uga
      insmod video_fb
 
      loadfont unicode
      set gfxmode=1024x768
      insmod gfxterm
      set locale_dir=$prefix/locale
      set lang=en_US
      insmod gettext
      terminal_output gfxterm

At a minimum insert: part_gpt, ext2, fat, configfile

Create the UEFI GRUB Configuration

This is the file that grubx64.efi will access AFTER running the required built-in config. At the creation of this Gist, if this content is located in the built-in file GRUB will ask for the passphrase twice.

/boot/efi/EFI/grub/grub.cfg

      insmod crypto
      insmod cryptodisk
      insmod luks
      insmod luks2
      insmod gcry_sha256
      insmod gcry_sha512
      insmod gcry_crc
      insmod pbkdf2
      insmod normal
      
      cryptomount (hd0,gpt2)
      configfile (crypto0)/grub/grub.cfg
      normal

Create a Custom UEFI Boot Image

Next we need to create a custom UEFI boot image to handle our lack of valid boot partition, and get us to a valid GRUB partition. The default image does not contain enough built-in modules (nor does it include when grub-install is run) to detect or read the UEFI partition, and is designed to shim to an unencrypted boot partition. Therefore we need to front load the all the modules to do the following: read the UEFI partition, GRUB configuration file, and decrypt the boot partition. Notice we have defined the built-in config for in the variable EFI_GRUB_CFG.

update-efi-image

      BITS=64
      ARCH="x86_64-efi"
      RELEASE="gentoo"
      
      EFI_GRUB_ROOT="/EFI/grub"
      EFI_BOOT_IMG="/EFI/${RELEASE}/grubx${BITS}.efi"
      
      EFI_PATH="/boot/efi"
      EFI_GRUB_CFG="${EFI_PATH}${EFI_GRUB_ROOT}/grub.cfg.built-in"
      EFI_IMAGE="${EFI_PATH}${EFI_BOOT_IMG}"
      
      VID_MODS=( video efi_gop efi_uga video_fb gfxterm gettext )
      CRYPT_MODS=( crypto cryptodisk luks2 gcry_sha256 gcry_sha512 gcry_crc pbkdf2 )
      FS_MODS=( part_gpt fat ext2 )
      
      MODULES=( normal configfile ${VID_MODS[@]} ${CRYPT_MODS[@]} ${FS_MODS[@]} )
      
      MKIMG_BIN=`which grub-mkimage`
      MKIMG_OPTS=( -O ${ARCH} -c ${EFI_GRUB_CFG} -p ${EFI_GRUB_ROOT} -o ${EFI_IMAGE} ${MODULES[@]} )
      
      $MKIMG_BIN ${MKIMG_OPTS[@]}

Additional Preparation of the UEFI GRUB directory (Optional)

If you elect for an early graphical terminal, you will need a few extra files in your UEFI partition. Copy the following to /boot/efi/EFI/grub:

      /boot/grub/fonts
      /boot/grub/locale

Optional: For development and testing as they will only be available by editing /EFI/grub/grub.cfg. While booting in this mode, there is no access to GRUB rescue.

      /boot/grub/x86_64-efi

Update /etc/crypttab and /etc/crypttab.initramfs (optional)

In the boot order, once we have loaded the initramfs and exited GRUB the encryption on the boot partition is release. Therefore we may need to add it back in to unlock so we can do system updates. There are options available for fancy automatic decryption with nested keys, but we're just going to record the the entry for now, and you can spend time on that later.

SECUIRTY NOTE: The usage of allow-discards flag has security implecations. Understand them and use them at your own risk.

/etc/crypttab

     luks_bootfs UUID={luks_boot_UUID} none luks,allow-discards,no-read-workqueue,no-write-workqueue

Duplicate this in /etc/crypttab.initramfs for completionary sake as some initramfs generators require this information, and add the following line:

/etc/crypttab.initrams

     luks_bootfs UUID={luks_boot_UUID} none luks,allow-discards,no-read-workqueue,no-write-workqueue
     luks_rootfs UUID={luks_root_UUID} none luks,allow-discards,no-read-workqueue,no-write-workqueue

Update GRUB Defaults

As we start to draw to a close, we need to make sure GRUB knows what to do with an encrypted root partition, and logical volumes when generating the grub.cfg file. Modify the GRUB_CMDLINE_LINUX_DEFAULT line with the following, at a minimum you will need the doluks and dolvm, rd.luks.uuid and crypt_root entries for a valid system.

     GRUB_CMDLINE_LINUX_DEFAULT="real_root=/dev/{VGname}/root 
          doluks dolvm rd.luks.uuid=luks-{luks_rootfs_UUID}
          crypt_root=UUID={luks_rootfs_UUID}
          rd.lvm.lv={VGname}/root {any additional LVs to activate}

Generate a GRUB Config

At this point you can generate your GRUB configuration file. Everything is in place now to allow you to boot a fully encrypted system, and allow it to mount everything.

      grub-mkconfig -o /boot/grub/grub.cfg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment