<!--
.. title: An encrypted-boot gentoo installation story
.. slug: an-encrypted-boot-gentoo-installation-story
.. date: 2025-01-11 12:15:49 UTC
.. tags: linux,gentoo,install,security
.. category: 
.. link: 
.. description: 
.. type: text
.. author: Dejice Jacob
-->

## How I got to gentoo
Like many people, I have gone through a few linux distros in my lifetime.
Over the span of two decades, I ended up going through OpenSuse &rarr; Ubuntu &rarr; Debian (Stable) &rarr;
Debian (Testing) &rarr; Gentoo. 
I keep an eye on distributions quite often, but the motivation for moving distributions crosses a threshold 
when a major workflow or technology disruption occurs within the distribution. It also correlates with my
(*Slowly*) increasing knowledge and comfort with delving into how systems are put together. 

<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Gentoo_Penguin_Baby_%2824940372635%29.jpg/209px-Gentoo_Penguin_Baby_%2824940372635%29.jpg"
 title="A Southern Gentoo Penguin"
 alt="Southern Gentoo Penguin"
 align="right">
</img>


I moved from Ubuntu &rarr; Debian (Stable) when Ubuntu decided to 
develop [Mir](https://fridge.ubuntu.com/2013/03/04/mir-an-outpost-envisioned-as-a-new-home) rather
than developing for Wayland. 
I am always enthusiastic about new software being developed, if only to see what could have been. The vision of convergence
between devices was a cool one and given the right circumstances, may have succeeded. 
However, any large scale surgery of this sort which veers off and does its own thing risks failure. 

Another thing that philosophically did not sit right with me was the usage of [*snap* packages](https://snapcraft.io/blog/a-technical-comparison-between-snaps-and-debs). 
To me this felt like a way to reduce the pressure of maintenance in the short-term at the 
expense of long-term fragmentation. Desktop *\*nix* distributions are not **yet** (it is now decade no.3 of
trying to conquer the desktop) popular enough. I wish it was different (*sigh*)!
Like any demand-supply equation, any let-up in the pressure to maintain 
library/API compatibility, in my opinion, would just lead to fragmentation and end-user frustration in the long-run.

So I decided to do the Ubuntu &rarr; Debian (Stable) switch. As I was using a slightly older laptop, and it had all the
drivers and packages I needed, I wonder why I did not do this earlier (*Doh*)! After two of my upgrades between major 
Debian revisions ended up requiring re-installations, I heard of this amazing new term called *rolling*-distributions. 
(Please excuse the *na&#239;vete* and yes, I really am that na&#239;ve)! 

The next trigger to move was Debian's [embrace](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=727708#6734)  of [Systemd](https://systemd.io). I feel more comfortable with [openRC](https://github.com/OpenRC/openrc) which I think keeps better to the
overall \*nix philosophy. 
I had by now done enough systems level software development and debugging to no longer be afraid of doing silly things
that break stuff (*a little knowledge being dangerous* and all that). Building a new system from ground up would be something 
that would allow me to explore and understand the guts of my own system. 

#### Enter Gentoo
Keeping with the theme of going further upstream and closer to the source 
(*wipe that smug [Icarus](https://en.wikipedia.org/wiki/Icarus) image from your mind*), 
the choice was between [Linux from Scratch](https://www.linuxfromscratch.org/), [Slackware](http://www.slackware.com) and [Gentoo](https://www.gentoo.org). In the end, I went with Gentoo due to the package manager and the sheer amount of 
support and documentation on the website.
It should help reduce the amount of debugging and maintenance work I have to do, while still making me 
feel like a proper computer scientist. *Vanity and na&#239;vete -- what could possibly go wrong?*

<img src="https://wiki.gentoo.org/images/thumb/e/ee/Gblend.png/234px-Gblend.png"
 title="Gentoo Linux"
 alt="Gentoo Linux Logo"
 align="right">
</img>



## Why ?! 
This leads us onto the purpose of this blog. Installing gentoo is relatively easy but time consuming if we go through 
the relatively straightforward instructions in the marvellous [Gentoo AMD64 handbook](https://wiki.gentoo.org/wiki/Handbook:AMD64). However, I wanted to do a fully encrypted `/boot` drive as well and had to search around for various instructions. 
This is the command log (My Gentoo installation story) for my own personal notes. Someone else finding it useful is just a bonus.
For more comprehensive information and various options, Each link in the subsections below are linked 
to the much more comprehensive information in the gentoo [wiki](https://wiki.gentoo.org/wiki/Handbook:AMD64). 

## Gentoo AMD64 installation with encrypted */boot*
#### Obtaining and preparing the [installation media](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Media)
Very detailed instructions are already provided
in the [handbook](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Media)
and I don't have anything new to add. 
Switch off secure-boot in the BIOS and choose to boot  from the USB drive that was just prepared. 
Once the laptop has been booted into the linux kernel and shows a root prompt, we will need to 
set up [networking](#configuring-the-network). 

#### Partitioning the storage [disks](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Disks)
I have a 16 GiB Laptop with 1TB of space on the SSD. I wanted to partition it with the following schema:

```bash
NAME                     MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
nvme0n1                  259:0    0 953.9G  0 disk
├─nvme0n1p1              259:2    0     2M  0 part
├─nvme0n1p2              259:4    0   512M  0 part
│ └─luks_boot            253:0    0   496M  0 crypt
├─nvme0n1p3              259:6    0   128M  0 part
└─nvme0n1p4              259:8    0 953.2G  0 part
  └─luks_root            253:1    0 953.2G  0 crypt
    ├─osvg-swap          253:2    0    8G  0 lvm   [SWAP]
    ├─osvg-gentoo--root  253:3    0    64G  0 lvm   /
    ├─osvg-gentoo--home  253:4    0    16G  0 lvm   /home
    └─osvg-data          253:5    0 865.2G  0 lvm   /media/data
```

Zap all pre-existing partitions on the disk. 
```bash
livecd ~# sgdisk --zap-all /dev/nvme0n1
```

Ensure the first *1MiB* is left for grub to be written into raw device head. So we create a *1MiB* partition with *offset=1MiB*.
```bash
livecd ~# sgdisk  --new=1:1M:+2M /dev/nvme0n1
livecd ~# sgdisk  --new=2:0:+512M /dev/nvme0n1
livecd ~# sgdisk  --new=3:0:+128M /dev/nvme0n1    
livecd ~# sgdisk  --new=4:0:0 /dev/nvme0n1
```

Change the names of the partitions and their filesystem types in the GPT partition table. 
A list of partition types can be obtained with `sgdisk -L`. 
```bash
livecd ~# sgdisk  --typecode=1:ef02 --typecode=2:8300 --typecode=3:ef00 --typecode=4:8300 /dev/nvme0n1
livecd ~# sgdisk --change-name=1:GRUB --change-name=2:/boot --change-name=3:EFI-SP --change-name=4:OS /dev/nvme0n1
```

Encrypt the `/boot` partition with a password. While it is certainly more robust to have a separate Keyfile
stored on another USB flash drive, it is cumbersome to carry around. Also, if you forget it or lose it,
then it can be a pain. I am just going to use a plain old password for this in this case. 
Additionally, grub can not yet decrypt keys in the default LUKS2 format (argon2id) and requires the
key to be in the LUKS1 default format of PBKDF2. So the `/boot` partition is formatted with LUKS1. 
I will maybe write up a detached header version in a future post.
```bash
livecd ~# cryptsetup luksFormat --key-size=512 --type=luks1 /dev/nvme0n1p2
```

Format the `/boot` and `efi-sp` partitions 
```bash
livecd ~# cryptsetup open /dev/nvme0n1p2 /dev/mapper/luks_boot
livecd ~# mkfs.ext4 -L boot /dev/mapper/luks_boot
livecd ~# mkfs.vfat -n EFI-SP -F 16 /dev/nvme0n1p3 
```

To obtain an encrypted *`/root, /home and swap`* partition, I decided to use 
Logical Volume Management ([LVM](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/configuring_and_managing_logical_volumes/overview-of-logical-volume-management_configuring-and-managing-logical-volumes#lvm-architecture_overview-of-logical-volume-management)) on a LUKS encrypted partition. 
With experience, I can say that if you intend on using the KDE desktop, the machine should ideally have 
32GB of RAM. Some packages such as firefox, the qtwebkit renderer etc require greater than 16GB of RAM. 
This would then influence the amount of swap space that you should keep aside. Since I have 32GiB of RAM, 
and I  do not want suspend, only 8GiB of swap space is allocated. The beauty of LVM is that 
this can be resized in the future if required. 
```bash
livecd ~# cryptsetup luksFormat --key-size=512 --key-slot=1 /dev/nvme0n1p4 
livecd ~# cryptsetup open /dev/nvme0n1p4 luks_root    
livecd ~# pvcreate /dev/mapper/luks_root  # Create a Physical volume on the decrypted device
livecd ~# vgcreate osvg /dev/mapper/luks_root  # Create a volume group on the Physical volume
livecd ~# lvcreate -L 8G -n swap osvg  # Create swap space on the encrypted LVM 
livecd ~# lvcreate -L 64G -n gentoo-root osvg  # Create /root on the encrypted LVM 
livecd ~# lvcreate -L 16G -n gentoo-home osvg  # Create /home on the encrypted LVM 
livecd ~# lvcreate -l 100%FREE -n data osvg  # Create a separate data partition
```

Format the partitions created in the LVM.
```bash
livecd ~# mkswap -L swap /dev/mapper/osvg-swap 
livecd ~# mkfs.ext4 -L root /dev/mapper/osvg-gentoo--root
livecd ~# mkfs.ext4 -L home /dev/mapper/osvg-gentoo--home
livecd ~# mkfs.ext4 -L data /dev/mapper/osvg-data
```

Now mount the encrypted drives to various directories
```bash
mkdir -p /mnt/gentoo/{root,home,data}
mount /dev/mapper/osvg-gentoo--root /mnt/gentoo/root
mount /dev/mapper/osvg-gentoo--home /mnt/gentoo/home
mount /dev/mapper/osvg-data /mnt/gentoo/data
```

#### Configuring the [network](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Networking)
I already have an ethernet cable to connect, so I did not require to set up WiFi. Gentoo already has 
the `net-setup` utility to help with setting up WiFi. 
The network interface names can be obtained using the `ip link` command. 
Set-up is through a fairly easy menu driven `ncurses` style interactive interface. 
```bash
livecd ~# net-setup 
```
1. For ethernet, configure the wired ethernet interface (starts with *enp*...) 
2. In the case of WiFi, choose wireless WiFi interface (starts with *wlp*...) 

Make sure the time of the system is accurate. I utilised a simple **NTP** client (*chronyd*) 
to correct the time. 
```bash
livecd ~# chronyd -q
```

#### Obtaining the [Stage-3](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Stage) Installation files
I like *openrc* and chose the *desktop-openrc* profile for the stage-3 tarball. 
Use the livecd built-in *ncurses* browser to obtain the stage-3 tarball. Alternatively 
download it on another PC and transfer via another USB device. 

#### Setup base [root filesystem](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Stage), configure [portage](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Stage#Configuring_compile_options) and [gentoo base](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Base) 
Assuming the stage-3 tarball is in `/mnt/gentoo/data` untar it to the target storage-device's `/root` directory. 
In our case, we have mounted it to `/mnt/gentoo/root`. 
```bash
livecd ~# cd /mnt/gentoo/data
livecd ~# tar -Jxpvf stage3_tarball.tar.xz --xattrs-include='*.*' --numeric-owner -C /mnt/gentoo/root  
livecd ~# cd  /mnt/gentoo/root
livecd ~# cp --dereference  /etc/resolv.conf /mnt/gentoo/root/etc/resolv.conf 
livecd ~# echo MAKEOPTS=\"-j$(nproc)\" >> /mnt/gentoo/root/etc/portage/make.conf
livecd ~# echo ACCEPT_KEYWORDS=\"amd64\" >> /mnt/gentoo/root/etc/portage/make.conf
livecd ~# echo USE=\"udev lvm dbus X pulseaudio networkmanager clang\" >> /mnt/gentoo/root/etc/portage/make.conf
```
Add the CPU flags for your host to `/mnt/gentoo/root/etc/portage/make.conf`. Make sure you replace the newly added line
to the format `CPU_FLAGS_X86="aes ....."`. 
```bash
livecd ~# echo $(cpuid2cpuflags) >> /mnt/gentoo/root/etc/portage/make.conf
```
Now add the [video-cards](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Base#VIDEO_CARDS)
depending on your machine. On this machine, I had an AMD video card. 
```bash
livecd ~# echo VIDEO_CARDS=\"amdgpu radeonsi\" >> /mnt/gentoo/root/etc/portage/make.conf
```
Add some miscellaneous devices as well. `libinput` provides input handling for display servers. 
```bash
livecd ~# echo INPUT_DEVICES=\"libinput\" >> /mnt/gentoo/root/etc/portage/make.conf
livecd ~# echo SANEBACKENDS=\"hp\" >> /mnt/gentoo/root/etc/portage/make.conf
```
Within the portage configuration file `/mnt/gentoo/root/etc/portage/make.conf` update the value of the 
variable `COMMON_FLAGS` to `COMMON_FLAGS="-march=native -O2 -pipe"`
Also select from the worldwide mirrors to download software from. 
```bash 
livecd ~# mirrorselect -i -o >> /mnt/gentoo/root/etc/portage/make.conf
```
Mount the system files required to prepare the target computer's `chroot` environment. 
```bash
livecd ~# mount --types proc /proc /mnt/gentoo/root/proc
livecd ~# mount --rbind /sys /mnt/gentoo/root/sys
livecd ~# mount --make-rslave /mnt/gentoo/root/sys
livecd ~# mount --rbind /dev /mnt/gentoo/root/dev
livecd ~# mount --make-rslave /mnt/gentoo/root/dev
livecd ~# mount --bind /run /mnt/gentoo/root/run
livecd ~# mount --make-slave /mnt/gentoo/root/run
```
Enter the `chroot` environment. 
```bash
livecd ~# umount /mnt/gentoo/home /mnt/gentoo/data
livecd ~# chroot /mnt/gentoo/root /bin/bash
livecd ~# source /etc/profile
livecd ~# export PS1="(chroot) ${PS1}"
(chroot) livecd ~# export PS1="(chroot) ${PS1}"
(chroot) livecd ~# mkdir -p /home /media/data
(chroot) livecd ~# mount /dev/mapper/osvg-gentoo--home /home
(chroot) livecd ~# mount /dev/mapper/osvg-data /media/data
(chroot) livecd ~# swapon /dev/mapper/osvg-swap
```
Prepare the EFI System Partition. 
```bash
(chroot) livecd ~# mount /dev/mapper/luks_boot /boot
(chroot) livecd ~# mkdir -p /boot/efi 
(chroot) livecd ~# mount /dev/nvme0n1p3 /boot/efi
```
Synchronise `emerge`'s software package list with upstream mirrors
```bash
(chroot) livecd ~# emerge-webrsync
```
Setup Locale Details
```bash
(chroot) livecd ~# echo "Europe/London" > /etc/timezone
(chroot) livecd ~# echo "C.UTF8 UTF-8" >> /etc/locale.gen
(chroot) livecd ~# echo "en_GB ISO-8859-1" >> /etc/locale.gen
(chroot) livecd ~# echo "en_GB.UTF-8 UTF-8" >> /etc/locale.gen
(chroot) livecd ~# locale-gen
(chroot) livecd ~# env-update
```

#### Configuring the [Linux Kernel](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Kernel)
Obtain all kernel related gentoo packages. 
```bash 
(chroot) livecd ~# mkdir -p /etc/portage/package.license 
(chroot) livecd ~# echo "sys-kernel/linux-firmware linux-fw-redistributable" >> /etc/portage/package.license/kernel
(chroot) livecd ~# emerge --ask --quiet-build sys-kernel/linux-firmware sys-fs/cryptsetup sys-kernel/gentoo-sources sys-kernel/genkernel 
(chroot) livecd ~# eselect kernel set 1  # this should link /usr/src/linux to current kernel source
(chroot) livecd ~# echo "sys-boot/grub:2 device-mapper" > /etc/portage/package.use/grub2
(chroot) livecd ~# emerge --ask --quiet-build sys-boot/grub sys-fs/genfstab
## Ensure all the relvant drives (including swap are already mounted / turned on
(chroot) livecd ~# genfstab -Up / >> /etc/fstab 
## add noauto to the /boot and /boot/efi mount-points in /etc/fstab
```

Generate a LUKS key and add the generated key to the block device holding the encrypted 
partition. In my case, this was  `luks_boot (/dev/nvme0n1p2)` and optionally`luks_root (/dev/nvme0n1p4)`. 
This will be the key that is used by the kernel-*initramfs*  to decrypt and mount the encrypted LVM volume and
(optionally) the `/boot` partition. There is a good argument to not automatically decrypt the  `/boot` partition. 
This is why I have decided it is optional. **Remember**: We are adding this newly generated key to *Key-slot:0*
of `luks_root` -- this is why we carefully added the original-key during disk-partitioning in *Key-slot:1*. 
Using *Key-slot:0* will make it faster during actual booting and each key is tried in sequence.
[link](#partitioning-the-storage-disks). 
```bash
(chroot) livecd ~# mkdir -p /etc/luks/mnt/key
(chroot) livecd ~# dd if=/dev/urandom of=/etc/luks/mnt/key/boot_os.keyfile bs=4096 count=1
(chroot) livecd ~# chmod u=rx,go-rwx /etc/luks
(chroot) livecd ~# chmod u=r,go-rwx /etc/luks/mnt/key/boot_os.keyfile
(chroot) livecd ~# cryptsetup --key-slot=0 /dev/nvme0n1p4  /etc/luks/mnt/key/boot_os.keyfile  # luks_root
(chroot) livecd ~# cryptsetup --key-slot=1 /dev/nvme0n1p2  /etc/luks/mnt/key/boot_os.keyfile  # luks_boot
(chroot) livecd ~# echo "luks_boot UUID=$(blkid -s UUID -o value /dev/nvme0n1p2) /etc/luks/mnt/key/boot_os.keyfile luks,discard" >> /etc/crypttab
(chroot) livecd ~# echo "luks_root UUID=$(blkid -s UUID -o value /dev/nvme0n1p4) /etc/luks/mnt/key/boot_os.keyfile luks,discard" >> /etc/crypttab
```
**NOTE**: The key should be generated in a directory with the following pattern `${INITRAMFS_OVERLAY}/mnt/key`. 
The `genkernel` tool when provided the `INITRAMFS_OVERLAY` variable will use this overlay within its filesystem. 
The kernel will then look for the internal key in `/mnt/key`. 

While you could spend a long time configuring the kernel, I think it is easier to use `genkernel` to generate 
a kernel with a lot of options. We can always slim down the kernel afterwards. We can see the list of kernels 
with `eselect kernel list`. 
```bash
(chroot) livecd ~# eselect kernel set 1  
```
Set-up the following configurations in `/etc/genkernel.conf`. **NOTE**: Without the `INITRAMFS_OVERLAY`, the initramfs kernel cannot decrypt the enncrypted block device holding 
the LVMs for `/root, /home` etc. 
```bash
NOCOLOR="false"
LVM="yes"
LUKS="yes"
GK_SHARE="${GK_SHARE:-/usr/share/genkernel}"
CACHE_DIR="/var/cache/genkernel"
DISTDIR="${GK_SHARE}/distfiles"
LOGFILE="/var/log/genkernel.log"
LOGLEVEL=1
ZFS="no"
BTRFS="no"
XFSPROGS="no"
DEFAULT_KERNEL_SOURCE="/usr/src/linux"
INITRAMFS_OVERLAY="/etc/luks"
```

Now execute `genkernel` and prune as much of the kernel config that you don't need before executing. (Ensure 
that `/boot` and `/boot/efi` are mounted)!
```bash
(chroot) livecd ~# genkernel --menuconfig --luks --lvm --no-zfs all
```

#### [Configuring the GRUB bootloader](https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Bootloader#Default:_GRUB)
Ensure the following settings are inserted into the *Grub* configuration file in `/etc/default/grub`
```bash
GRUB_DISTRIBUTOR="Gentoo"
GRUB_TIMEOUT=3
GRUB_TIMEOUT_STYLE=menu
GRUB_DISABLE_LINUX_PARTUUID=false
GRUB_PRELOAD_MODULES="part_gpt part_msdos lvm"
GRUB_CMDLINE_LINUX_RECOVERY="recovery"
GRUB_ENABLE_CRYPTODISK=y
```
Also add the commandline for the linux kernel during boot before downloading and installing `grub`
```bash
(chroot) livecd ~# echo GRUB_CMDLINE_LINUX=\"keymap=uk dolvm crypt_root=UUID=$(blkid -s UUID -o value /dev/nvme0n1p4) root_key=boot_os.keyfile root_trim=yes resume=/dev/osvg/swap\" >> /etc/default/grub
(chroot) livecd ~# emerge --ask --quiet-build sys-boot/grub
(chroot) livecd ~# mkdir /boot/grub
(chroot) livecd ~# grub-mkconfig -o /boot/grub/grub.cfg 
(chroot) livecd ~# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id="grub"
```
This should install various bootloader stages in their respective locations. Set `keymap=uk`
in the file `/etc/conf.d/keymaps`. Otherwise, a recovery shell dropping you into a 
different keymap can be frustrating for passwords and debugging in a shell. 

#### Preparing to reboot into our newly installed bare-bones system. 
We are now ready to shutdown and reboot into our newly installed system. 
Unmount all the mount points, bind-mounts and dmcrypt. 
First set a root password for the new system. Then unmount all our devices. 
```bash
(chroot) livecd ~# passwd     #Set new password
(chroot) livecd ~# umount /boot/efi
(chroot) livecd ~# umount /boot
(chroot) livecd ~# cryptsetup close luks_boot 
(chroot) livecd ~# swapoff /dev/mapper/osvg-swap
(chroot) livecd ~# umount /media/data /home
(chroot) livecd ~# exit
livecd ~# umount /mnt/gentoo/root/proc
livecd ~# umount --recursive /mnt/gentoo/root/dev /mnt/gentoo/root/sys /mnt/gentoo/root/run
livecd ~# shutdown -Ph now 
```
Now reboot into the newly installed system and put in the password for grub. This should 
then drop you into a prompt for the root password for the new system. 

(**Optional**): It might be a good idea to automatically decrypt the encrypted `/boot` block device 
so that we can very simply just use a `mount /boot` command that was earalier set up in `/etc/fstab`. 
We add entries for the  *dmcrypt* service  to automatically decrypt `/boot` during bootup and start
the dmcrypt service
```bash
hostname ~# echo "target=luks_boot" >> /etc/conf.d/dmcrypt
hostname ~# echo source=UUID=\"$(blkid -s UUID -o value /dev/nvme0n1p2)\" >> /etc/conf.d/dmcrypt
hostname ~# echo "key=/etc/luks/mnt/key/boot_os.keyfile" >> /etc/conf.d/dmcrypt
hostname ~# rc-update add dmcrypt boot
hostname ~# rc-service dmcrypt start
```

## Conclusion
This will get gentoo booting into a shell. Modern desktop computing is however a lot more. 
~~I will chronicle my system setup in a further post.~~
My **gentoo** desktop installation saga continues in [Part-II](../a-gentoo-installation-story-continued). 
