Arch Linux Laptop Setup

PURPOSE

These are my instructions for setting up an Arch Linux laptop. I have just purchased a Lenovo ThinkPad X1 Carbon, 11th Generation laptop, which arrived after a little more than two weeks. This laptop has a 13th generation Intel EVO vPRO CPU (Core i7-1370P), 64GiB RAM, and 2TiB NVMe SSD. It will also have a TPM2 chip. I will also repeat and refine these instructions for my aged Lenovo ThinkPad 25th Anniversary Edition, which also has a TPM2 chip in it, with 32GiB RAM and a 512GiB NVMe SSD.

These instructions will set up the following:

  • encrypted root (using TPM2 chip)
    • note: EFI partition will not be encrypted
  • Unified Kernel Images (UKIs) with Secure Boot
  • A Btrfs filesystem with root and home as subvolumes of the top-level top subvolume
    • If desired, another filesystem can be used instead. Partition and format the disk(s) accordingly. I chose Btrfs for its snapshot capability (I will use snapper to manage my snapshots after the laptop is set up)
  • systemd-boot as the bootloader
  • systemd-homed for user home directory management

The intention is to document the entire installation process, using the standard Arch Linux installation instructions tailored for this purpose. The goal is to have this be repeatable.

NOTE: This document is a work in progress. Once I'm able to follow it with my new laptop, I will document any missed steps or gotchas I encounter along the way, and hopefully how I fix them.

CAVEAT LECTOR (Reader BEWARE): This guide is only ONE way to install Arch on a laptop. Do NOT blindly follow ANYTHING in this guide, mostly because if a lot of time has passed since this article was published the official Arch Installation Guide has likely evolved beyond the steps delineated here. Make sure you understand each and every command before you execute them. If there's something you don't understand, read the excellent Arch Manual Pages for the specific command, and also try to find the corresponding Arch Wiki article describing full details and alternatives (I try to link back to the Wiki several times throughout this article). Also note, this does not guarantee security of the target laptop, there may be latent, glaring deficiencies I have yet to identify. If you blindly follow these commands, and do not understand the why behind the decisions I've made (for myself, the primary intended audience of this article), you WILL NOT be supportable by the wider Arch Linux community (forums and IRC). I can offer assistance where I can, but my time is limited, and due to life I don't frequent the places Archers find help as much as I used to.

Possible Gaps in this document

  • since I haven't used UKI with systemd-boot and Secure Boot, I'm not sure if the kernel cmdline will be protected
    • I'll have to see if editing the cmdline at all before booting is possible
    • possibly look into systemd-boot-password, but this package hasn't been updated since 2018
      • this package may have been originally intended to be rolled into systemd
      • but I can't find anything on the systemd site which mentions setting a boot password
    • ANSWER With Secure Boot enabled, the kernel cmdline will not be editable with systemd-boot. So, to enter single user mode, I will likely need to disable Secure Boot (Reset to Setup Mode), boot from the Arch ISO, and perform whatever maintenance I need to do. See the notes on the Systemd-boot#Adding_loaders and UKI Booting Wiki articles which are the only places I could find this documented anywhere. Another thing: no boot menu beyond the UEFI firmware splash screen and prompts even shows with my UKI+SecureBoot setup; even though I have systemd-boot installed, it's not really used (unless the UEFI firmware doesn't find a kernel to boot).
  • I mention setting UEFI BIOS passwords, but these may be defeatable through a physical attack on ThinkPads. I'm not sure if the X1 Carbon or 25th Anniversary (20K7) are susceptible to this, but it's something to keep in mind.
    • The Hardware Maintenance Manuals (HMMs, can't copy direct links to them from the Lenovo site) for both of my ThinkPads state the following,"Attention: If the supervisor password has been forgotten and cannot be made available to the service technician, there is no service procedure to reset the password. The system board must be replaced for a scheduled fee."
      • so, not even a depot technician can reset the password, suggesting that a simple screwdriver used to short a couple of pins won't work.
  • I read about a new attack called LogoFAIL, a particularly nasty image-processing vulnerability that could allow an attacker to install a maliciously crafted UEFI/BIOS logo image to the ESP to bypass the Secure Boot protections and execute arbitrary code.
    • I can mitigate this to an extent by setting a UEFI BIOS password that needs to be entered in order to change the boot device (in order to boot off of USB or other medium)
      • But this doesn't prevent a physical attacker removing the SSD, installing the malicious crafted image, and putting it back in my system
      • I'll have to look into whether my X1 Carbon has a way to determine if the chassis has been opened, and not boot if it has (have to enter the UEFI supervisor password)
        • Oh, beautiful! I used the Lenovo UEFI Simulator (see below) to determine there is a feature that the laptop can determine if the chassis has been tampered with, and require the supervisor password to boot if it has. Neat!
          • I think this is an acceptable mitigation
    • Lenovo released a firmware update, and included both my 25th Anniversary ThinkPad/20K7, and the new X1 Carbon.
      • I was able to apply the new UEFI firmware with fwupd to my ThinkPad 25/20K7
      • I suspect the process will be nearly identical for the X1 Carbon
        • I'll be putting instructions for fwupd below, after first boot
  • These instructions assume the system administrator only wants to install Arch Linux as the only operating system. If you intend to dual boot into another operating system (e.g. Windows, another Linux distribution, etc.), these instructions must be carefully adapted for that purpose. Especially with Windows, great care must be taken to avoid Windows making other operating systems unbootable, an exercise I am not interested in going through.

PREREQUISITES

  • a laptop containing:
    • Unified Extensible Firmware Interface Basic Input/Output System (UEFI BIOS, required for UKI)
    • x86_64/amd64 CPU (Arch only supports this architecture)
    • 1GiB RAM (necessary for installation, though Arch can run with 512MiB RAM)
    • NVMe standard v1.4+ M.2 SSD (for NVMe Sanitize)
    • TPM2.0+ cryptoprocessor (optional security chip, but highly recommended)
  • latest Arch Linux ISO loaded onto bootable medium (USB flash drive, PXE boot, DVD/BD, etc.)
  • latest memtest86+, also loaded onto bootable medium (optional, for testing system RAM)

PROCEDURE

!!WARNING!! These instructions will DESTROY ALL DATA on the target laptop, including the partition table and EFI partition, and any previous operating systems. Be sure your backups are sound AND TESTED prior to proceeding.

Preparation

  1. Power on the system, and enter the UEFI setup menu, to enable Secure Boot Setup Mode for the installation. Usually the firmware splash screen will give a hint as to how to do this. On my ThinkPad 25th Anniversary Edition I press Enter to interrupt the boot sequence, and then F1 to enter the Setup Menu (this is the same for the ThinkPad X1 Carbon, press Enter then F1). Lenovo has a neat UEFI BIOS simulator, but my ThinkPad 25th Anniversay Edition (20K7) is not listed, probably because it's now too old to be covered by this tool (I bought it new in Q4 2017). To enable Secure Boot Setup Mode, at least on the X1 Carbon 11gen, navigate to Security/Secure Boot/Reset to Setup Mode. This actually disables Secure Boot (at least on this ThinkPad), which should allow booting the memtest86+ and Arch ISO images (see below). See your laptop's UEFI BIOS manual for details on how to do this (if you don't have a ThinkPad).
  2. Verify the memtest86+ image, and load it onto a bootable medium (e.g., USB flash drive, CD/DVD/BD, PXE boot, etc.).
  3. OPTIONAL: Boot memtest86+. Your laptop may have a separate menu to choose the boot device, or you may need to go into the UEFI Setup as disabling Secure Boot above in order to boot off the removable medium. Once memtest86+ boots, it will pause awaiting input. This is where I recommend enabling multiple CPUs (SMP mode); despite what the memtest86+ README states, it's in SMP mode by default (press F2 at the beginning to disable). It will then automatically start testing the system RAM after a few moments. memtest86+ will then run indefinitely, repeatedly testing RAM with each pass. Let a complete test finish; I recommend multiple passes (the last time I did this I let four full passes complete). The length of time this takes will vary depending on the size and speed of system RAM, CPU, and the number of CPU cores. Back when I did this in 2020 on my ThinkPad 25th Anniversary Edition (using the proprietary, non-open source version of memtest), it took nearly four days to execute a complete four-pass test on 32GiB RAM (I don't recall enabling SMP mode back then). With my new X1 Carbon, it took just over eight hours to make three test passes (passes 0-2), and it finished the fourth pass (pass 3) in just over 11 hours total. Not bad!
  4. Verify the Arch Linux ISO, and load it onto a bootable medium.
  5. REQUIRED: Boot the Arch Linux ISO, and run the nvme sanitize operation once booted:
    # lsblk -f # determine device name for NVMe disk
    # nvme sanitize /dev/nvme0 --sanact=0x02  # change device accordingly
    
    NOTE: we use the NVMe character device (i.e. nvme0) rather than the block device (nvme0n1), since we're applying the sanitize step to the entire device.
    This command will return immediately. To view the progress, issue the following command:
    # nvme sanitize-log /dev/nvme0
    
    The Sanitize Status (SPROG) is an unsigned 16-bit decimal integer (maximum value is 65535, which means complete). The Sanitize Status (SSTAT) has a specific hexadecimal code, see nvme-sanitize-log(1) for details on what the different codes mean. When the sanitize was complete on my ThinkPad X1 Carbon, the code was 0x101, since the non-volatile storage has not been written to, and the most recent sanitize operation completed successfully. The Arch Wiki article regarding Sanitize is a little confusing, I missed the part where it said that the Estimated Time for Block Erase was set to 60, meaning it was estimated to take up to 60 seconds to complete. For me, this operation was quick (only taking a few seconds). If the estimated time for block erase is set to 4294967295, the time is indeterminate and could take several hours based on the size of your NVMe disk. However, lsblk -f still shows the existing partition table (Lenovo did not give me the option of purchasing this laptop with Linux or without an operating system, so it came with Windows 11 Home as that was the no extra cost option). This is likely due to no overwrite, which is recommended to be avoided to preserve write endurance. Actually, I forgot to run partprobe, which would have loaded the new (empty) partition table into kernel memory, and I wouldn't have seen the existing Windows 11 Home partitions at all.



    If you're using an older NVMe SSD (standard predating 1.4), you'll have to use the nvme format command. The drawback to this is the caches on the controller interface and on the SSD itself will likely not be cleared, which could potentially mean data on disk is recoverable. Not ideal if the drive is to be encrypted (as this disk will be). The chances of recovering data from the encrypted partitions are low, but if your security paranoia is high enough it should be concerning. See the Arch Wiki for full details.

PROCEDURE

Once the preparation steps are complete, continue with the installation.

Initialization

  1. If necessary, reboot into the Arch Linux ISO via bootable medium.

  2. Set the console font:

    # setfont ter-132b
    

    At this point you can also set the keyboard map, if your keyboard layout is not the US keyboard layout. See the Arch Wiki for details if you need to do this.

  3. Verify the EFI boot mode:

    # cat /sys/firmware/efi/fw_platform_size
    

    This should return 64 or 32 if you're in UEFI mode. If this file does not exist, you have a legacy BIOS system (or your UEFI system is in legacy BIOS mode) and these instructions are not appropriate for your laptop. See the Arch Wiki for details.

  4. Connect to the Internet. I will be using WiFi for this, as my X1 Carbon does not have an Ethernet port.

    1. First, verify the WiFi adapter is loaded and functional:

      # ip link
      

      Interface name should be a variation on wlan0 or wlpNs0 (e.g., wlp4s0), the 'N' will be different based on the order it appears on the PCI bus.

    2. Ensure the wireless adapters are not blocked using the rfkill command with no options:

      # rfkill
      

      It should at least return that the wlpNs0 or wlan0 device is unblocked by the kernel or hardware switch. See the rfkill caveat of the Wireless article on the Arch Wiki.

    3. Use iwctl to connect to the WiFi network. This should obtain an IP address from DHCP automatically, depending on the network.

      1. List the wireless devices:
        # iwctl device list
        
      2. If the device or its corresponding adapter is turned off, power it on:
        # iwctl device <device> set-property Powered on
        # iwctl adapter <adapter> set-property Powered on
        
      3. Scan for networks and list the available networks:
        # iwctl station <device> scan # command produces no output
        # iwctl station <device> get-networks
        
      4. Connect to the desired network:
        # iwctl station <device> connect <SSID>
        
        You should be prompted for the network password.
    4. Test that the network is functional:

      # ping archlinux.org
      
    5. At this point, consider logging into the new Arch Linux system from another computer, if only to load these instructions in a graphical web browser so you can copy and paste commands into the terminal emulator while logged into the laptop we're working on.
      Before we start sshd, we need to allow root to login with a password. Edit /etc/ssh/sshd_config and add the following line (the default is prohibit-password but it's commented out; place this line just below that one):

      PermitRootLogin yes
      

      Then run passwd to set a temporary root password (it need not be the same root password for the installed system). Note this is only for the live environment booted from the Arch ISO; I would not recommend enabling this in the installed system.
      Start sshd and determine the current IP address of this laptop (look at the wlan0 or wlpNs0 interface):

      # systemctl start sshd
      # ip a s
      

      Then, on the other computer on the same network:

      $ ssh root@<ip_addr>
      
  5. Verify the system time is correct and synchronized:

    # timedatectl
    

    Note, the timezone might not be set, you can optionally set it with:

    # timedatectl set-timezone 'America/New_York'
    

    If the RTC is in the local timezone (RTC in local TZ: yes, the default in Windows for the longest time; it seems at least in Windows 11 Home this is no longer the case, thank God), set it to no:

    # timedatectl set-local-rtc no
    
  6. Ensure the NVMe SSD is using the optimal logical sector size.

    1. Check to see what this is set to:
      # nvme id-ns -H /dev/nvme0n1 | grep "Relative Performance" # change to proper NVMe disk device, as seen with lsblk -f
      
      It should display which one is in use. As the Arch Wiki states, choose the best logical block size with zero Metadata (the Relative Performance output will show degraded, good, better, best, or some subset thereof). My X1 Carbon only showed logical block sizes of 512 (good) or 4096 (better), so I chose the better option.
    2. If the logical block size needs to be changed, change it to the LBA Format value which is optimal.
      # nvme format --lbaf=1 /dev/nvme0n1
      
      This format should be quick, not as long as the sanitize step above.
  7. Partition the disks. Be sure to create a GPT partition table (GPT is part of the UEFI specification). We'll create two partitions, one small (512MiB) EFI partition which will be mounted at /efi, the Arch Wiki refers to this as the esp. The other will be a Linux partition, taking up the rest of the disk. You can optionally create a separate swap partition, which is what I'm most familiar with. However, such a swap partition would need to be encrypted separately, which could complicate the process of decrypting on boot.

    I will use parted to partition the disks, as it is the most scriptable. Alternatively you can use fdisk or gdisk to use an interactive, menu-driven TUI (Text User Interface) to partition the disk. GNU parted also has its own interactive shell, but for brevity I will just be using rote commands without entering the interactive shell.

    Create an empty partition table (substitute nvme0n1 with the actual device name). Add both a fat32 partition (labeled esp) giving it the esp attribute, and create the enc partition, giving it the type btrfs and letting it take the rest of the disk. I also use the --align optimal with percentages for the starting and ending sectors. Otherwise parted will complain about the partitions not being aligned (which would degrade performance).

    # parted --list # confirm partition tables on all block devices
    # parted --script --align optimal /dev/nvme0n1 mklabel gpt -- \
        mkpart esp fat32 0% 512MiB \
        set 1 esp on \
        mkpart enc btrfs 512MiB 100%
    

    Be sure to load the new partition table into memory, run the following command:

    # partprobe
    
  8. Create the vfat filesystem on the esp, /dev/nvme0n1p1:

    # mkfs.fat -F 32 /dev/nvme0n1p1
    
  9. Create the top-level Btrfs filesystem, on an encrypted device. Follow the instructions on the Arch Wiki. Set an empty passphrase for now; after first boot we will be setting a recovery key, and use the TPM2 cryptoprocessor in conjunction with Secure Boot to unlock the system as long as the disk isn't tampered with.

    1. Create the LUKS device, and create a top-level Btrfs filesystem:
      # cryptsetup luksFormat /dev/nvme0n1p2  # empty passphrase!
      # cryptsetup open /dev/nvme0n1p2 top
      # mkfs.btrfs -L top /dev/mapper/top
      
    2. Create the root, and home Btrfs subvolumes. First, mount the top filesystem, then create the subvolumes, and finally unmount the top filesystem:
      # mount /dev/mapper/top /mnt
      # btrfs subvolume create /mnt/root
      # btrfs subvolume create /mnt/root/home
      # umount /mnt
      
  10. Mount the root subvolume, create the home directory, and mount it:

    # lsblk -f  # get the UUID of the Btrfs filesystem
    # mount -t btrfs -o compress-force=zstd:5,subvol=/root UUID=<UUID> /mnt
    # mount -t btrfs -o compress-force=zstd:5,subvol=/root/home --mkdir UUID=<UUID> /mnt/home
    # chattr +C /mnt/home/
    

    Due to limitations in the Linux VFS framework, it is not currently possible to have different mount options for different Btrfs subvolumes (see note at link); setting compression implies CoW (Copy on Write), and no subsequently mounted subvolume can have nodatacow enabled. Instead we enable nodatacow on the /mnt/home/ directory, so any new systemd-homed image (a LUKS2 container) will have the nodatacow attribute. Each of these systemd-homed LUKS2 images will have a Btrfs filesystem within it, and hopefully these internal Btrfs filesystems will have CoW set up. It turns out you can have subvolumes on these systemd-homed Btrfs filesystems. It takes a bit of a hack to get them mounted and owned by the systemd-homed user; this is where having passwordless sudo comes in handy.
    Optionally create other subvolumes below /root/, like /root/etc or /root/var (or even /root/root, the root user's home directory). Btrfs subvolumes are separate namespaces, and snapshotting a parent subvolume does not include any subvolumes below it. You can think of them logically as separate partitions or filesystems, though hard links can be cross-subvolume (unlike with separate filesystems). Also, consider setting the nodatacow attribute on relevant mount points (like /mnt/var, before entering the arch-chroot).
    After I ran into issues with my first boot attempt on my X1 Carbon, I realized that having /etc in a separate subvolume is a bad idea. The systemd-gpt-auto detector doesn't yet seem to be able to read Btrfs subvolumes, and if the etc subvolume isn't mounted, the system can't read /etc/fstab. But this wasn't the only problem: my first boot attempt had no way to identify and unlock the LUKS2 container in the second GPT partition (more on that below).

  11. Create the swap subvolume and swap file, and enable it:

    # btrfs subvolume create /mnt/swap
    # mount -t btrfs -o subvol=/root/swap --mkdir UUID=<UUID> /mnt/swap
    # btrfs filesystem mkswapfile --size 16g --uuid clear /mnt/swap/swapfile
    # swapon /mnt/swap/swapfile
    
  12. Make the /mnt/efi directory, and mount the esp to it:

    # mount --mkdir /dev/nvme0n1p1 /mnt/efi
    
  13. Synchronize the mirror list with reflector:

    # reflector --protocol rsync,https,http \
        --country US \
        --sort rate \
        --latest 10 \
        --fastest 10 \
        --age 12 \
        --save /etc/pacman.d/mirrorlist
    
  14. Copy the base system and any packages you want installed with pacstrap. Note, we are not installing a kernel with pacstrap, as the Arch Linux Installation Guide recommends, since we'll be installing and signing a Unified Kernel Image (UKI) in the section below. Most of these packages are optional at this point; we will be installing more once the installation is complete.

    # pacstrap -K /mnt base \
        base-devel \
        sudo \
        btrfs-progs \
        vim \
        networkmanager \
        man-db \
        man-pages \
        texinfo \
        git \
        tmux \
        openssh \
        sbctl \
        efitools \
        zsh \
        mkinitcpio \
        systemd-ukify \
        intel-ucode
    

    Note: if your laptop has an AMD CPU install amd-ucode instead. And normally mkinitcpio is a dependency of the linux package, so if we don't include it with pacstrap it won't already be installed (it's not part of base).

  15. Generate /mnt/etc/fstab:

    # genfstab -U /mnt >> /mnt/etc/fstab
    # vim /mnt/etc/fstab # ensure all entries are correct
    
  16. Get the UUID and Btrfs subvolume ID of the root partition, for later reference:

    # echo "rd.luks.name=$(blkid | grep crypto_LUKS | grep -Po '\bUUID=".*?"' | sed 's/UUID=//' | tr -d '"')=top" | tee -a /mnt/etc/kernel/cmdline  # get LUKS UUID and copy it to /etc/kernel/cmdline in new system
    # echo "root=$(blkid | grep /dev/mapper/top | grep -Po '\bUUID=".*?"')" | tee -a /mnt/kernel/cmdline
    # btrfs subvolume list -p /mnt  # get subvolid of root
    

Configure the System

  1. Change root into the new system:
    # arch-chroot /mnt
    
  2. Set the timezone:
    # ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime
    
  3. Run hwclock(8) to generate /etc/adjtime:
    # hwclock --systohc
    
  4. Edit /etc/locale.gen and uncomment en_US.UTF-8 UTF-8 and other needed UTF-8 locales. Then generate locales:
    # locale-gen
    
  5. Create /etc/locale.conf:
    # echo 'LANG=en_US.UTF-8' | tee /etc/locale.conf
    
  6. Set the console keyboard layout, and make it persistent in /etc/vconsole.conf (if you set a different keyboard layout earlier, be sure to set the proper layout):
    KEYMAP=us
    
  7. Create /etc/hostname:
    fluorine
    

Unified Kernel Image Setup

  1. Configure /etc/mkinitcpio.conf. Ensure it has the following in the HOOKS= line:

    HOOKS=(systemd autodetect modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)
    
  2. Edit /etc/kernel/cmdline, and set the following (this is where knowing the UUIDs come into play):

    rd.luks.name=<LUKS partition UUID>=top root=UUID=<Btrfs top-level UUID> rw rootflags=subvol=/root
    

    When I attempted my first boot, I did not have the rd.luks.name parameter with the LUKS container partition UUID (this UUID is visible from the lsblk -f output). Without this parameter, the UKI cannot find the root subvolume, and the kernel doesn't even know to unlock the LUKS container. Please see the LUKS#Kernel parameters section of the Arch Wiki for a full discussion of this. Since this is the only LUKS container I intend to install and this UUID should persist for the life of this installation (except for the LUKS containers created by systemd-homed), I did not bother to set up /etc/crypttab and related files.

  3. Set up the kernel to be installed by systemd's kernel-install. There are a few options. If you don't specify the uki_generator parameter in /etc/kernel/install.conf, it defaults to systemd's ukify which as of 2024-01-15 will only work if systemd-ukify is installed (so I've added it to the pacstrap command above). Alternatively you can add uki_generator=mkinitcpio to /etc/kernel/install.conf to have mkinitcpio generate the UKI (it implicitly did this if systemd-ukify was not installed prior to 2024-01-15). See UKI#kernel-install and the Kernel-install articles on the Arch Wiki for more details:

    # echo "layout=uki" >> /etc/kernel/install.conf
    
  4. Mask the mkinitcpio pacman hooks:

    # mkdir -p /etc/pacman.d/hooks # doesn't exist already
    # ln -sf /dev/null /etc/pacman.d/hooks/60-mkinitcpio-remove.hook
    # ln -sf /dev/null /etc/pacman.d/hooks/90-mkinitcpio-install.hook
    
  5. Next we need to install pacman-hook-kernel-install. But first, we need to create an unprivileged user so we can run makepkg (Arch forbids root from running makepkg as PKBUILDs from the AUR are untested and could irreparably harm the installed system). We also give the wheel group sudo access; for convenience I allow sudo access without prompting for a password. See the sudoers(5) manual page if you'd rather have any unprivileged user in the wheel group submit their password to run sudo. We'll create a regular user later in the systemd-homed section. Create the user:

    # useradd -G wheel,adm,disk -m admin
    # echo "%wheel ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/wheel
    # sudo -u admin -i
    

    My initial plan was to install my AUR-helper pikaur, but it won't run in the arch-chroot (due to systemd not being PID 1).

    $ cd
    $ git clone https://aur.archlinux.org/pacman-hook-kernel-install.git
    $ cd pacman-hook-kernel-install
    $ makepkg -firs
    $ logout
    
  6. Set up Secure Boot with sbctl. sbctl is not compatible with all systems. If it reports any errors, you may need to follow the manual process instead.
    a. First, create the keys:

    # sbctl create-keys
    

    b. Enroll the keys, being sure to enable the Microsft vendor certificates:

    # sbctl enroll-keys --microsoft
    

    At least on my X1 Carbon, entering Setup Mode clears the Platform key (there's a stern warning about it prompting you to confirm before entering Setup Mode, disabling Secure Boot). And the UEFI BIOS on this X1 Carbon has no way to enable Secure Boot after this (likely because the OS needs to install the Platform key). Contrary to what the sbctl maintainer said, the only way to reenable Secure Boot is with sbctl enroll-keys. If you do not have a ThinkPad, it's possible that both enroll-keys and enabling Secure Boot in the UEFI firmware menu are required; the latter step was not required for my X1 Carbon. We'll see if my 25/20K7 has a similar "feature."
    Not passing the --microsoft or -m flag could be dangerous! Without this flag, the Microsoft certificates will be removed. If your laptop uses an Option ROM that is signed by Microsoft or their third-party UEFI CA (Certificate Authority), not passing this flag could brick your laptop! Ensure you have a way to restore Secure Boot to factory defaults and keys if you don't want to heed this warning.
    When I ran this, it listed two files in /sys/firmware/efi/efivars/ as immutable, so it said change them with chattr -i:

    # chattr -i /sys/firmware/efi/efivars/{KEK-...,db-...} # use tab completion to get the full filenames
    # sbctl enroll-keys --microsoft
    
  7. Install the linux, linux-firmware, and linux-headers kernel packages:

    # pacman -Syu linux{,-{firmware,headers}}
    

    This should automatically run mkinitcpio, and sign the UKI (both via the kernel-install plugins).

  8. Install systemd-boot and sign its EFI executables:

    # bootctl install
    # sbctl sign --save /efi/EFI/systemd/systemd-bootx64.efi
    # sbctl sign --save /efi/EFI/BOOT/BOOTX64.EFI
    
  9. Check the Secure Boot status:

    # sbctl status
    

    This might show that we're still in Setup Mode, and that Secure Boot is disabled. Otherwise it should show Setup Mode is disabled, and Secure Boot is enabled. I honestly don't remember, the final sbctl status after first successful boot should show Setup Mode is Disabled, and Secure Boot is Enabled. YMMV, depending on your laptop you may need to explicitly enable Secure Boot in the UEFI BIOS firmware settings after enrolling keys.

  10. Run sbctl verify. Note, due to limitations of lsblk -f in the arch-chroot (at least at the time I was executing this installation), we need to set the ESP_PATH=/efi so sbctl verify can find the ESP (/efi). It should show that the UKI we just installed is signed:

    # sbctl verify
    

Finalizing before first boot

  1. Set the root password:
    # passwd
    
  2. Exit the arch-chroot, press Ctrl-d or type exit, then Enter.
  3. Reboot:
    # systemctl reboot
    
  4. Consider enabling a UEFI password (my X1 Carbon supports multiple levels of UEFI BIOS passwords) to protect against attackers who gain physical access to your laptop (i.e. if it is lost or stolen). My X1 Carbon has several options I would like to enable:
    • Config
      • Keyboard/Mouse
        • Disable TrackPad. One of the main reasons I prefer ThinkPads is they come with the TrackPoint. I never liked touchpads on laptops, and the biggest problem I've always had is it registers taps while I'm typing. Even the touchpad on my MacBook Pros do this all the time. How hard is it to configure a default that the touchpad won't register a tap if keys have been pressed within within 200-300ms? I'm sure there are settings that can adjust this, but they seem unreliable when I've tried to use them. I did disable the TrackPad on my ThinkPad 25/20K7, but within Linux with libinput, which itself could be unreliable. Disabling it in the BIOS will also mean my wife won't be able to use my ThinkPads, and I'm OK with that.
    • Security
      • Password
        • Supervisor password.
        • Lock UEFI BIOS Settings (will require the Supervisor password)
        • Password at Boot Device List (also will require Supervisor password)
      • Internal Device Access
        • Bottom Cover Tamper Detection
      • Secure Boot
        • Secure Boot. At this point, Secure Boot was already enabled due to the sbctl enroll-keys, and during the troubleshooting trying to fix first boot I could not reenable Secure Boot through the UEFI BIOS alone; I had to do it from the OS installation. Again, YMMV (your mileage may vary), laptops from other manufacturers might require both: enroll-keys and enable Secure Boot in the UEFI firmware settings.

First Boot

If everything goes well, the system should boot into the Arch Linux system. It shouldn't prompt you for the LUKS password, but if it does simply press Enter since earlier we set an empty passphrase (this is precisely what happened once I had a successful boot on my X1 Carbon; I suppose the kernel has to prompt for the keyboard input passphrase, even though it's empty). In a few steps we will be enabling the recovery password, and then set up the TPM2 cryptoprocessor to unlock the LUKS volume assuming the UKI is not tampered with.

  1. At the login prompt, enter the root username, and password when prompted for it.
  2. Set up the recovery password:
    # systemd-cryptenroll /dev/nvme0n1p2 --recovery-key
    
    This should print the recovery key, and show a QR code which can be scanned to retrieve the key and store it in a password manager for future reference. Contrary to expectation, a QR code was NOT displayed; I'm not sure why. On the second try (the first time I completely failed to capture the recovery key), I took a photo of the recovery key so if the TPM2 device doesn't unlock the LUKS container, I have a backup. TODO: Currently my 20K7 ThinkPad 25 uses a LUKS1 volume since that was all GRUB was compatible with the last time I set up this laptop. I have seen QR codes in text-only terminals before, so this should work even though a graphical user interface is not yet installed. But the QR code did not show in the Arch ISO Linux console; I guess what I had seen was in a graphical terminal emulator. systemd-cryptenroll is only compatible with LUKS2 volumes, which is what we set up earlier (the default LUKS container is LUKS2 in the latest versions of cryptsetup).
  3. Enroll the TPM2 key, and wipe the empty slot:
    # systemd-cryptenroll /dev/nvme0n1p2 --wipe-slot=empty --tpm2-device=auto
    
  4. Reboot the system:
    # systemctl reboot
    
    If all goes well, it should boot back to the login prompt without intervention (unless you require a UEFI BIOS password to boot the system).

Create non-root users with systemd-homed

  1. Log back in as root.
  2. Enable and start systemd-homed.service:
    # systemctl enable --now systemd-homed.service
    
  3. Ensure /home has the C (No_COW) extended attribute set:
    # lsattr -d /home
    
    It should show the following:
    ---------------C------ /home
    
    If C doesn't appear, set No_COW:
    # chattr +C /home
    
    ...and check again. This is so the systemd-homed image loopback files have No_COW applied, so the outer Btrfs filesystem doesn't have the overhead associated with Copy on Write. It is intended for the mounted Btrfs filesystems contained within the unlocked LUKS2 containers will have COW enabled, so each individual top-level Btrfs filesystem can have its own snapshots and subvolumes.
  4. Create a user account and set its password:
    homectl create <username> \
        --storage=luks \
        --fs-type=btrfs \
        --shell=/usr/bin/zsh \
        --member-of=adm,dbus,disk,input,uucp,wheel
        --disk-size=500G
    
    Note, when I was setting this up for the first time, I didn't set the --disk-size=500G parameter. If this isn't specified, the LUKS2 container with Btrfs filesystem within it will take 85 percent of the available space. This isn't a huge problem for me right now, but it's something to keep in mind if you plan on having more than one regular user of the laptop.
    Repeat for as many users as you wish to create. Note that if the --shell parameter is not passed, homectl defaults the user's shell to /bin/bash.
  5. Log out as root: Ctrl-d, logout, or exit.
  6. Log in as your primary user, using the password set above. This should unlock and mount the home directory.

Configure NetworkManager

We don't yet have a fully operational system, since the network manager (NetworkManager) hasn't been set up yet. If you'd rather another network manager, you can install that instead. Honestly, I would prefer to use systemd-networkd, but I haven't seen where it has a nice graphical applet which I like to use on laptops and anything with a graphical user interface (since I can quickly enable VPN access as an unprivileged user, as well as see the relative WiFi network strength, etc.).

  1. Enable and start the systemd-resolved and NetworkManager service units:
    $ sudo systemctl enable --now systemd-resolved.service NetworkManager.service
    
    NOTE: Do NOT install and run two network managers at once, unless you really know what you're doing! Basically, if you run multiple network managers you'll need to ensure that the sets of network interfaces they manage do not overlap. Since this takes a lot of extra thought, planning, and configuration work, it's best to only use one.
  2. List nearby WiFi networks:
    $ nmcli device wifi list
    
  3. Connect to the desired WiFi network:
    $ nmcli device wifi connect <SSID_or_BSSID> password "<password>"
    
  4. Test the connection:
    $ ping archlinux.org
    

Upgrade the firmware of the laptop

I will use fwupdmgr (part of fwupd) to download a list of available firmware from the Linux Vendor Firmware Service(LVFS), and upgrade my X1 Carbon's firmware. Alternatively, Lenovo provides a bootable ISO which can upgrade the firmware as well. If you do not have a ThinkPad, you may want to check the LVFS to see if your laptop is supported, or refer to the manufacturer's website.

The Arch Wiki mentions that one needs to use shim in order to chainload the EFI binary, but that looks fairly complicated to pull off, especially since shim seems to hard code some filenames and EFI executable paths. Alternatively, it mentions you can sign the EFI executables installed by fwupdmgr so Secure Boot will boot from it. I will be using sbctl to sign the EFI files. As of this writing (2023-12-16 17:46:32 -05:00), the Arch Wiki doesn't mention exactly how to do this. If this works as expected, I'll update the Arch Wiki with these details.

One other thing and this is CRUCIAL: Upgrading the firmware will modify hashes for it. The previous hashes will be stored in the TPM PCR registers (7+possibly others). This WILL cause the TPM to fail to unlock the LUKS2 container, and a recovery key (or regular user passphrase) will be required to boot after a firmware upgrade. I haven't yet tested how to work around this; but it involves a new feature of systemd called systemd-pcrlock. As of this writing (2024-02-06, systemd 255), systemd-pcrlock only works for external, removable drives encrypted with LUKS2. There are plans to make it handle root volumes, but that isn't released yet (and will likely not be out of experimental phases for a few systemd versions). Lennart Poettering, the lead developer of systemd, has opened a Request For Enhancement (RFE) to fwupd, to allow it to properly update the TPM2 PCRs when the firmware is updated, but that is very far from being production ready; even then it may still not be appropriate for laptops (possibly only really remote servers).

The current suggestion is to merely unlock the LUKS2 container for the root subvolume with the recovery key should a firmware upgrade cause the TPM to not unlock the container. We can call systemd-cryptenroll to update the stored key. Since I've settled on this solution my last firmware update only seemed to be for the webcam, and did not cause the TPM2 to refuse to unlock the LUKS2 container. I'm monitoring a few LVFS Atom feeds for the X1 Carbon 11g, so it remains to be seen whether the next firmware update will cause this problem.

  1. Install fwupd and udisks2 (the latter is to be able to update the UEFI firmware). Using pikaur:
    $ pikaur -Syu udisks2 fwupd
    
  2. Enable and start the udisks2 service:
    $ sudo systemctl enable --now udisks2.service
    
  3. Edit /etc/fwupd/fwupd.conf and add the following lines:
    [uefi_capsule]
    DisableShimForSecureBoot=true
    
  4. Start the fwupd service (we do not enable it on boot, after a period of exactly two hours it shuts itself down, or at least that was my experience when I started it on my ThinkPad 25):
    $ sudo systemctl start fwupd.service
    
  5. Download the LVFS list:
    $ sudo -i
    # fwupdmgr refresh
    
    Note, this seems to issue a non-critical error if the list hasn't been downloaded before on this system (I've run it on a few systems now, outside of my laptops).
  6. List all devices which are eligible for update:
    # fwupdmgr get-updates
    
  7. Update all eligible devices:
    # fwupdmgr --no-reboot-check update
    
    We use --no-reboot-check to ensure it doesn't prompt to reboot, we need to sign the fwupd EFI executable. I forgot to use this flag and didn't heed this warning when I rebooted after the first firmware update on my X1 Carbon, and that causd the TPM digest/hash to change so I could not unlock on reboot.
  8. Sign the resulting EFI executable with sbctl:
    # sbctl sign --save /efi/EFI/arch/fwupdx64.efi
    # sbctl verify
    
  9. Reboot the laptop:
    # systemctl reboot
    

At this point, the laptop will reboot. It should show progress information as the EFI executable flashes the firmware, and may reboot several times. Some laptop models may show blank screens or the screen may be completely off; DO NOT FORCE THE POWER OFF, as doing so could lead to a partially flashed firmware image, irreperably harming the firmware and bricking your device. It could take several reboots and many minutes (or even hours in some cases) as the process completes.

If all goes well Arch Linux should boot back to a login prompt, and you can proceed with the rest of the installation. If the firmware upgrade causes the TPM to refuse to unlock the LUKS2 container, enter your recovery key, and reenroll the TPM using this procedure:

  1. Determine which keyslot the TPM2 is using:
    # export keyslot=$(sudo cryptsetup luksDump /dev/nvme0n1p2 --dump-json-metadata  | jq -r '.tokens.[] | select(.type == "systemd-tpm2") | .keyslots.[]')
    
  2. Reenroll the TPM2:
    # sudo systemd-cryptenroll --wipe-slot=${keyslot} --tpm2-device=auto
    

Conclusion

Now a basic Arch system should be ready to use! Follow the Arch Linux General Recommendations for setting up the laptop however you want.

For me, I use a basic X.org setup with XMonad and no desktop environment (DE, like GNOME, KDE Plasma, or Xfce), and intend to install borg (restore backup from my Arch Linux file server) and syncthing (mirrored directory).

As a matter of course and convenience, I enable passwordless sudo for the wheel group (alternatively, you could use the sudo group, but you'll need to modify the homectl command above). I do not recommend doing this if you do not trust everyone in your household or your workplace, or wherever you will be using this laptop the most. If you frequently take this laptop out in public places, consider not doing this, to increase security. Enable passwordless sudo:

$ sudo -i
# echo "%wheel ALL=(ALL) NOPASSWD: ALL" | tee /etc/sudoers.d/wheel

The following is what I intend to install at the end of these instructions. Your list will likely be different! Again, I use pikaur as my AUR-helper, which is a drop-in replacement for pacman (it accepts the same options, with extensions that search the AUR for packages and install them and all dependencies with pacman underneath). What I like about pikaur is that by default it prompts you to review the PKGBUILD for any AUR packages you install. When an AUR package is upgraded, whether it be a new upstream major, minor, or patch release version, or if the AUR package maintainer has made a package release update, it shows you a diff between the old PKGBUILD and the new PKBUILD. It also pulls in all build and runtime dependencies, whether they're in the AUR, or part of core or extra. If they're build dependencies, by default pikaur will remove these once the package is built and installed (though this is configurable). Install everything:

$ pikaur -Syu xmonad{,-{contrib,utils} stalonetray borg syncthing alacritty qutebrowser vivaldi firefox mutt isync w3m evince bc tmuxp autokey-gtk zoom slack bitwarden-cli 1password-cli lm-sensors conky-cli wget curl openssl network-manager-applet wireguard-tools yegonesh arandr xorg-xrandr xorg-xset xorg-xsetroot zip unzip rsync clipmenu dunst simple-scan cups{,-filters} deluge-gtk dmidecode dzen2 etckeeper exfat-utils feh geoip gimp hplip aspell enchant i3lock iftop iotop imapfilter imagemagick inetutils inkscape xournalpp iperf3 nodejs-fast-cli iptraf-ng graphviz logrotate logwatch postfix lsof mailcap mediainfo mplayer mutt-ics neofetch nmap notmuch-{mutt,runtime} pacman-contrib pacserve pavucontrol pinfo rofi rust scrot signal-desktop sipcalc smartmontools snapper ssh-audit stress sysstat tcpdump wireshark texinfo texlive-{basic,fontsextra,latex{,extra}} traceroute ttf-cascadia-code ttf-intel-one-mono tuptime unhide webcamoid wego xautolock xclip xcursor-vanilla-dmz-aa xdotool xorg-{xev,xprop}

Next step, restoring stuff from backup, including what I have backed up from my private repositories on git.eldon.me. Also, I will be setting up the fingerprint reader with fprint, setting up my HP Color LaserJet MFP M283fdw for both printing and scanning, ensuring the webcam works (I specifically ordered this X1 Carbon so it would have a working webcam, the high end webcam option is currently incompatible with Linux because there aren't drivers for it yet), and setting up new SSH keys for the new laptop.

Once the new laptop has been running for a while, and most if not all the kinks have been worked out, I will set up snapper for my root subvolume and systemd-homed Btrfs filesystems, and set it up to back it up to my file server using borg.