User:Liassica/Secure Arch Setup

From ArchWiki

This article is not officially supported.

The Arch Linux community does not offer support for the information contained in this page; for installation procedures, the Installation guide is the only officially supported document. The content below is mainly maintained by User:Liassica, who last reviewed it on 2023-10-03, and it may be out of date or inaccurate.

Acknowledgments

This guide is primarily a synthesis of information from these sources:

Overview

This guide makes use of secure boot, unified kernel images, dm-crypt, Btrfs, and systemd-homed to create a secure and robust system.

Pre-installation

Follow the installation guide and stop before partitioning the disks. Ensure that your system is booted in 64-bit UEFI mode, otherwise this guide will not work.

Partition the disks

Create the following GPT layout using your chosen partitioning tool. GPT fdisk is recommended.

Partition table
Mount point Partition Partition type (GUID) Partition label Suggested size
/mnt/efi /dev/efi_system_partition EFI system partition (ef00) ESP 300M-1G, depending on the number of EFI binaries you plan to keep in it
/mnt /dev/root_partition Linux x86-64 root (/) (8304) CRYPTROOT At least 10G, more depending on the amount of software you plan to install. 50G is a safe bet if you don't want to worry about it.
/mnt/home /dev/home_partition Linux x86-64 home (/home) (8302) HOME Remainder of the device

Format the partitions

Encrypted root

Since we are using unified kernel images, /boot does not have to be accessible at startup, and therefore the root partition will be LUKS2 encrypted. The encryption unlocking can optionally be keyed to a TPM device, in which case the encryption password can be left blank.

Create the dm-crypt container:

# cryptsetup luksFormat /dev/disk/by-partlabel/CRYPTROOT

Next, open the encrypted container:

# cryptsetup open /dev/disk/by-partlabel/CRYPTROOT root

Finally, format the mapped device with Btrfs:

# mkfs.btrfs -L Root /dev/mapper/root

Home partition

The home partition itself will be left unencrypted as to avoid double encrypting user home directories created with systemd-homed.

Format the home partition as Btrfs:

# mkfs.btrfs -L Home /dev/disk/by-partlabel/HOME

EFI System Partition

As per the UEFI specification, the EFI System Partition must be FAT32:

# mkfs.fat -F 32 -n ESP /dev/disk/by-partlabel/ESP

Btrfs subvolume layout

We will use a Btrfs subvolume expanding upon Snapper#Suggested filesystem layout.

First, mount the whole root container:

# mount /dev/mapper/root /mnt
# cd /mnt

Create the top-level subvolumes:

# btrfs subvolume create @

# for i in {snapshots,root,srv,usr,usr/local,swap,var,opt}; do btrfs subvolume create @$i; done

# for i in {tmp,spool,log,cache,crash,opt}; do btrfs subvolume create @var/$i; done

The effect of this is we get a flat layout like this:

Btrfs layout
Subvolume Moint point Contains Reason
@ / /etc, /var, /usr, /boot Include in snapshots
@snapshots /.snapshots Snapshots of / Avoid rollbacks
@root /root Root user files
@srv /srv Server files
@swap /swap Swap files
@usr/local /usr/local Locally installed software
@opt /opt Third-party software
@var/log /var/log System logs
@var/opt /var/opt Third-party data
@var/tmp /var/tmp Persistent temporary data Exclude from snapshots
@var/spool /var/spool Spool data
@var/cache /var/cache Cached data
@var/crash /var/crash Crash data

You may consider adding other paths to the @var subvolume, such as lib/libvirt/images (libvirt), lib/docker (Docker), or lib/flatpak (Flatpak):

Other potential subvolumes
Subvolume Moint point Contains Reason
@var/lib/libvirt/images /var/lib/libvirt/images Virtual machine images Avoid rollbacks
@var/lib/docker /var/lib/docker Docker containers
@var/lib/flatpak /var/lib/flatpak Flatpak apps

Finally, unmount the root container:

# cd ~
# umount /mnt

Mount the file systems

Mount the Btrfs subvolumes

Mount root subvolume:

# mount /dev/mapper/root /mnt -o subvol=@,compress-force=zstd,noatime

Mount snapshots subvolume:

# mount --mkdir /dev/mapper/root /mnt/.snapshots/ -o subvol=@snapshots,compress-force=zstd,noatime

Mount other top level subvolumes:

# for i in {root,srv,swap,usr/local,opt}; do mount --mkdir /dev/mapper/root /mnt/$i -o subvol=@$i,compress-force=zstd,noatime; done

Mount var subvolumes:

# for i in {tmp,spool,log,cache,crash,opt}; do mount --mkdir /dev/mapper/root /mnt/var/$i -o subvol=@var/$i,compress-force=zstd,noatime; done

For certain directories, you should disable copy-on-write:

# chattr +C /mnt/swap
# chattr +C /mnt/var/lib/libvirt/images

Mount ESP and /home

Mount the ESP:

# mount --mkdir LABEL=ESP /mnt/efi

Mount the home partition:

# mount --mkdir LABEL=Home /mnt/home -o compress-force=zstd,noatime

Installation

Install essential packages

Install the packages necessary to get this setup working:

# pacstrap -K /mnt base linux linux-firmware nano cryptsetup btrfs-progs efibootmgr amd-ucode

Replace linux, nano, and amd-ucode with your preferred kernel, text editor, and microcode, respectively.

Configuration

Fstab

Generate an fstab file:

# genfstab -t PARTUUID /mnt >> /mnt/etc/fstab

In the generated fstab file, remove the subvolid=id options from every btrfs subvolume mount point. Additionally, remove the subvol=@ option from the subvolume mounted at / so that it mounts the default subvolume set by snapper instead. Optionally, you may comment out or delete the entries for the ESP and home partition since systemd will automount them. However, keeping the home partition entry may be helpful for e.g. setting compression or permissions options.

Tip: sed can be used to remove all of the subvolid=id options at once:
# sed -i 's/subvolid=[0-9]*,//g' /mnt/etc/fstab

subvol=@ must still be explicitly removed from the / mount point entry.

Chroot

Change root into the new system:

# arch-chroot /mnt

General configuration

Resume following the installation guide and stop before configuring the initramfs.

Initramfs

We will configure mkinitcpio to create unified kernel images.

First, edit the mkinicpio configuration to use a systemd based startup:

/etc/mkinitcpio.conf
...
BINARIES=(/usr/bin/btrfs)
...
HOOKS=(base systemd autodetect modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)
...

If desired, kernel command line arguments can be added to .conf files in /etc/cmdline.d.

For example, this guide does not use a swap partition, so if you want to use zram instead, disable zswap:

/etc/cmdline.d/zswap.conf
zswap.enabled=0

Next, edit the preset file for your chosen kernel (e.g /etc/mkinitcpio.d/linux.preset for the standard Linux kernel) and make the following changes:

  • Un-comment the [PRESET]_uki= parameter for each item in PRESETS=
  • Optionally, comment out [PRESET]_image= to avoid storing a redundant initramfs-*.img file
  • Optionally, un-comment or add the --splash parameter to each [PRESET]_options= line for which you want to add a splash image

You can then remove the initramfs files from /boot:

# rm /boot/initramfs-*.img

Root password

Set the root password:

# passwd

Boot loader

efibootmgr can be used to add UKIs directly the UEFI menu:

# efibootmgr --create --disk /dev/disk --part partnum --label "Arch Linux" --loader 'EFI\Linux\arch-linux.efi' --unicode

However, it may be advantageous to use systemd-boot instead for automatic detection of UKIs and an interactive boot menu. The downside is there is one more EFI binary to sign for secure boot, potentially increasing attack surface. It is worth noting, however, that unlike GRUB, systemd-boot will only boot other secure boot signed EFI binaries.

To install systemd-boot:

# bootctl install

Finally, generate the UKIs with mkinitcpio:

# mkinitcpio -P

Snapper

The Btrfs layout we set up earlier facilitates easy recovery of root when used in conjunction with Snapper.

First, install relevant snapper packages:

# pacman -S snapper snap-pac

Set up a snapper configuration for the root file system:

# umount /.snapshots/
# rmdir /.snapshots/
# snapper --no-dbus -c root create-config /
# btrfs subvolume delete /.snapshots/
# mkdir /.snapshots/
# chmod 750 /.snapshots/
# mount /.snapshots/

Next, enable the various snapper timers:

# systemctl enable /lib/systemd/system/snapper-*

Finally, make sure to set the default subvolume so that root will properly mount

Find out the id of the @ subvolume (likely 256 since we created it first):

# btrfs subvolume list -p /

Set it as the default:

# btrfs subvolume set-default @_subvolid /

Reboot

Exit the chroot environment by typing exit or pressing Ctrl+d.

Unmount the mounted partitions and close the cryptsetup container:

# umount -R /mnt
# cryptsetup close root

Finally, reboot into UEFI in preparation to set up secure boot:

# systemctl reboot --firmware-setup

Post-installation

Secure boot

Setup

In the UEFI, go into the secure boot settings and enter setup mode. See Unified Extensible Firmware Interface/Secure Boot#Putting firmware in "Setup Mode" for how to do so. After secure boot setup mode is enabled, continue into the new system.

Install sbctl, a tool for managing secure boot:

# pacman -S sbctl

Check secure boot status:

# sbctl status

You should see that setup mode is enabled, and secure boot is disabled.

Creating and enrolling keys

Create custom secure boot keys:

# sbctl create-keys

Enroll your keys and Microsoft's to the UEFI:

# sbctl enroll-keys -m
Warning: The reason we enroll Microsoft's keys is because some option ROMs for expansion cards may not work otherwise, potentially leaving the UEFI inaccessible without an integrated GPU. If you are absolutely certain your GPU does not need this (see [1]) or you have an integrated GPU that you can use to troubleshoot, you may leave out -m.

Signing

Check which files need to be signed:

# sbctl verify

This will include the UKIs and systemd-boot if you installed it. Sign each file with sbctl sign -s /path/to/file.

Finally, reboot into the UEFI again and turn on secure boot (it may have been turned on automatically when the new keys were enrolled). One final sbctl status should verify if secure boot is working.

sbctl includes a pacman hook to automatically sign new UKIs when they are generated, however it also generates the UKIs which we do not want.

Copy the hook to the local configuration:

# cp /usr/share/libalpm/hooks/zz-sbctl.hook /etc/pacman.d/hooks/

Edit the hook and remove -g from the Exec = line. This will let mkinitcpio handle UKI generation, as the sbctl documentation recommends. [2]

When manually generating UKIs, make sure to sign them before use. Alternatively, you can use a mkinitcpio post hook to automatically sign any new UKIs generated:

/etc/initcpio/post/uki-sign
#!/usr/bin/env bash
sbctl sign -s "$3"

Make the file executable:

# chmod +x /etc/initcpio/post/uki-sign

TPM Enrollment

Warning: Make sure to do this after verifying secure boot is enabled. Otherwise, it will not work and you'll have to fix it using the recovery key.

The unlocking of the root partition can be keyed to the TPM, potentially adding security and bypassing the need for entering the decryption password, if so desired.

Generate a recovery key:

# systemd-cryptenroll /dev/disk/by-partlabel/CRYPTROOT --recovery-key

Enroll the TPM:

# systemd-cryptenroll /dev/disk/by-partlabel/CRYPTROOT --wipe-slot=empty --tpm2-device=auto

You can use the --tpm2-with-pin=yes option if you wish to retain a passphrase needed for unlocking.

Users

Normal users (i.e. non-root and non-system) will be managed with systemd-homed.

First, enable and start systemd-homed:

# systemctl enable --now systemd-homed

To create a user in the wheel group (for use with sudo, etc.):

# homectl create --storage=luks --member-of=wheel user

homectl will prompt for a password for the new user and create the user.home loop file in /home. The benefit of using systemd-homed for users is that no user, not even root, can access a user's home directory while they are logged out without knowing that user's password. Additionally, users can be moved between systems in certain cases, adding a degree of portability.

Other configuration

See General recommendations for other post-installation setup. Particularly, one may be interested in Sudo, Security, and Zram or Btrfs#Swap file.