Extended Brain Storage

Linux: Full Disk Encryption with BIOS, UEFI using MBR, GPT, LUKS, LVM and GRUB

Posted on September 19, 2017

Disk layouts and encryption strategies differ within the Linux realm based on various computer dispositions. This is an overview to achieve full disk encryption (FDE) using GNU GRand Unified Bootloader (GRUB)...

Introduction

In order to perform hardware initialisation during the booting process (power-on startup) and to provide run-time services for operating systems and programs, IBM PC compatible computers utilise firmware, which is a specific class of computer software stored in a non-volatile read-only memory (ROM), erasable programmable ROM (EPROM) or flash memory. The most widespread are:

The fact that the firmware can be replaced enabled not only easy fixes of bugs, but also a possibility of BIOS rootkit infections and other security-related issues, which are somewhat addressed by the UEFI and its secure boot protocol. Besides, UEFI provides support for:

Note that this overview considers only "standard" Linux kernel provided by the particular Linux distribution. Not all options, such as hibernation/resume, will work with hardened kernels.


Disk Partition Schemes

In order to install an operating system on a physical storage device, it needs to be formatted. There are several ways to partition a disk drive, the most common of which (in Linux-based environment) are the following two:

The MBR enables for up to four primary partitions (the partition table has four "slots"). If more partitions are needed, one of them needs to be replaced by an extended partition that can contain several logical partitions in it. The total number of partitions depends on the operating system and formatting tool. On the other hand, GPT can contain up to 128 ("primary") partitions and of size up to 8 ZiB = (2^64)*512 bytes.

In order to start an operating system, a boot loader needs to be executed. The location of a boot loader and its other requirements on partitions differ as per each software. A reliable workhorse of all boot loaders is the GNU GRand Unified Bootloader (GRUB).


GRand Unified Bootloader (GRUB)

In the realm of BIOS, GRUB itself is not a single binary, rather it is a group of executable code that can be loaded in the following stages:

The location of the boot.img and core.img files depends on whether the disk drive is formatted using MBR or GPT. The difference is nicely depicted either in Wikipedia or Barney Desmond's blog.

If GRUB is used as the boot loader, BIOS systems that are partitioned with GPT require a BIOS boot partition (BBP), which is:

In the realm of UEFI and 64-bit computer architecture (x86_64), GRUB is a single binary named grubx64.efi located in the \EFI\grub\ directory (FAT notation) in the EFI System Partition (ESP), which is:

Some UEFI-based systems are equipped with Compatibility Support Module (CSM) which enables booting in legacy BIOS mode from MBR-partitioned disks relying on the content of a boot sector. If GRUB is used as the boot loader, UEFI reads the Stage 1 (boot.img) instead of reading the content of the ESP.


Full Disk Encryption (FDE)

Unless designed as deniable, any encryption method regardless of key strength will always be subject to the Rubberhose Attack. The following examples do not provide plausible deniability, but they can be modified accordingly. Reader discretion advised!

There exist several ways to achieve full disk encryption (FDE). Considering that a computer system has a single hard drive, the following example considers the LVM on LUKS scenario only, as it utilises:

In other words, the scenario creates logical volumes within a LUKS-encrypted partition, which is:

So the root and swap partitions will be created as logical volumes within a volume group that is encrypted within a single LUKS physical volume available as follows:

The disk partition details and format can be depicted as follows:

LUKS and LVM Device Availability

LUKS and LVM Device Availability


Practical Approaches

To sum the aforementioned information up, users or administrators can work with:

These four options generate four possible practical approaches:


GRUB on BIOS with MBR

The disk partitioning schema and format of partitions can be depicted as follows:

GRUB on BIOS with MBR

GRUB on BIOS with MBR

In order to achieve full disk encryption, the necessary commands are as follows:

$ parted -s /dev/sda mklabel msdos
$ parted -s -a optimal /dev/sda mkpart primary "0%" "100%"
$ parted -s /dev/sda set 1 boot on
$ parted -s /dev/sda set 1 lvm on
$ parted -s /dev/sda print
$ cryptsetup luksFormat -v /dev/sda1
$ cryptsetup luksOpen /dev/sda1 lvm-system
$ pvcreate /dev/mapper/lvm-system
$ vgcreate lvmSystem /dev/mapper/lvm-system
$ lvcreate -L 1G lvmSystem -n volSwap
$ lvcreate -l +100%FREE lvmSystem -n volRoot
$ mkswap /dev/lvmSystem/volSwap
$ mkfs.ext4 -L volRoot /dev/lvmSystem/volRoot
$ swapon /dev/lvmSystem/volSwap
$ mount /dev/lvmSystem/volRoot /mnt
...
$ sed -i "s/FILES=(/FILES=(\/crypto_keyfile.bin/g" /etc/mkinitcpio.conf
$ sed -i "s/modconf block/modconf block encrypt lvm2 resume/g" /etc/mkinitcpio.conf
$ dd if=/dev/random of=/crypto_keyfile.bin bs=512 count=8 iflag=fullblock
$ cryptsetup luksAddKey /dev/sda1 /crypto_keyfile.bin
$ chmod 000 /crypto_keyfile.bin
$ mkinitcpio -p linux
...
$ sed -i "s/quiet/quiet resume=UUID=`blkid -s UUID -o value /dev/lvmSystem/volSwap`/g" /etc/default/grub
$ sed -i "s/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID=`blkid -s UUID -o value /dev/sda1`:lvm-system\"/g" /etc/default/grub
$ sed -i "s/#GRUB_ENABLE_CRYPTODISK/GRUB_ENABLE_CRYPTODISK/g" /etc/default/grub
$ grub-install --target=i386-pc --recheck /dev/sda
$ grub-mkconfig -o /boot/grub/grub.cfg

GRUB on BIOS with GPT

The disk partitioning schema and format of partitions can be depicted as follows:

GRUB on BIOS with GPT

GRUB on BIOS with GPT

In order to achieve full disk encryption, the necessary commands are as follows:

$ parted -s /dev/sda mklabel gpt
$ parted -s -a optimal /dev/sda mkpart primary "0%" "2MiB"
$ parted -s /dev/sda set 1 bios_grub on
$ parted -s -a optimal /dev/sda mkpart "primary" "2MiB" "100%"
$ parted -s /dev/sda set 2 lvm on
$ parted -s /dev/sda print
$ cryptsetup luksFormat -v /dev/sda2
$ cryptsetup luksOpen /dev/sda2 lvm-system
$ pvcreate /dev/mapper/lvm-system
$ vgcreate lvmSystem /dev/mapper/lvm-system
$ lvcreate -L 1G lvmSystem -n volSwap
$ lvcreate -l +100%FREE lvmSystem -n volRoot
$ mkswap /dev/lvmSystem/volSwap
$ mkfs.ext4 -L volRoot /dev/lvmSystem/volRoot
$ swapon /dev/lvmSystem/volSwap
$ mount /dev/lvmSystem/volRoot /mnt
...
$ sed -i "s/FILES=(/FILES=(\/crypto_keyfile.bin/g" /etc/mkinitcpio.conf
$ sed -i "s/modconf block/modconf block encrypt lvm2 resume/g" /etc/mkinitcpio.conf
$ dd if=/dev/random of=/crypto_keyfile.bin bs=512 count=8 iflag=fullblock
$ cryptsetup luksAddKey /dev/sda2 /crypto_keyfile.bin
$ chmod 000 /crypto_keyfile.bin
$ mkinitcpio -p linux
...
$ sed -i "s/quiet/quiet resume=UUID=`blkid -s UUID -o value /dev/lvmSystem/volSwap`/g" /etc/default/grub
$ sed -i "s/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID=`blkid -s UUID -o value /dev/sda2`:lvm-system\"/g" /etc/default/grub
$ sed -i "s/#GRUB_ENABLE_CRYPTODISK/GRUB_ENABLE_CRYPTODISK/g" /etc/default/grub
$ grub-install --target=i386-pc --recheck /dev/sda
$ grub-mkconfig -o /boot/grub/grub.cfg

GRUB on UEFI with MBR

Theoretically, it could work with either no ESP partition at all, loading the GRUB from the first sector of the MBR just like in "BIOS with MBR" case; or with ESP partition grub containing something like: /boot/grub/grub386.efi. Either way, this is a very non-standard and non-recommended way to partition the disks.

A more systematic and standard way would be to use the ESP as follows:

GRUB on UEFI with MBR

GRUB on UEFI with MBR

In order to achieve full disk encryption, the necessary commands are as follows:

$ parted -s /dev/sda print
$ parted -s /dev/sda mklabel msdos
$ parted -s -a optimal /dev/sda mkpart primary "0%" "512MiB"
$ parted -s /dev/sda set 1 esp on
$ parted -s -a optimal /dev/sda mkpart "primary" "512MiB" "100%"
$ parted -s /dev/sda set 2 lvm on
$ parted -s /dev/sda print
$ cryptsetup luksFormat -v /dev/sda2
$ cryptsetup luksOpen /dev/sda2 lvm-system
$ pvcreate /dev/mapper/lvm-system
$ vgcreate lvmSystem /dev/mapper/lvm-system
$ lvcreate -L 1G lvmSystem -n volSwap
$ lvcreate -l +100%FREE lvmSystem -n volRoot
$ mkswap /dev/lvmSystem/volSwap
$ mkfs.fat -F 32 -n ESP /dev/sda1
$ mkfs.ext4 -L volRoot /dev/lvmSystem/volRoot
$ swapon /dev/lvmSystem/volSwap
$ mount /dev/lvmSystem/volRoot /mnt
$ mkdir -p /mnt/boot/efi
$ mount /dev/sda1 /mnt/boot/efi
...
$ sed -i "s/FILES=(/FILES=(\/crypto_keyfile.bin/g" /etc/mkinitcpio.conf
$ sed -i "s/modconf block/modconf block encrypt lvm2 resume/g" /etc/mkinitcpio.conf
$ dd if=/dev/random of=/crypto_keyfile.bin bs=512 count=8 iflag=fullblock
$ cryptsetup luksAddKey /dev/sda2 /crypto_keyfile.bin
$ chmod 000 /crypto_keyfile.bin
$ mkinitcpio -p linux
...
$ sed -i "s/quiet/quiet resume=UUID=`blkid -s UUID -o value /dev/lvmSystem/volSwap`/g" /etc/default/grub
$ sed -i "s/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID=`blkid -s UUID -o value /dev/sda2`:lvm-system\"/g" /etc/default/grub
$ sed -i "s/#GRUB_ENABLE_CRYPTODISK/GRUB_ENABLE_CRYPTODISK/g" /etc/default/grub
$ grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=artix --recheck /dev/sda
$ grub-mkconfig -o /boot/grub/grub.cfg

GRUB on UEFI with GPT

The disk partitioning schema and format of partitions can be depicted as follows:

GRUB on UEFI with GPT

GRUB on UEFI with GPT

In order to achieve full disk encryption, the necessary commands are as follows:

$ parted -s /dev/sda print
$ parted -s /dev/sda mklabel gpt
$ parted -s -a optimal /dev/sda mkpart primary "0%" "512MiB"
$ parted -s /dev/sda set 1 esp on
$ parted -s -a optimal /dev/sda mkpart "primary" "512MiB" "100%"
$ parted -s /dev/sda set 2 lvm on
$ parted -s /dev/sda print
$ cryptsetup luksFormat -v /dev/sda2
$ cryptsetup luksOpen /dev/sda2 lvm-system
$ pvcreate /dev/mapper/lvm-system
$ vgcreate lvmSystem /dev/mapper/lvm-system
$ lvcreate -L 1G lvmSystem -n volSwap
$ lvcreate -l +100%FREE lvmSystem -n volRoot
$ mkswap /dev/lvmSystem/volSwap
$ mkfs.fat -F 32 -n ESP /dev/sda1
$ mkfs.ext4 -L volRoot /dev/lvmSystem/volRoot
$ swapon /dev/lvmSystem/volSwap
$ mount /dev/lvmSystem/volRoot /mnt
$ mkdir -p /mnt/boot/efi
$ mount /dev/sda1 /mnt/boot/efi
...
$ sed -i "s/FILES=(/FILES=(\/crypto_keyfile.bin/g" /etc/mkinitcpio.conf
$ sed -i "s/modconf block/modconf block encrypt lvm2 resume/g" /etc/mkinitcpio.conf
$ dd if=/dev/random of=/crypto_keyfile.bin bs=512 count=8 iflag=fullblock
$ cryptsetup luksAddKey /dev/sda2 /crypto_keyfile.bin
$ chmod 000 /crypto_keyfile.bin
$ mkinitcpio -p linux
...
$ sed -i "s/quiet/quiet resume=UUID=`blkid -s UUID -o value /dev/lvmSystem/volSwap`/g" /etc/default/grub
$ sed -i "s/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"cryptdevice=UUID=`blkid -s UUID -o value /dev/sda2`:lvm-system\"/g" /etc/default/grub
$ sed -i "s/#GRUB_ENABLE_CRYPTODISK/GRUB_ENABLE_CRYPTODISK/g" /etc/default/grub
$ grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=artix --recheck /dev/sda
$ grub-mkconfig -o /boot/grub/grub.cfg

UEFI Boot Records Management

Some UEFI firmware (e.g. VirtualBox) likes to search for EFI in \EFI\BOOT\bootx64.efi. This can be easily resolved by the following copy command:

$ mkdir -p /boot/efi/EFI/BOOT
$ cp /boot/efi/EFI/artix/grubx64.efi /boot/efi/EFI/BOOT/bootx64.efi

Different UEFI may require different approaches. If necessary, the UEFI boot records can be manipulated using the efibootmgr utility. For instance, in order to:

$ efibootmgr
$ efibootmgr --bootnum 002a --delete-bootnum
$ efibootmgr --create --disk /dev/sdDISK-LETTER --part PARTITION-NUMBER --loader "\EFI\PATH\grubx64.efi" --label "BOOT RECORD LABEL"

Automatic Mounting of Other Partitions

Having a desktop computer with more disks or simply separating partitions for /home, /tmp, /var directories from the root partition seem to be a reasonable solution for many administrators. A random byte file can be generated in order to serve as key to the encrypted partition(s), example of which is as follows (assuming there will be a single partition on disk sdY and the key used will be called lvm-data.bin and located in a newly created /etc/luks-keys directory, which nobody needs to have access to):

$ mkdir -m 700 /etc/luks-keys
$ dd if=/dev/random of=/etc/luks-keys/lvm-data.bin bs=512 count=8 iflag=fullblock
$ cryptsetup luksAddKey /dev/sdY1 /etc/luks-keys/lvm-data.bin
$ chmod 000 /etc/luks-keys/*

For more details regarding usage of /dev/random and /dev/urandom for keys generation, this ArchWiki page can be visited.

An automatic decryption and mounting of an encrypted partition can be performed in regards to the init (short for initialisation) system installed:

$ echo "lvm-data UUID=\"`blkid -s UUID -o value /dev/sdY1`\" /etc/luks-keys/lvm-data.bin" >> /etc/crypttab
$ echo "target=lvm-data" >> /etc/conf.d/dmcrypt
$ echo "source=UUID=\"`blkid -s UUID -o value /dev/sdY1`\"" >> /etc/conf.d/dmcrypt
$ echo "key=/etc/luks-keys/lvm-data.bin" >> /etc/conf.d/dmcrypt

Finally, the respective options need to be put into the /etc/fstab file in order to get the disk partitions mounted accordingly:

$ echo "UUID=`blkid -s UUID -o value /dev/lvmData/volHome` /home ext4 rw,relatime,data=ordered 0 0" >> /etc/fstab

Tags: #security #encryption #FDE #BIOS #UEFI #MBR #GPT #LUKS #LVM #GRUB

⏴ Previous Post Next Post ⏵