This is a twist on my current Arch install method.
This install will result in a very clean base install using btrfs for a filesystem, mkinitcpio
set up to generate UKIs, Secure Boot handled by sbctl
, and your TPM handling encryption unlocking. You just need to bake in your DE of choice.
You'll need to grab the latest Arch Linux ISO, write it a USB drive and boot off it. From there, do your usual checks for internet access, and then we can begin.
Disk preparation
We'll use a 512MB FAT32 system partition for our EFI partition and we're going to use btrfs, inside of a LUKS encrypted partition, for the root.
We're going to take the time and effort to use correct partition types, so systemd can detect them (see this page for more information on Discoverable Partitions Specification).
First of all, let's find our drive name:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 673M 1 loop /run/archiso/airootfs
sr0 11:0 1 793.3M 0 rom /run/archiso/bootmnt
vda 254:0 0 30G 0 disk
Our drive, in this case, is /dev/vda/
, so let's use sgdisk
to setup the disk:
$ sgdisk -Z /dev/vda
$ sgdisk -n1:0:+512M -t1:ef00 -c1:EFI -N2 -t2:8304 -c2:LINUXROOT /dev/vda
$ partprobe -s /dev/vda
We should now have an EFI partition on /dev/vda1
and our root partition on /dev/vda2
. Let's check:
$ lsblk /dev/vda
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 254:0 0 30G 0 disk
├─vda1 254:1 0 512M 0 part
└─vda2 254:2 0 29.5G 0 part
Looking good, next, let's encrypt our root partition with LUKS, using cryptsetup
:
$ cryptsetup luksFormat --type luks2 /dev/vda2
$ cryptsetup luksOpen /dev/vda2 linuxroot
Let's create the filesystems:
$ mkfs.vfat -F32 -n EFI /dev/vda1
$ mkfs.btrfs -f -L linuxroot /dev/mapper/linuxroot
Let's mount our partitions, and create our btrfs subvolumes:
$ mount /dev/mapper/linuxroot /mnt
$ mkdir /mnt/efi
$ mount /dev/vda1 /mnt/efi
$ btrfs subvolume create /mnt/home
$ btrfs subvolume create /mnt/srv
$ btrfs subvolume create /mnt/var
$ btrfs subvolume create /mnt/var/log
$ btrfs subvolume create /mnt/var/cache
$ btrfs subvolume create /mnt/var/tmp
Base install
Let's update the pacman mirrors, and then pacstrap a base install:
$ reflector --country GB --age 24 --protocol http,https --sort rate --save /etc/pacman.d/mirrorlist
$ pacstrap -K /mnt base base-devel linux linux-firmware amd-ucode vim nano cryptsetup btrfs-progs dosfstools util-linux git unzip sbctl kitty networkmanager sudo
Update our locale settings:
$ sed -i -e "/^#"en_GB.UTF-8"/s/^#//" /mnt/etc/locale.gen
$ systemd-firstboot --root /mnt --prompt
Welcome to your new installation of Arch Linux!
Please configure your system!
-- Press any key to proceed --
‣ Please enter system keymap name or number (empty to skip, "list" to list options): uk
/mnt/etc/vconsole.conf written.
‣ Please enter timezone name or number (empty to skip, "list" to list options): Europe/London
/mnt/etc/localtime written
‣ Please enter hostname for new system (empty to skip): archinstall9001
/mnt/etc/hostname written.
$ arch-chroot /mnt locale-gen
Generating locales...
en_GB.UTF-8... done
Generation complete.
User creation
Let's create our local user account, in this case the username will be "walian", add him/her to the wheel
group, and then give the wheel
group sudo privileges:
$ arch-chroot /mnt useradd -G wheel -m walian
$ arch-chroot /mnt passwd walian
$ sed -i -e '/^# %wheel ALL=(ALL:ALL) NOPASSWD: ALL/s/^# //' /mnt/etc/sudoers
Unified Kernel fun
We want to create Unified Kernel Images, so first, let's create our kernel cmdline file. This doesn't need to contain anything because we're using Discoverable Partitions, we just do it so mkinitcpio
doesn't complain:
$ echo "quiet rw" >/mnt/etc/kernel/cmdline
Let's create the EFI folder structure:
$ mkdir -p /mnt/efi/EFI/Linux
We need to change the HOOKS in mkinitcpio.conf
to use systemd, so make yours look like:
/mnt/etc/mkinitcpio.conf
# vim:set ft=sh
MODULES=()
BINARIES=()
FILES=()
HOOKS=(base systemd autodetect modconf kms keyboard sd-vconsole sd-encrypt block filesystems fsck)
And now let's update the .preset file, to generate a UKI:
/mnt/etc/mkinitcpio.d/linux.preset
# mkinitcpio preset file to generate UKIs
ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"
ALL_microcode=(/boot/*-ucode.img)
PRESETS=('default' 'fallback')
#default_config="/etc/mkinitcpio.conf"
#default_image="/boot/initramfs-linux.img"
default_uki="/efi/EFI/Linux/arch-linux.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"
#fallback_config="/etc/mkinitcpio.conf"
#fallback_image="/boot/initramfs-linux-fallback.img"
fallback_uki="/efi/EFI/Linux/arch-linux-fallback.efi"
fallback_options="-S autodetect"
And now let's generate our UKIs:
$ arch-chroot /mnt mkinitcpio -P
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
==> Using configuration file: '/etc/mkinitcpio.conf'
-> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -U /efi/EFI/Linux/arch-linux.efi --splash /usr/share/systemd/bootctl/splash-arch.bmp --microcode /boot/amd-ucode.img
==> Starting build: '6.4.12-arch1-1'
-> Running build hook: [base]
-> Running build hook: [systemd]
-> Running build hook: [autodetect]
-> Running build hook: [modconf]
-> Running build hook: [kms]
-> Running build hook: [keyboard]
==> WARNING: Possibly missing firmware for module: 'xhci_pci'
-> Running build hook: [sd-vconsole]
-> Running build hook: [sd-encrypt]
-> Running build hook: [block]
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
==> Generating module dependencies
==> Creating zstd-compressed initcpio image: '/tmp/mkinitcpio.Hp46P3'
==> Image generation successful
==> Creating unified kernel image: '/efi/EFI/Linux/arch-linux.efi'
-> Using UEFI stub: '/usr/lib/systemd/boot/efi/linuxx64.efi.stub'
-> Using os-release file: '/etc/os-release'
-> Using cmdline file: '/etc/kernel/cmdline'
-> Using splash image: '/usr/share/systemd/bootctl/splash-arch.bmp'
-> Using kernel image: '/boot/vmlinuz-linux'
-> Using microcode image: '/boot/amd-ucode.img'
==> Unified kernel image generation successful
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'fallback'
==> Using configuration file: '/etc/mkinitcpio.conf'
-> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -U /efi/EFI/Linux/arch-linux-fallback.efi -S autodetect --microcode /boot/amd-ucode.img
==> Starting build: '6.4.12-arch1-1'
-> Running build hook: [base]
-> Running build hook: [systemd]
-> Running build hook: [modconf]
-> Running build hook: [kms]
==> WARNING: Possibly missing firmware for module: 'ast'
-> Running build hook: [keyboard]
==> WARNING: Possibly missing firmware for module: 'xhci_pci'
-> Running build hook: [sd-vconsole]
-> Running build hook: [sd-encrypt]
-> Running build hook: [block]
==> WARNING: Possibly missing firmware for module: 'aic94xx'
==> WARNING: Possibly missing firmware for module: 'bfa'
==> WARNING: Possibly missing firmware for module: 'qed'
==> WARNING: Possibly missing firmware for module: 'qla1280'
==> WARNING: Possibly missing firmware for module: 'qla2xxx'
==> WARNING: Possibly missing firmware for module: 'wd719x'
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
==> Generating module dependencies
==> Creating zstd-compressed initcpio image: '/tmp/mkinitcpio.nCqFo4'
==> Image generation successful
==> Creating unified kernel image: '/efi/EFI/Linux/arch-linux-fallback.efi'
-> Using UEFI stub: '/usr/lib/systemd/boot/efi/linuxx64.efi.stub'
-> Using os-release file: '/etc/os-release'
-> Using cmdline file: '/etc/kernel/cmdline'
-> Using kernel image: '/boot/vmlinuz-linux'
-> Using microcode image: '/boot/amd-ucode.img'
==> Unified kernel image generation successful
If we have a look into our EFI partition, we should see our UKIs:
$ ls -lR /mnt/efi
/mnt/efi:
total 4
drwxr-xr-x 3 root root 4096 Aug 25 20:51 EFI
/mnt/efi/EFI:
total 4
drwxr-xr-x 2 root root 4096 Aug 25 21:02 Linux
/mnt/efi/EFI/Linux:
total 118668
-rwxr-xr-x 1 root root 29928960 Aug 9 14:07 arch-linux.efi
-rwxr-xr-x 1 root root 91586048 Aug 9 14:07 arch-linux-fallback.efi
Services and Boot Loader
OK, we're just about done in the archiso, we just need to enable some services, and install our bootloader:
$ systemctl --root /mnt enable systemd-resolved systemd-timesyncd NetworkManager
$ systemctl --root /mnt mask systemd-networkd
$ arch-chroot /mnt bootctl install --esp-path=/efi
OK, let's reboot, and then finish off the installation. Whilst you're rebooting, head into your UEFI/BIOS and put Secure Boot into "Setup Mode", you'll need to check with your PC/Motherboard manufacturer for exact details on how to do that:
$ sync
$ systemctl reboot --firmware-setup
Secure Boot with TPM2 Unlocking
Ok, now that we've rebooted and logged back in, we need to first check that Secure Boot is in "Setup Mode":
$ sbctl status
Installed: ✓ sbctl is installed
Setup Mode: ✗ Enabled
Secure Boot: ✗ Disabled
Vendor Keys: none
Looks good. Let's first create and enroll our personal Secure Boot keys:
$ sudo sbctl create-keys
$ sudo sbctl enroll-keys -m
We use the -m
option to enroll the Microsoft vendor key as well as our self-created platform key. IF you're sure that none of your hardware has any OPROMs signed by Microsoft, you can leave this option out. WARNING - Your system CAN get bricked if you're mistaken. I know it sucks, but it's usually safer to just install the Microsoft vendor key. YOU HAVE BEEN WARNED!!! :-)
Let's use sbctl
to sign our .efi files. We'll sign the systemd-boot .efi file, and our UKI files which mkinitcpio
is now generating for us. We'll use the -s
option so sbctl
will automatically resign them for us when we update the kernel or bootloader through pacman
:
$ sudo sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi
$ sudo sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
$ sudo sbctl sign -s /efi/EFI/Linux/arch-linux.efi
$ sudo sbctl sign -s /efi/EFI/Linux/arch-linux-fallback.efi
Let's reinstall the kernel to make sure it resigns the UKI:
$ sudo pacman -S linux
... (I've cut out the blurb)** ...
(4/4) Signing EFI binaries...
Generating EFI bundles....
✓ Signed /efi/EFI/Linux/arch-linux-fallback.efi
✓ Signed /efi/EFI/Linux/arch-linux.efi
Looking good. Reboot your PC now, so the Secure Boot settings will get saved. Once rebooted we need to configure automatic unlocking of the root filesystem, by binding a LUKS key to the TPM. Let's generate a new key, add it to our volume so it can be used to unlock it in addition to the existing keys, and bind this new key to PCRs 0 and 7 (the system firmware and Secure Boot state).
First things first, let's generate a recovery key in case it all gets messed up some time in the future:
$ sudo systemd-cryptenroll /dev/gpt-auto-root-luks --recovery-key
Keep this key safe. Keep it hidden. Moving on, we'll now enroll our system firmware and Secure Boot state. Normally, this would allow our TPM to unlock our encrypted drive, as long as the state hasn't changed. If you're particularly paranoid, you can add --tpm2-with-pin=yes
, so we get prompted for a PIN code on boot.
$ sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 /dev/gpt-auto-root-luks
Now we need to reboot and test this. If all goes well, we should be able to unlock our encrypted root partition with just a PIN code.
Job done
And with that, we have a very nice base install completed. You can now go ahead and install your Desktop Environment. I'll throw a YouTube video together in the next few days, walking through this install and update this blogpost. Until then, have a good one!
Update
I've embedded a YouTube video below for your viewing pleasure.
More information
Discoverable Partitions Specification.
Install Arch with Secure boot, TPM2-based LUKS encryption, and systemd-homed
Podman
Trusted Platform Module
Linux Boot Partitions
Brave New Trusted Boot World
Unlocking LUKS2 volumes with TPM2, FIDO2, PKCS#11 Security Hardware on systemd 248
There are comments.