Secured Arch Linux Installation

Arch Linux installation using BTRFS on Full Disk Encryption and Secure Boot

View on GitHub
Secured Arch Linux Installation | Arch Linux installation using BTRFS on Full Disk Encryption and Secure Boot

Introduction

This is a documentation for an arch-linux installation from a security standpoint using BTRFS filesystem instead of LVM on Full Disk Encryption including /boot using LUKS.

In order to give some context and understand why we are doing this, we must first recognize the risks that we are trying to minimize & the asset that we are trying to protect:

Device theft leading to sensitive data exposure

Physical access to device for a period of time

Please keep in mind that even though we take as much security measures as we can to protect ourselves against threats, in the case of physical access to a device for a good duration of time, nothing will 100% guarantee your security but we will definetly make it extremly hard for anyone to access it :)

Plan

partition mount Encrypted
/dev/sda1 /boot/efi NO
/dev/sda2 /swap YES
/dev/sda3 / YES
  1. Only keep /boot/efi our ESP parition unencrypted.
  2. will need to decrypt at the GRUB level.
  3. will need to use LUKS 1 to encrypt our system partition(since 2019 GRUB does not support LUKS2).
  4. our bootloader is still an attack vector at this point, then we can address this problem by using UEFI secure boot, enroll our own Secure Boot keys and sign the kernel and GRUB with our keys.

Note:

The passphrase cannot be passed on from Grub to initramfs so we will need to enter our passphrase twice: one time for grub to unlock the encrypted root partition and another time for initramfs, the reason for that is that we currently do not have a secure way to pass our passphrase from GRUB down to initramfs unless we can embedd our secure key file in our initramfs, then we only need to enter our passphrase once.

what about SWAP?

Why BTRFS?

Few notes worth mentioning:


Installation

loadkeys us

ls /sys/firmware/efi/efivars

timedatectl set-ntp true

ping archlinux.org

cryptsetup open --type plain -d /dev/urandom /dev/<block-device> to_be_wiped

 lsblk

NAME          MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
sda             8:0    0  1.8T  0 disk
└─to_be_wiped 252:0    0  1.8T  0 crypt
dd if=/dev/zero of=/dev/mapper/to_be_wiped status=progress

dd: writing to ‘/dev/mapper/to_be_wiped’: No space left on device

cryptsetup close to_be_wiped

cfdisk /dev/sda

size type
550MB for ESP partition EFI system
8GB for swap partition Linux swap
the rest for the root partition Linux Filesystem
 mkfs.fat -F32 -n EFI /dev/sda1
 cryptsetup luksFormat --type luks1 --use-random -S 1 -s 512 -h sha512 -i 5000 /dev/sda3 
 cryptsetup luksOpen /dev/sda3 cryptroot
 cryptsetup open --type plain --key-file /dev/urandom /dev/sda2 swap 
 mkswap -L swap /dev/mapper/swap 
 swapon -L swap
 mkfs.btrfs --force --label cryptroot /dev/mapper/cryptroot

Warning

Notice how LUKS prompts you to enter a passphrase and not a password, this is a good article that explains the difference, generally speaking humans are terrible at creating strong passwords with good entropy and remembering it and that’s why we use password managers, but another option would be using passphrases 4-5 words chosen at random, example: “correct horse battery staple”

 mount -t btrfs -o compress=lzo /dev/mapper/cryptroot /mnt   
 cd /mnt
 btrfs subv create @  
 btrfs subv create @home
 btrfs subv create @snapshots
 cd /
 umount /mnt
 o=defaults,x-mount.mkdir 
 o_btrfs=$o,compress=lzo,ssd,noatime    
 mount -o compress=lzo,subvol=@,$o_btrfs /dev/mapper/cryptroot /mnt
 mount -o compress=lzo,subvol=@home,$o_btrfs /dev/mapper/cryptroot /mnt/home
 mount -o compress=lzo,subvol=@snapshots,$o_btrfs /dev/mapper/cryptroot /mnt/.snapshots 
 mkdir -p /mnt/boot/efi
 mount /dev/sda1 /mnt/boot/efi
 pacstrap /mnt base base-devel btrfs-progs linux linux-firmware mkinitcpio nano vim dhcpcd wpa_supplicant
  genfstab -L -p /mnt >> /mnt/etc/fstab 
  
  change this in /mnt/etc/fstab
  LABEL=swap           none       swap       defaults   0 0
  
  to this:
  /dev/mapper/swap     none       swap       sw   0 0
  
  edit crypttab:
  
swap        /dev/sda2        /dev/urandom        swap,offset=2048,cipher=aes-xts-plain64,size=256

arch-chroot /mnt /bin/bash

 ln -s /usr/share/zoneinfo/Europe/Paris /etc/localtime

hwclock --systohc --utc

 echo arch > /etc/hostname
 
 edit /etc/hosts file and match that accordingly 

locale-gen

LANG=en_US.UTF-8

KEYMAP=us

 pacman -S grub

 Edit /etc/default/grub, add GRUB_ENABLE_CRYPTODISK=y and GRUB_DISABLE_SUBMENU=y

 GRUB_CMDLINE_LINUX="cryptdevice=/dev/sda3:cryptroot:allow-discards root=/dev/mapper/cryptroot"
  
 pacman -S efibootmgr

 grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB --modules="tpm" --disable-shim-lock

 pacman -S intel-ucode

 grub-mkconfig -o /boot/grub/grub.cfg
* mkdir /root/secrets && chmod 700 /root/secrets
* head -c 64 /dev/urandom > /root/secrets/crypto_keyfile.bin && chmod 600 /root/secrets/crypto_keyfile.bin
* cryptsetup -v luksAddKey -i 1 /dev/sda3 /root/secrets/crypto_keyfile.bin

Warning

we are embedding our key file to the initramfs only because we are expected to enter our passphrase at the grub level (before initramfs and the kernel are loaded) already and a 2nd time at the initramfs level so it makes sense to embed a keyfile into our initramfs in order to avoid this redundancy. Please do NOT embed a key file on your disk and use it to decrypt it without requiring a passphrase at an earlier level because it kind of defeats the purpose of encrypting your disk, if all that is required to decrypt it is.. booting your computer.(Unless you are storing your keyfile on a usb drive for example)

/etc/mkinitcpio.conf

add encrypt just before filesystems

HOOKS=(base udev autodetect keyboard modconf block encrypt filesystems fsck)


FILES=(/root/secrets/crypto_keyfile.bin)

mkinitcpio -p linux


Image below shows our crypto_keyfile embedded inside initramfs:

crypto_keyfile

Edit /etc/default/grub again

GRUB_CMDLINE_LINUX="... cryptkey=rootfs:/root/secrets/crypto_keyfile.bin"

grub-mkconfig -o /boot/grub/grub.cfg


The cryptkey parameter we are passing in our grub, will be picked up by the encrypt hook that we included in our initramfs:

encrypt_hook

Side Note:

But wait, how can we decrypt the same partition using a passphrase and then later using a keyfile with some /dev/urandom in it?! The answer to that is: well that’s how LUKS work:

  • it has 8 key slots stored in the partition header for multiple passphrases(for multiple users).
  • LUKS uses your passphrase to decrypt the master key which then itself is used to decrypt the bulk data, this way when you want to change your passphrase, you are only changing the encrypted masterkey and not re-encrypting the whole block device.

I recommend you to make a backup of your LUKS header

cryptsetup luksHeaderBackup --header-backup-file=testing.header testing.encrypted.iso

But enough about LUKS, let us continue with our installation

useradd -m -G wheel -s /bin/bash <user>`
passwd <user>


Post-Installation

At this point we have finished our installation, I will not go into post installation procedures because it is already covered in the Arch Linux wiki but we can confidently say that we have a fully encrypted disk by now and we have protected ourselves from data leaks in case of device theft.

Nonetheless we are still prone to other types of attacks that are a bit more advanced we call those Evil Maid attacks, in a nutshell these are attacks that involve physical access to your encrypted device for a period of time, tampering with your initramfs file or bootloader(GRUB) in order to make reveal your password in cleartext on a file stored on the unencrypted partition of your disk(/boot or /boot/efi or /efi) or sent over the internet to the attacker, who later on can come back and access your device using the passphrase that he retreived.

If you are using UEFI then one way to protect ourselves against these attacks is to use Secure boot, then we can only allow signed and verified binaries(bootloaders, kernel,drivers) to load on our device so any tampering with our boot process would be detected. Of course this means that you need to protect access to your UEFI settings with a password(Otherwise attackers can just turn off secure boot altogether) and lower the boot order priority for USB. I recommend you take a few minutes to read this and also this to understand what is going on below.

-> Install efitools

pacman -S efitools

-> Generate random GUID

uuidgen --random > guid.txt

Please note the below:

*.key : PEM format private keys for EFI binary and EFI signature list signing. *.crt : PEM format public keys for sbsign. *.cer: DER format public keys for firmware. *.esl: Certificates in EFI Signature List for firmware. *.auth: Certificates in EFI Signature List with authentication header for firmware.

Before going with this manually, Rod Smith has made this script that creates the keys that we need, it requires python3 though but we can fix this by replacing:

GUID=`python3 -c 'import uuid; print(str(uuid.uuid1()))'`

with:

GUID=$(uuidgen --random)

However if you want to proceed with creating the keys manually:

-> Generate Platform key

openssl req -newkey rsa:4096 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN=Platform Key/" -out PK.crt
openssl x509 -outform DER -in PK.crt -out PK.cer
cert-to-efi-sig-list -g "$(< guid.txt)" PK.crt PK.esl
sign-efi-sig-list -g "$(< guid.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth

-> Generate Key Exchange Key

openssl req -newkey rsa:4096 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=Key Exchange Key/" -out KEK.crt
openssl x509 -outform DER -in KEK.crt -out KEK.cer
cert-to-efi-sig-list -g "$(< guid.txt)" KEK.crt KEK.esl
sign-efi-sig-list -g "$(< guid.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth

-> Generate our Signature Database key

openssl req -newkey rsa:4096 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=Signature Database key/" -out db.crt
openssl x509 -outform DER -in db.crt -out db.cer
cert-to-efi-sig-list -g "$(< guid.txt)" db.crt db.esl
sign-efi-sig-list -g "$(< guid.txt)" -k KEK.key -c KEK.crt db db.esl db.auth

-> make sure to restrict permissions on our private keys:

chmod 0600 *.key

-> Install sbsigntools to sign our binaries

pacman -S sbsigntools

-> Signing our kernel and GRUB binary

sbsign --key db.key --cert db.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux
sbsign --key db.key --cert db.crt --output /boot/efi/EFI/GRUB/grubx64.efi /boot/efi/EFI/GRUB/grubx64.efi

-> Automatically sign bootloader and kernel on install and updates

It is necessary to sign GRUB with your UEFI Secure Boot keys every time the system is updated via pacman. This can be accomplished with a pacman hook.

-> Create the hooks directory

mkdir -p /etc/pacman.d/hooks

-> Create hooks for both the linux and grub packages

/etc/pacman.d/hooks/99-secureboot-linux.hook

[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = linux

[Action]
Description = Signing Kernel for SecureBoot
When = PostTransaction
Exec = /usr/bin/find /boot/ -maxdepth 1 -name 'vmlinuz-*' -exec /usr/bin/sh -c 'if ! /usr/bin/sbverify --list {} 2>/dev/null | /usr/bin/grep -q "signature certificates"; then /usr/bin/sbsign --key /root/db.key --cert /root/db.crt --output {} {}; fi' \ ;
Depends = sbsigntools
Depends = findutils
Depends = grep

/etc/pacman.d/hooks/98-secureboot-grub.hook

[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = grub

[Action]
Description = Signing GRUB for SecureBoot
When = PostTransaction
Exec = /usr/bin/find /boot/efi/ -name 'grubx64*' -exec /usr/bin/sh -c 'if ! /usr/bin/sbverify --list {} 2>/dev/null | /usr/bin/grep -q "signature certificates"; then /usr/bin/sbsign --key /root/db.key --cert /root/db.crt --output {} {}; fi' \ ;
Depends = sbsigntools
Depends = findutils
Depends = grep

-> Copy *.cer, *.esl, *.auth to the ESP partition

cp /root/*.cer /root/*.esl /root/*.auth /boot/efi/

-> Boot into UEFI firmware setup

systemctl reboot --firmware

-> clear all preloaded keys to get into “Setup” mode -> add our newly created keys starting with db then KEK then finally PK -> after booting and making sure that everything works fine, please make sure to shred the keys that we copied into our ESP partition(remember this is an unencrypted parition)

shred -uzv *.cer
shred -uzv *.esl
shred -uzv *.auth

-> Grub password protection (Additional protection)

grub-mkpasswd-pbkdf2 sudo nano /etc/grub.d/00_header

cat << EOF
set superusers="admin"
password_pbkdf2 admin HASH
EOF

where HASH is the hash generated earlier

sudo update-grub

Conclusion

Personally i think there is never a good reason to not encrypt your data if you care about it, just like there is never a good reason to use HTTP over HTTPS or to send out an unencrypted email, encrypting data-at-rest is just as important as encrypting data-in-transit. Windows did a good job by implementing Bitlocker for drive encryption, TPM, Secure Boot, Trusted Boot, Measured Boot all to prevent bootkits, rootkits and any kind of tampering with the boot process and we have taken a similar approach with our Arch Linux Installation to protect ourselves against those attacks.



Chady MORRA, 8/14/2021

Buy Me A Coffee