A hardened Gentoo-Linux/openrc base installation
This blog entry will demonstrate how to install a hardened x86_64 Gentoo Linux musl/openrc/UEFI signed UKI operating system on an encrypted ZFS pool with automatic decryption using TPM. This entry is based on the Gentoo x86_64 handbook and the Gentoo wiki. Gentoo supplies the right tools to build a Linux operating system from scratch, suited to the hardware and needs of the user. This form of customizability and optimizability together with the strong community behind Gentoo makes it a good choice for a desktop operating system.
Provisioning
To install Gentoo this guide will be using the Alpine Extended ISO. It provides all of the necessary utilities for bootstrapping Gentoo. Make sure to boot with secureboot in setup mode or to already have keys ready to deploy.
After booting the Alpine Linux extended ISO, partition the disks. For this action internet is required since zfs, sgdisk and various other necessary packages are not included on the extended ISO, therefore they need to be obtained from the Alpine package repository.
Set it up with setup-interfaces and setup-apkrepos:
- To use Wi-Fi simply run
setup-interfaces -rand selectwlan0or similar.
Install the necessary packages:
- The
zlevispackage is as of this moment not yet in the alpine package repository. Try to get it into thebinvia a different method and add its dependenciestpm2-toolsandjose.
and load the ZFS kernel module:
Wipe the existing disk partitions:
Create on the disk an EFI system partition (ESP) and a Linux filesystem partition (LFP):
Reload the device nodes:
Format the ESP with a FAT32 filesystem:
ZFS pool creation
The ZFS system pool is going to be encrypted. First generate an encryption key and save it temporarily to the file /tmp/rpool.key with:
sh# cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1 > /tmp/rpool.key && cat /tmp/rpool.key
All the while we use
zlevisfor automatic decryption, this key is required when making changes are made to the BIOS or secureboot, so make sure to save it.
Create the system pool:
sh# zpool create -f \
-o ashift=12 \
-O compression=lz4 \
-O acltype=posix \
-O xattr=sa \
-O dnodesize=auto \
-O encryption=on \
-O keyformat=passphrase \
-O keylocation=prompt \
-m none \
rpool /dev/<disk>2
Then create the system datasets:
sh# zfs create -o mountpoint=none rpool/root
sh# zfs create -o mountpoint=legacy -o quota=48g rpool/root/gentoo
sh# zfs create -o mountpoint=legacy -o quota=32g rpool/root/gentoo/var
sh# zfs create -o mountpoint=/home -o atime=off -o setuid=off -o devices=off -o quota=<home-quota> rpool/home
Setting the
<home-quota>depends on the total size of the pool, generally try to reserve some empty space in the pool.
Write the encryption key to TPM with zlevis:
To check if it worked, perform
zlevis decrypt rpool.
Finally, export the zpool:
Installation
To install Gentoo Linux on the system, the ESP and the datasets of the system pool have to be mounted to the live (ISO) environment.
First import and decrypt the system pool:
Then mount the datasets and the ESP on /mnt:
sh# mount -t zfs rpool/root/gentoo /mnt
sh# mkdir /mnt/var
sh# mount -t zfs rpool/root/gentoo/var /mnt/var
sh# mkdir /mnt/efi
sh# mount -t vfat /dev/disk/by-label/esp /mnt/efi
Now we are going to fetch a stage 3 tarball, the archive containing a minimal Gentoo environment. Which will act as the seed of the Gentoo install. Replace the <release_date> with the latest stage file release:
sh# wget https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-musl-hardened/stage3-amd64-musl-hardened-<release_date>.tar.xz #(1) #(2)!
-
It is also possible to use
linksinstead ofwgetwhich provides a small user interface for navigation: -
There are also other mirrors like
https://ftp.snt.utwente.nl/pub/os/linux/gentoo/releases/amd64/autobuilds/current-stage3-amd64-musl-hardened/which might provide a faster download depending on your location. Check out https://www.gentoo.org/downloads/mirrors/ for other mirrors.
Unpack the stage file in the root of the system:
To have a functional chroot into the system, copy resolv.conf and bind the system process directories:
sh# cp --dereference /etc/resolv.conf /mnt/etc/
sh# for dir in dev proc sys run; do
> mount --rbind --make-rslave /$dir /mnt/$dir
> done
sh# chroot /mnt
Configure portage (1) before doing anything else, an example file of its make.conf is given below:
- The package manager of Gentoo Linux, which provisions build files from the Gentoo ebuild repository or any additional ebuild repositories.
-
The
MAKEOPTSdefines (-jthe number of jobs/processes) and limits (-lthe load average) how many parallel make/compilation processes can be launched fromportage. The number of parallel processes are limited by the number of available logical CPUs and by RAM, each process can take up to2 GBof RAM. The load average-lis generally set slighly above the number of jobs-j. -
This selection of global (removed
-) USE flags is rather personal and should be set according to the preferences of the user.
Synchronise the ebuild repositories and emerge some relevant packages:
sh# emaint sync
sh# emerge -a app-portage/gentoolkit app-editors/vim sys-libs/timezone-data net-misc/openntpd sys-apps/musl-locales
Configure some key aspects of the system:
sh# echo <hostname> > /etc/hostname
sh# echo TZ="/usr/share/zoneinfo/<Region>/<City>" > /etc/env.d/00tz
sh# echo MUSL_LOCPATH="/usr/share/i18n/locales/musl" > /etc/env.d/01locales #(1)!
sh# env-update && source /etc/profile
sh# eselect locale set <locale> #(2)!
sh# rc-update add ntpd default
sh# passwd root #(3)!
-
Musl does not support locales out of the box. They are not necessary but some programs rely on them to set the language of their application.
-
The correct
<locale>can be found in the locale list: -
The root password does not really matter because it is going to be locked after a user has been created.
Edit the fstab to set the correct mounts:
rpool/root/gentoo / zfs rw,noatime,xattr,posixacl,casesensitive 0 1
rpool/root/gentoo/var /var zfs rw,noatime,nodev,nosuid,xattr,posixacl,casesensitive 0 2
/dev/disk/by-label/esp /efi vfat defaults,nodev,nosuid,noexec,umask=0077 0 2
tmpfs /tmp tmpfs rw,nodev,nosuid,noexec,mode=1777 0 0
proc /proc proc nodev,nosuid,noexec,hidepid=2 0 0
Configure the kernel command-line to be able to boot correctly:
Emerge sbctl and sbsigntools which will be used alongside dracut (1) to sign the build Unified Kernel Image (UKI):
- The initramfs builder.
Verify that secureboot is in
setup modewithsbctl status.
Create and enroll the secureboot keys into the system:
- Whilst enrolling the keys it might be necessary to add the
--microsoftflag if you are unable to use custom keys.
Enable dracut to create a UKI with ukify:
and enable automatic signing with sbsign:
Tell portage to generate a UKI when installing a kernel:
Set the required USE flags for systemd-utils such that on emerging the bootloader systemd-boot will be installed:
The installation of the hardware firmware on the system, such as the CPU microcode, is hardware specific:
The microcode updates for systems with an AMD CPU are all contained in sys-kernel/linux-firmware, accept its license:
sys-kernel/linux-firmware linux-fw-redistributable @BINARY-REDISTRIBUTABLE
and emerge them together with the Gentoo kernel, its necessary kernel modules for this system and the bootloader:
The microcode updates for systems with an Intel CPU require alongside sys-kernel/linux-firmware also sys-kernel/intel-microcode, accept its licenses:
sys-kernel/linux-firmware linux-fw-redistributable @BINARY-REDISTRIBUTABLE
sys-firmware/intel-microcode intel-ucode
and emerge them together with the Gentoo kernel, its necessary kernel modules for this system and the bootloader:
Install the bootloader on the ESP:
sh# bootctl install
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed" to "/efi/EFI/systemd/systemd-bootx64.efi".
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed" to "/efi/EFI/BOOT/BOOTX64.EFI".
Random seed file /efi/loader/random-seed successfully refreshed (32 bytes).
Created EFI boot entry "Linux Boot Manager".
and configure the bootloader:
One may verify the signed files by running
sbctl verify.
Generate the hostid and reconfigure the Gentoo Kernel:
Finally, add some services for ZFS:
sh# rc-update add zfs-mount sysinit
sh# rc-update add zfs-import sysinit #(1)!
sh# rc-update add zfs-load-key sysinit #(2)!
-
Omit if a faster boot time is preferred.
-
Omit if a faster boot time is preferred.
Now exit the chroot, unmount the datasets and reboot:
Optional installation
Repositories
GURU is an extra repository containing ebuilds that are not available in the Gentoo repository. Although the packages it contains might not be as well tested as in the main repository they are still necessary for some setups, such as some dependencies required for automatic decryption or graphical user interfaces. Add GURU with:
sh# emerge -a app-eselect/eselect-repository
sh# eselect repository enable guru
sh# emaint sync --repo guru
Portage-ample is an extra extra repository containing ebuilds maintained by us, the Ampel organisation. Add portage-ample with:
sh# eselect repository add portage-ample git https://git.ampel.dev/ampel/portage-ample
sh# emaint sync --repo portage-ample
Automatic decryption
Automatic decryption will be managed with zlevis, which is able to unlock an encrypted ZFS root pool with keys saved in a TPM. Currently it is only available in the portage-ample repository and also has some dependencies in the guru repository.
Set the dracut flag for zlevis:
emerge zlevis:
and enable the zlevis module for dracut:
Swap
To add swap to the system emerge zram-init:
Configure zram-init to create a swap device of size one fourth of the ram size:
load_on_start="yes"
unload_on_stop="yes"
num_devices="1"
type0="swap"
size0=`LC_ALL=C free -m | awk '/^Mem:/{print int($2/4)}'`
maxs0=1
algo0=zstd
labl0=zram_swap
and add zram-init to the default runlevel:
Compiler cache
Compiler cache can speed up recompile's, by avoiding recompilation of the same object files by fetching the result from a cache directory. The package ccache enables a compiler cache for C/C++ object files, present in the Gentoo Kernel for example. Emerge it with:
and edit its configuration file:
cache_dir = /var/cache/ccache
max_size = 20G
umask = 002
hash_dir = false
compiler_check = %compiler% -dumpversion
compression = true
compression_level = 1
Configure portage to allow ccache for specified packages:
such that for every C/C++ package with which you want to use ccache:
Users
To run processes securely, in an environment with fewer privileges, a user is necessary.
Before creating the user, emerge doas, to be able to "do as" root when it is required:
which requires us to set:
then
and configure doas by editing:
Now users who are in the wheel group are allowed to use the doas command to gain root privileges.
We can add a user, set its password and add it to the wheel group (if admin) with:
The wheel group should ideally only be assigned to one singular admin account. The users in the group are allowed to use the doas command to gain root privileges. This is necessary for installing packages and changing system files but not for a normal user.
If you have checked that doas works with the user then you can lock the root account because it imposes security risks if it is kept open. This can be done with:
and by changing its login shell to:
Networking
For desktop use NetworkManager is preferred over dhcpcd as network daemon, due to its versatility, i.e. Wi-Fi and VPN compatibility, MAC randomisation, et cetera.
First set the relevant USE flags:
Also make sure the
networkmanagerUSE flag is enabled in yourmake.conf.
Now emerge networkmanager:
and configure networkmanager to have MAC randomisation by editing:
[main]
hostname-mode=none
plugins=ifupdown,keyfile
[ifupdown]
managed=true
[device]
wifi.scan-rand-mac-address=yes
[connection-mac-randomization]
ethernet.cloned-mac-address=random
wifi.cloned-mac-address=random
Then stop any other network service, such as dhcpcd running in the dynamic runlevel, and enable NetworkManager:
For users to be able to modify connections on the system they will have to be added to the plugdev group.
Concluding remarks
This is the bare minimum for a Gentoo Linux desktop system. Some additional features such as bluetooth, laptop battery management, printer compatiblity, et cetera, have been documented well in the Gentoo Wiki, and can thus be found there. The next steps are the improvement of the security of the system and the configuration of the graphical session.