Btrfs on LUKS, separate EFI partition on /efi, GNOME, systemd-boot, TPM2
Before you proceed, you're recommended to refer to https://github.com/Bigstool/i-use-arch-btw/blob/main/installation-guides.md first. Compare and choose a path or a combination of paths suitable for your use case.
Boot the Arch Linux installation image, then configure the console keyboard layout with:
loadkeys usAvailable keys can be listed with:
localectl list-keymapscat /sys/firmware/efi/fw_platform_sizeYou are in UEFI if the returned value is 64 or 32.
ping -c 5 archlinux.orgCheck that NTP service is active:
timedatectlThe partition layout being used is as follows:
+-----------------------+---------------------------------+
| EFI system partition | LUKS encrypted root partition |
| | |
| | |
| /efi | / |
| | |
| | /dev/mapper/cryptroot |
| |---------------------------------|
| /dev/sdx1 | /dev/sdx2 |
+-----------------------+---------------------------------+
Find the right disk with:
fdisk -lPartition the disk using /dev/sdx as example:
fdisk /dev/sdxCreate a new GPT partition table:
Command (m for help): g
# Expected output
Created a new GPT disklabel (GUID: C1BCB28A-66E7-4DE9-89CC-B808E30243B3).
Create the EFI system partition, allocating 1G of space:
Command (m for help): n
Partition number (1-128, default 1): <ENTER>
First sector (2048-468862094, default 2048): <ENTER>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-468862094, default 468860927): +1G
# Expected output
Created a new partition 1 of type 'Linux filesystem' and of size 1 GiB.
Change the partition type to "EFI System":
Command (m for help): t
Selected partition 1
Partition type or alias (type L to list all): 1
# Expected output
Changed type of partition 'Linux filesystem' to 'EFI System'.
Create the Linux root partition, allocating all remaining space:
Command (m for help): n
Partition number (2-128, default 2): <ENTER>
First sector (2099200-468862094, default 2099200): <ENTER>
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2099200-468862094, default 468860927): <ENTER>
# Expected output
Created a new partition 2 of type 'Linux filesystem' and of size 222.6 GiB.
Change the partition type to "Linux root (x86_64)"
Command (m for help): t
Partition number (1,2, default 2): <ENTER>
Partition type or alias (type L to list all): 23
# Expected output
Changed type of partition 'Linux filesystem' to 'Linux root (x86-64)'.
Check the provisional partitions:
Command (m for help): p
Save the changes, or discard if you want to start over:
# To save
Command (m for help): w
# To discard
Command (m for help): q
For the EFI system partition:
mkfs.fat -F 32 /dev/sdx1For the Linux root partition, create a LUKS volume with a strong password:
cryptsetup luksFormat /dev/sdx2
cryptsetup open /dev/sdx2 cryptrootThe volume will be available at /dev/mapper/cryptroot. Create the Btrfs file system with:
mkfs.btrfs /dev/mapper/cryptrootMount the file system to /mnt:
mount /dev/mapper/cryptroot /mntCreate the subvolumes as desired (refer to https://wiki.archlinux.org/title/Snapper#Suggested_filesystem_layout for other layouts):
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@log
btrfs subvolume create /mnt/@cache
btrfs subvolume create /mnt/@tmp
btrfs subvolume create /mnt/@snapshotsTip
To list the created subvolumes, use btrfs subvolume list -t /mnt.
Unmount:
umount /mntMount the root:
mount -o compress=zstd,subvol=@ /dev/mapper/cryptroot /mntCreate the directories to mount to:
mkdir -p /mnt/{home,var/log,var/cache,var/tmp,.snapshots,efi}Mount the rest:
mount -o compress=zstd,subvol=@home /dev/mapper/cryptroot /mnt/home
mount -o compress=zstd,subvol=@log /dev/mapper/cryptroot /mnt/var/log
mount -o compress=zstd,subvol=@cache /dev/mapper/cryptroot /mnt/var/cache
mount -o compress=zstd,subvol=@tmp /dev/mapper/cryptroot /mnt/var/tmp
mount -o compress=zstd,subvol=@snapshots /dev/mapper/cryptroot /mnt/.snapshots
mount /dev/sdx1 /mnt/efiRefer to Select the mirrors to configure pacman mirrors.
Install base packages (for more details, refer to Packages):
pacstrap -K /mnt base base-devel linux-lts linux-lts-headers linux-firmware btrfs-progs dosfstools mtools cryptsetup sudo man-db vim nano cronie networkmanager reflector openssh gitGenerate Fstab config w.r.t. how the file systems and subvolumes are mounted now:
genfstab -U /mnt >> /mnt/etc/fstabarch-chroot /mntTip
Take a look at the packages below and make adjustments according to preference and hardware. For example, Intel users should install intel-ucode instead of amd-ucode. For more details, refer to https://github.com/Bigstool/i-use-arch-btw/blob/main/packages.md.
pacman -Syu amd-ucode bluez fuse2 zsh pipewire pipewire-alsa pipewire-pulse pipewire-jack wireplumber rsync noto-fonts-cjk noto-fonts gnome gnome-tweaks gnome-themes-extra gdm gnome-browser-connector guake firefox ibus ibus-rime ibus-anthySet the timezone:
ln -sf /usr/share/zoneinfo/<Area>/<Location> /etc/localtimeTip
Optionally, set the system to read the RTC time in the local time zone. This is useful when Windows will be run on the same computer:
timedatectl set-local-rtc 1Sync to the hardware clock:
hwclock --systohcUncomment the locales that you wish to generate:
nano /etc/locale.genGenerate the selected locales by:
locale-genSet the system language in /etc/locale.conf:
LANG=en_US.UTF-8Set the keymap in /etc/vconsole.conf:
KEYMAP=usPut the hostname in the first line of /etc/hostname. For example, if the desired hostname is arch:
archEdit /etc/hosts to include the hostname:
127.0.0.1 localhost
::1 localhost
127.0.1.1 archCreate a new user in the administration group:
useradd -mG wheel bigstoolCreate a password for the new user:
passwd bigstoolGrant sudo privilege to the new user:
EDITOR=nano visudoAdd the following line under # User privilege specification:
bigstool ALL=(ALL) NOPASSWD: ALLVerify that the privilege has been granted:
# As root
su - bigstool
# As the new user
sudo echo hello # Should print "hello" to the console
exitBuild a working systemd based initramfs by modifying the HOOKS of /etc/mkinitcpio.conf:
HOOKS=(base *systemd* autodetect microcode modconf kms *keyboard* *sd-vconsole* block *sd-encrypt* filesystems fsck)Pay attention to the items surrounded with *asterisks* and add them without the asterisks.
Find the UUID of the encrypted /dev/sdx2 partition with:
blkidThe output should include something like this:
/dev/sdx2: UUID="06b34979-42f7-4033-95bd-6587b49191a0" TYPE="crypto_LUKS" PARTUUID="642251ea-aa7c-4a7c-ba49-3e41ccebeb3e"
The UUID (not the PARTUUID) is what to look for. Edit the kernel command line with:
nano /etc/kernel/cmdlineAdd the following, then save and exit:
rd.luks.name=<UUID>=cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@In the example, it is:
rd.luks.name=06b34979-42f7-4033-95bd-6587b49191a0=cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@Next, edit /etc/mkinitcpio.d/linux-lts.preset (or /etc/mkinitcpio.d/linux.preset if using the linux kernel), comment out the _image fields, uncomment the _uki and _options fields. Then, change the paths of the .efi files to /efi/EFI/....
The linux-lts.preset file should now look somewhat like this:
# mkinitcpio preset file for the 'linux-lts' package
#ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux-lts"
#ALL_kerneldest="/boot/vmlinuz-linux-lts"
#PRESETS=('default')
PRESETS=('default' 'fallback')
#default_config="/etc/mkinitcpio.conf"
#default_image="/boot/initramfs-linux-lts.img"
default_uki="/efi/EFI/Linux/arch-linux-lts.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"
#fallback_config="/etc/mkinitcpio.conf"
#fallback_image="/boot/initramfs-linux-lts-fallback.img"
fallback_uki="/efi/EFI/Linux/arch-linux-lts-fallback.efi"
fallback_options="-S autodetect"If the initramfs was generated previously, remove the residual initramfs files in /boot as they are no longer relevant with UKI:
rm /boot/initramfs-linux-*Additional note: https://wiki.archlinux.org/title/Unified_kernel_image#pacman_hook
Install the systemd-boot boot loader with:
bootctl installNote
If bootctl complains about the random seed file being world accessible, edit /etc/fstab. Find the entry for /efi, and change the values for fmask and dmask to 0077. This change will take effect at First boot.
Optionally, configure the boot loader to display a menu for kernel selection at boot by editing /efi/loader/loader.conf (ref: https://wiki.archlinux.org/title/Systemd-boot#Loader_configuration, https://www.freedesktop.org/software/systemd/man/latest/loader.conf.html):
default @saved
timeout 3
console-mode max
editor noRegenerate initramfs with:
mkinitcpio -PEnable services with:
systemctl enable NetworkManager sshd cronie gdm bluetoothExit from chroot:
exitSync and unmount everything:
sync
umount -R /mntShut down:
poweroffTry booting the new system. If it works, shut down the PC.
Note
It is assumed that all shell commands from this point onward are run as the newly created user with sudo privilege.
Install sbctl:
sudo pacman -Syu sbctlReboot the PC into firmware setup. Under the secure boot settings, put secure boot into enter setup mode by deleting the Platform Key (PK). If this is not possible, delete/clear the secure boot keys. If the firmware offers an "OS type" option, choose Windows instead of other OS, as this setup will be Microsoft secure boot compatible. (ref: https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#Putting_firmware_in_%22Setup_Mode%22, https://man.archlinux.org/man/sbctl.8#USAGE)
Boot back into Arch and verify that setup mode is indeed activated with:
sbctl statusIf yes, create the custom secure boot keys:
sudo sbctl create-keysThe keys are stored in /var/lib/sbctl/keys.
Enroll the keys alongside Microsoft's keys and the OEM firmware's built-in certificates to the UEFI:
Warning
Do NOT omit the -m flag below, otherwise it might brick the device.
sudo sbctl enroll-keys -m -fTip
If the command above runs into the following error:
‼ File is immutable: /sys/firmware/efi/efivars/KEK-732f2b7e-7013-4e43-be44-d0de168a3d92
‼ File is immutable: /sys/firmware/efi/efivars/db-141c4108-7a45-4de4-929a-4b27d558b59c
You need to chattr -i files in efivarfs
Temporarily remove the immutable flag of the UEFI secure boot variables with:
sudo chattr -i /sys/firmware/efi/efivars/KEK-732f2b7e-7013-4e43-be44-d0de168a3d92
sudo chattr -i /sys/firmware/efi/efivars/db-141c4108-7a45-4de4-929a-4b27d558b59cThen, try enrolling the keys again.
Check the status again, sbctl should be installed now:
sbctl statusCheck what files need to be signed:
sudo sbctl verifySign all the unsigned files. For example:
sudo sbctl sign -s /efi/EFI/Linux/arch-linux-lts.efi
sudo sbctl sign -s /efi/EFI/Linux/arch-linux-lts-fallback.efi
sudo sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
sudo sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efiTip
If an undesired file was accidentally signed, remove the file from the signing database with:
sudo sbctl remove-file <file>For systemd-boot, sign the boot loader directly in /usr/lib as well (ref: https://wiki.archlinux.org/title/Unified_Extensible_Firmware_Interface/Secure_Boot#Automatic_signing_with_the_pacman_hook):
sudo sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efiReboot, and check that secure boot is working with:
sbctl statusOffload the UKI generation from mkinitcpio to systemd-ukify by installing the package. This step is required for the configuration of a signed PCR policy using PCR 11:
sudo pacman -Syu systemd-ukifyGenerate the keys for PCR signing:
sudo ukify genkey --pcr-private-key=/etc/systemd/tpm2-pcr-private-key.pem --pcr-public-key=/etc/systemd/tpm2-pcr-public-key.pemCreate the systemd-ukify configuration for PCR signing in /etc/kernel/uki.conf:
[PCRSignature:initrd]
Phases=enter-initrd
PCRPrivateKey=/etc/systemd/tpm2-pcr-private-key.pem
PCRPublicKey=/etc/systemd/tpm2-pcr-public-key.pemEnroll the key, replace <UUID> with the one used in Configure mkinitcpio and unified kernel image:
sudo systemd-cryptenroll /dev/disk/by-uuid/<UUID> --wipe-slot=empty --tpm2-device=auto --tpm2-pcrs=0+2+5+7+15:sha256=0000000000000000000000000000000000000000000000000000000000000000 --tpm2-public-key /etc/systemd/tpm2-pcr-public-key.pem0: Core System Firmware executable code (aka Firmware). May change if you upgrade your UEFI.
2: Extended or pluggable executable code; includes option ROMs on pluggable hardware.
5: GPT/Partition table; changes when the partitions are added, modified, or removed.
7: Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, ...) changes.
15: Root LUKS volume key, machine ID, mount points, file system UUIDs, labels, partition UUIDs; starts being all zero at boot.
--tpm2-public-key: Configures a signed PCR policy and binds to PCR 11 (Hash of the Unified kernel image) by default.
Warning
Only binding to PCRs 0-7 can introduce vulnerabilities. Refer to sources including https://wiki.archlinux.org/title/Systemd-cryptenroll#Trusted_Platform_Module, https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers, https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/, and https://man.archlinux.org/man/systemd-cryptenroll.1 for more information, then choose the combination that works for the particular setup and threat model.
Append the following to /etc/kernel/cmdline:
rd.luks.options=cryptroot:tpm2-measure-pcr=yesRegenerate initramfs:
sudo mkinitcpio -PReboot to see if the drive is automatically unlocked. The installation is complete.
To change the enrolled TPM key, wipe the existing slot first with:
sudo systemd-cryptenroll /dev/disk/by-uuid/<UUID> --wipe-slot=tpm2Then, enroll the key again with modified PCR.
Refer to https://barn.bigstool.com/post/server-config/
Refer to https://github.com/Bigstool/i-use-arch-btw/blob/main/pacman.md
Take a further look at https://github.com/Bigstool/i-use-arch-btw/blob/main/packages.md
sudo pacman -Syu snapperRef: https://wiki.archlinux.org/title/Snapper#Suggested_filesystem_layout
Unmount and remove the /.snapshots directory since Snapper assumes that /.snapshots is not mounted and does not exist as a folder:
sudo umount /.snapshots
sudo rm -r /.snapshotsCreate a new configuration for /:
sudo snapper -c root create-config /Delete the subvolume .snapshots newly created by Snapper:
sudo btrfs subvolume delete /.snapshotsRecreate the /.snapshots directory:
sudo mkdir /.snapshotsMount @snapshots to /.snapshots utilizing the existing fstab entry:
sudo mount -aVerify the mount with:
findmnt -nt btrfsGive the folder 750 permissions:
sudo chmod 750 /.snapshots/Automatic timeline snapshots is enabled by default with a cron daemon correctly set up. Edit the configurations in /etc/snapper/configs/root to liking. For example:
TIMELINE_MIN_AGE="1800"
TIMELINE_LIMIT_HOURLY="5"
TIMELINE_LIMIT_DAILY="7"
TIMELINE_LIMIT_WEEKLY="0"
TIMELINE_LIMIT_MONTHLY="0"
TIMELINE_LIMIT_YEARLY="0"Btrfs Assistant is a GUI tool that can manage Btrfs subvolumes and Snapper snapshots. Install Btrfs Assistant with:
sudo pacman -Syu btrfs-assistantThe unified kernel images are stored in the EFI system partition and will not be included in the Btrfs snapshots. In case of kernel updates, returning to a snapshot with older kernel version would draw the system unbootable (ref: https://wiki.archlinux.org/title/EFI_system_partition#Typical_mount_points). Therefore, we create a post hook in mkinitcpio to make a backup of /efi every time the unified kernel image is generated to be later restored with the snapshot.
Create the target directory of backup:
sudo mkdir /efibakCreate the following script as /etc/initcpio/post/efibackup.sh:
#!/bin/bash
echo "Backing up /efi to /efibak..."
rsync -a --delete /efi/ /efibak/
echo "Done!"Give the script permissions to execute:
sudo chmod +x /etc/initcpio/post/efibackup.shRegenerate initramfs with:
sudo mkinitcpio -PTODO: run the post hook after sbctl
Create a temporary mount point for the restored root for later restoration of /efi:
sudo mkdir /newrootIn Btrfs Assisatnt, restore the desired snapshot at Snapper > Browse/Restore.
Mount the new root to /newroot with:
sudo mount -o compress=zstd,subvol=@ /dev/mapper/cryptroot /newrootOverwrite /efi with the backup included in the snapshot:
sudo rsync -a --delete /newroot/efibak/ /efi/Unmount:
sudo umount /newrootReboot:
sudo rebootThe system should boot without any problem. After logging in, verify that / is indeed mounted with @ with:
findmnt -nt btrfsThe output should look something like this:
/ /dev/mapper/cryptroot[/@] btrfs rw,relatime,compress=zstd:3,ssd,space_cache=v2,subvolid=300,subvol=/@
Make sure that /dev/mapper/cryptroot[/@] is mounted at /.
On the source installation, make a copy of the GUID file and the keys directory at /var/lib/sbctl.
On the target installation, install sbctl:
sudo pacman -Syu sbctlCreate the secure boot keys. This sets up the /var/lib/sbctl directory on the target machine. We will overwrite the created files later:
sudo sbctl create-keysImport the keys copied from the source installation:
sudo sbctl import-keys --directory /path/to/copied/keys --forceCopy the GUID from the copied GUID file:
cat /path/to/copied/GUIDReplace the newly-generated GUID with the copied one, then save and exit:
sudo nano /var/lib/sbctl/GUIDRefer to Secure boot to finish the migration. Continue from the "Check what files need to be signed" step, skipping the key enrollment and any prior steps.
https://wiki.archlinux.org/title/Installation_guide#Mount_the_file_systems
https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#LUKS_on_a_partition
https://gist.github.com/mjkstra/96ce7a5689d753e7a6bdd92cdc169bae
https://itsfoss.com/wrong-time-dual-boot/
https://wiki.archlinux.org/title/Unified_kernel_image#mkinitcpio
https://wiki.archlinux.org/title/Unified_kernel_image#ukify
https://wiki.archlinux.org/title/Btrfs#Mounting_subvolume_as_root
ChatGPT
https://wiki.archlinux.org/title/Mkinitcpio#Post_hooks
Doubao
https://odysee.com/@daimarstein:d/arch-install-guide-tpm-secureboot:e
https://man.archlinux.org/man/extra/sbctl/sbctl.8.en
https://blastrock.github.io/posts/fde-tpm-sb-ng/
Claude
https://man.archlinux.org/man/ukify.1
https://edu4rdshl.dev/posts/uki-secure-boot-on-archlinux-systemd-boot-walkthrough/