User:Screenager/install/laptop rootfsencryption detached luks header
Introduction
Why
Gentoo allows for extreme customization starting with the installation process. This guide is meant to explain an advanced encrypted installation using logical volume management underneath, aiming to extend the 'Preparing the disks' section in the official handbook.
Prerequisites
Block Devices
Without understanding block devices fully, it will be hard to follow this guide. There will be encapsulating of abstraction layers. If this sounds scary, careful reading of the following articles is advised.
It is important to understand block devices. The official handbook offers information for that: Link
dm-crypt
This software will be responsible for creating an encrypted container on your block device, which in itself will be treated as just another block device when mounted. More information here: Link
LVM - Logical Volume Manager
This will add more block device abstraction layers. Again it is advised to read the wiki article beforehand: Link
Secure Wipe
This guide will be written for installation on a SSD, therefore a secure wipe beforehand is advised: Link
Preparing the disks
Example Setup:
A detached Header placed on the same physical drive as the luks container is no security improvement, but there is also no downside to just always use detached headers, even if they are not placed on a removable device.
Output generated with lsblk -o NAME,PARTLABEL,SIZE,FSTYPE,TYPE,MOUNTPOINT
NAME PARTLABEL SIZE FSTYPE TYPE MOUNTPOINT
sda 465,8G disk
|-sda1 efi 256M vfat part /mnt/gentoo/efi
|-sda2 bootx 768M ext4 part /mnt/gentoo/boot
`-sda3 luks 464,8G part
`-luks 464,8G LVM2_member crypt
|-vg1-swap 16G swap lvm [SWAP]
`-vg1-root 448,5G ext4 lvm /mnt/gentoo
Due to the detached Header lsblk is showing a partition without a filesystem for sda3
By now Networking should be completed and all Prerequisites should be understood. First identify your block devices, going forward this guide will use
root #
lsblk -o NAME,PARTLABEL,SIZE,FSTYPE,TYPE,MOUNTPOINT
NAME PARTLABEL SIZE FSTYPE TYPE MOUNTPOINT sda 465,8G disk
If the device got erased just now, it might be necessary to run
root #
partprobe /dev/sda
Creating the partitions
Invoke fdisk:
root #
fdisk /dev/sda
Welcome to fdisk (util-linux 2.38.1). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. Device does not contain a recognized partition table. Created a new DOS disklabel with disk identifier 0xba70857e. Command (m for help):
Next create a GPT partition table and a 256MB ESP partition:
Command (m for help):
g
Created a new GPT disklabel (GUID: 226B0C64-4F00-4A44-86B3-D897AB1F5009).
Command (m for help):
n
Partition number (1-128, default 1): 1 First sector (2048-976773134, default 2048): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-976773134, default 976773119): +256M Created a new partition 1 of type 'Linux filesystem' and of size 256 MiB.
Command (m for help):
t
Selected partition 1 Partition type or alias (type L to list all): 1 Changed type of partition 'Linux filesystem' to 'EFI System'.
Now create a 768MB extended Boot Partition:
Command (m for help):
n
Partition number (2-128, default 2): First sector (526336-976773134, default 526336): Last sector, +/-sectors or +/-size{K,M,G,T,P} (526336-976773134, default 976773119): +768M Created a new partition 2 of type 'Linux filesystem' and of size 768 MiB.
Command (m for help):
t
Partition number (1,2, default 2): Partition type or alias (type L to list all): 136 Changed type of partition 'Linux filesystem' to 'Linux extended boot'.
Finnally create a partition for the rest of the drive:
Without activating the SSD discard function it may be beneficial to leave 10-15% of unpartitioned space
Command (m for help):
n
Partition number (3-128, default 3): First sector (2099200-976773134, default 2099200): Last sector, +/-sectors or +/-size{K,M,G,T,P} (2099200-976773134, default 976773119): Created a new partition 3 of type 'Linux filesystem' and of size 464.8 GiB.
Write the changes with:
Command (m for help):
w
The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks.
Now format the ESP partitions with fat32 and the extended Boot partition with ext4:
root #
mkfs.vfat -F 32 /dev/sda1
root #
mkfs.ext4 /dev/sda2
Partitionlabels may be set with parted or Gparted when working from a GUI.
root #
parted
GNU Parted 3.6 Using /dev/sda Welcome to GNU Parted! Type 'help' to view a list of commands. (parted) name 2 'bootx'
Finnaly make shure everything is how it should be:
root #
lsblk -o NAME,PARTLABEL,SIZE,FSTYPE,TYPE,MOUNTPOINT
NAME PARTLABEL SIZE FSTYPE TYPE MOUNTPOINT sda 465.8G disk ├─sda1 efi 256M vfat part ├─sda2 bootx 768M ext4 part └─sda3 cryptContainer 464.8G part
Cryptsetup
Run
root #
cryptsetup benchmark
to make shure everything is ready to continue. First, temporarily mount the extended boot partition to save the header there, create a temp folder:
root #
mkdir /mnt/tmp
and mount the partition:
root #
mount /dev/sda2 /mnt/tmp
Now run
root #
cryptsetup -c aes-xts-plain64 -s 512 -y luksFormat /dev/sda3 --header /mnt/tmp/header.img
It may not be unwise to use a rather simple password in the beginning. It is possible to destroy the first keyslot later and use a more secure password once everything is running. Take special care in not copying the header until it is fully secured.
If the header image file is lost all data will be unrecoverable
To instantly check if everything worked run:
root #
cryptsetup luksDump /dev/sda3 --header /mnt/tmp/header.img
continue with opening and mapping the luks container:
root #
cryptsetup luksOpen /dev/sda3 lukscont --header /mnt/tmp/header.img
this will create the lukscont block device in /dev/mapper/
Now it is save to unmount the extended boot partition again:
root #
umount /mnt/tmp
and
root #
rmdir /mnt/tmp
to clean up. lsblk should now show the new logical block device:
root #
lsblk -o NAME,PARTLABEL,SIZE,FSTYPE,TYPE,MOUNTPOINT
NAME PARTLABEL SIZE FSTYPE TYPE MOUNTPOINT sda 465.8G disk ├─sda1 efi 256M vfat part ├─sda2 bootx 768M ext4 part └─sda3 cryptContainer 464.8G part └─lukscont 464.8G crypt
LVM Setup
With LVM it will be easily possible to encrypt and decrypt multiple partitions with a single password/keyfile. LVM will also allow easy extending or shrinking of partitions as well as percentage based allocation. Start by creating a pysical volume:
root #
pvcreate /dev/mapper/lukscont
follwing by creating a volume group within the just created pysical volume
root #
vgcreate vg1 /dev/mapper/lukscont
this will create the volume group vg1. Next it is time to create logical volumes for a swap and root partition.
root #
lvcreate --size 16G --name swap vg1
and
root #
lvcreate --size 100G --name root vg1
It will be very easy to create more partitions later or extent the root partition to the full size of the volume group
Next, formate and enable the swap partition:
root #
mkswap /dev/vg1/swap
root #
swapon /dev/mapper/vg1-swap
check with:
root #
swapon -d
Format the root partition and prepare the filesystem:
root #
mkfs.ext4 /dev/vg1/root
root #
mkdir /mnt/gentoo
root #
mount /dev/vg1/root /mnt/gentoo/
root #
mkdir /mnt/gentoo/efi
root #
mkdir /mnt/gentoo/boot
root #
mount /dev/sda1 /mnt/gentoo/efi/
root #
mount /dev/sda2 /mnt/gentoo/boot/
which concludes the disk preparation and filesystem setup. Perform a final check with lsblk:
root #
lsblk -o NAME,PARTLABEL,SIZE,FSTYPE,TYPE,MOUNTPOINT
NAME PARTLABEL SIZE FSTYPE TYPE MOUNTPOINT sda 465.8G disk ├─sda1 efi 256M vfat part /mnt/gentoo/efi ├─sda2 bootx 768M ext4 part /mnt/gentoo/boot └─sda3 cryptContainer 464.8G part └─lukscont 464.8G LVM2_member crypt ├─vg1-swap 16G swap lvm [SWAP] └─vg1-root 100G ext4 lvm /mnt/gentoo
Continue with the next part of the handbook until 'Configuring the bootloader'
Configuring the bootloader
Dracut and detached headers without using systemd seem to be broken as of 2023. If using this configuration the system won't boot without user intervention
Remember to emerge LVM and dm-crypt too, while chrooted and add them to OpenRC. Then beginn by installing grub:
root #
emerge --ask sys-boot/grub
and install it to UEFI with:
root #
grub-install --target=x86_64-efi --efi-directory=/efi
generate a config for grub to boot
root #
grub-mkconfig -o /boot/grub/grub.cfg
Next it is time to configure dracut
It might be a good idea to specify a keymap when facing a password input
add_dracutmodules+=" dm crypt lvm resume "
hostonly=yes
kernel_cmdline="rd.vconsole.keymap=<your_keymap_here> rd.vconsole.unicode"
and rebuild the initramfs from chroot with specifing the kernel after passing empty parameters:
root #
dracut "" 6.1.67-gentoo-dist --force
If the system is now rebooted it will propably drop to dracuts rescue shell where the lukscontainer will need to be mounted (remember to mount and afterwards unmount your partition with the header)
dracut:/#
cryptsetup luksOpen /dev/sda3 lukscont --header /mnt/tmp/header.img
and then activate the lvm volume group with
dracut:/#
lvm vgchange -ay
exit the rescue shell with
dracut:/#
exit
and the system should boot.
Modifying Dracut
Dracut is build with customization in mind, it not only allows but encourages users to hook into any stage of it's bootstrap process. Check the official documentation with:
root #
man dracut.modules
At this point it is clear that a script needs to be installed into dracut that will automate what we did before during the rescue shell. Start by creating a new folder:
root #
mkdir /usr/lib/dracut/modules.d/92local-root
For a minimal example two files within that folder need to be created:
#!/bin/bash
check(){
return 0
}
install(){
inst_hook initqueue/settled 92 "$moddir/mount-root.sh"
}
this file will tell dracut to always install the specified script and where in the bootstrap process it will be inserted into the loop. The actual script will be:
#!/bin/bash
#This function will get looped in dracuts initqueue
mount_local_root(){
if [ ! -b /dev/vg1/root ] #Check if root already is mountable
then
mkdir /tmp/mnt
mount /dev/sda2 /tmp/mnt #mount the header partition
dmesg -D #disables the kernel console output in order to not clutter the screen
read -s -p "Enter Password:" pword
echo $pword | cryptsetup luksOpen /dev/sda3 lukscont --header /tmp/mnt/header.img
unset pword # we do NOT want our password to stay somewhere in memory
if [ ! -b /dev/mapper/lukscont ] #check if unlock succeeded
then
echo "YOU DIDN'T SAY THE MAGIC WORD"
dmesg -E
umount /tmp/mnt
else
lvm pvscan
lvm vgscan
lvm lvscan
lvm vgchange -ay
dmesg -E
umount /tmp/mnt
fi
fi
}
mount_local_root
This script will automate the boot process and allow dracut to mount the root partition and continue with booting after entering the password. Finally rebuild the initramfs with
root #
dracut "" 6.1.67-gentoo-dist --force
after reboot it should be now possible to boot as expected.