Installation
Boot from an Arch Linux live USB.
Preparation
Set the keyboard layout to your language, here I am using the French keyboard layout:
loadkeys fr
Set the clock to your timezone:
timedatectl set-timezone Europe/Paris
timedatectl set-ntp true
Partitioning
List the disks:
fdisk -l
Choose the desired disk and create 3 (or 4) partitions:
- An EFI partition (for UEFI)
- A boot partition (for the bootloader)
- A root partition ( / )
- Optionally, a home partition ( /home )
Partition the disk using parted
:
parted /dev/sdX
The EFI partition should be 500MiB.
With a /home
partition:
mklabel gpt
mkpart primary fat32 1MiB 501MiB # EFI partition
set 1 esp on
mkpart primary ext4 501MiB 20.5GiB # Root partition, approximately 20GiB
mkpart primary ext4 20.5GiB 100% # Home partition, using the remaining space
q
Without a /home
partition:
mklabel gpt
mkpart primary fat32 1MiB 551MiB # EFI partition
set 1 esp on
mkpart primary ext4 551MiB 100% # Root partition
q
Encryption of Partitions
Choose a passphrase (long enough) for the /root
partition (and one for the /home
partition if you have one) and encrypt the partition(s). We will use AES with a 512-bit key, a key derivation time of 5000 ms, and the Argon2id algorithm.
cryptsetup -v --type luks -c aes-xts-plain64 -s 512 -h sha512 --pbkdf argon2id -i 5000 -y luksFormat /dev/sdX2
cryptsetup -v --type luks -c aes-xts-plain64 -s 512 -h sha512 --pbkdf argon2id -i 5000 -y luksFormat /dev/sdX3
Mount the partition(s):
cryptsetup open /dev/sdX2 root
cryptsetup open /dev/sdX3 home
Formatting Partitions
Format the partitions as ext4 (except for the EFI partition, which is formatted as fat32):
mkfs.fat -F32 /dev/sdX1
mkfs.ext4 /dev/mapper/root
mkfs.ext4 /dev/mapper/home
Arch Linux Installation
Mount the various partitions under /mnt
:
mount /dev/mapper/root /mnt
mkdir /mnt/home && mount /dev/mapper/home /mnt/home # If using /home partition
mkdir -p /mnt/boot/efi && mount /dev/sda1 /mnt/boot/efi
Install the base packages and the hardened kernel (replace linux-hardened
with linux
for the regular kernel):
pacstrap /mnt base base-devel linux-hardened linux-hardened-headers linux-firmware
Generate the fstab
:
genfstab -U /mnt >> /mnt/etc/fstab
Arch Linux Configuration
Use the chroot
command to enter our new installation as the root:
arch-chroot /mnt
Choose the hostname for the machine (here we use MachineName as an example):
echo MachineName > /etc/hostname
echo '127.0.0.1 MachineName.localdomain MachineName' >> /etc/hosts
Configure the clock:
ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime
hwclock --systohc
Generate the system’s locales:
-
In French:
sed -i 's/#fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/' /etc/locale.gen locale-gen echo LANG="fr_FR.UTF-8" > /etc/locale.conf export LANG=fr_FR.UTF-8
-
In English:
sed -i 's/#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen locale-gen echo LANG="en_US.UTF-8" > /etc/locale.conf export LANG=en_US.UTF-8
Configure the keyboard layout as AZERTY:
echo KEYMAP=fr > /etc/vconsole.conf
Set a password for the root
account:
passwd
Boot and Decryption Management
Install efibootmgr
and a text editor:
pacman -S efibootmgr <TEXT_EDITOR>
Since we haven’t created a separate partition for /boot
, we will create a unified kernel image so that our installation can be bootable.
Brief explanation of the Linux boot process
Typically, to boot a Linux system, the UEFI (the motherboard firmware) starts by loading a bootloader, a program that “prepares” the system to boot the Linux kernel. The bootloader is a .efi file located on the first partition of the disk. It loads a CPU microcode (a set of patches provided by the CPU manufacturer to ensure its stability), an initramfs (which creates an initial file system to access the root file system), and finally a Linux kernel. It also allows passing parameters to the kernel before booting. For more information, see https://wiki.archlinux.org/title/Arch_boot_process.
Some bootloaders, such as GRUB or systemd-boot, allow choosing between multiple kernels or operating systems (in the case of multiboot).
In our case, we will configure the system to boot directly into the kernel without using a bootloader (reducing the attack surface) by compiling the CPU microcode, initramfs, the kernel, and its arguments into a single .efi file that can be launched directly by the motherboard.
Microcode Installation
Install the microcode for Intel and AMD processors:
pacman -S amd-ucode # for AMD
pacman -S intel-ucode # for Intel
If you are installing Arch on a bootable USB drive, you can install the microcodes for Intel and AMD to support different processors.
Kernel Boot Arguments
Retrieve the UUID of the /
partition:
lsblk -f /dev/sdX2
Edit the /etc/kernel/cmdline
file:
- Add the options
pti=on
,page_alloc.shuffle=1
, andrw
. - Add the disk decryption information
cryptdevice=UUID=<partition UUID>:root root=/dev/mapper/root
(the UUID is the one where FSTYPE iscrypto_LUKS
in thelsblk -f /dev/sdX
command) - Finally, add the options
loglevel=3 quiet bgrt_disable
Your file should look like this:
pti=on page_alloc.shuffle=1 rw cryptdevice=UUID=XXXXXXXX-XXXX-4XXX-XXXX-XXXXXXXXXXXX:root root=/dev/mapper/root mem_sleep_default=deep loglevel=3 quiet bgrt_disable
If you are using an SSD, which is likely, you can improve performance for dm-crypt in terms of reading and writing by disabling asynchronous reads and writes and queuing mechanisms: replace the cryptdevice=UUID=<partition UUID>:root root=/dev/mapper/root
part with cryptdevice=UUID=<YOUR UUID>:root:no-read-workqueue,no-write-workqueue root=/dev/mapper/root
Generating EFI Images
Edit the /etc/mkinitcpio.conf
file and add the keymap
and encrypt
hooks, which will allow us to decrypt our root partition during boot:
HOOKS=(base udev autodetect modconf block filesystems keyboard fsck keymap encrypt)
If you have a partition for /home
, modify the /etc/fstab
file to avoid using the UUID
of /dev/mapper/home
for mounting. Comment out the corresponding line and add one last line containing:
/dev/mapper/home /home ext4 rw,relatime,data=ordered 0 2
If you have an SSD and want to improve its longevity, you can replace relatime
with noatime
in /etc/fstab
. This will disable file access timestamp updates and reduce the number of disk writes.
Now we will create an mkinitcpio preset to generate a bootable unified image. Edit the /etc/mkinitcpio.d/linux-hardened.preset
file and add the following content:
# mkinitcpio preset file for the 'linux-hardened' unified image
ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux-hardened"
ALL_microcode="/boot/*-ucode.img"
PRESETS=('default' 'fallback')
default_image="/boot/initramfs-linux-hardened.img"
default_uki="/boot/efi/EFI/Linux/archlinux-linux.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"
fallback_image="/boot/initramfs-linux-hardened-fallback.img"
fallback_options="-S autodetect"
fallback_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"
fallback_uki="/boot/efi/EFI/Linux/archlinux-linux-fallback.efi"
This preset will allow us to generate two EFI files: /boot/efi/EFI/Linux/archlinux-linux.efi
and /boot/efi/EFI/Linux/archlinux-linux-fallback.efi
, containing our CPU microcode, initramfs, and kernel.
Generate the EFI images:
mkdir -p /boot/efi/EFI/Linux
mkinitcpio -p linux-hardened
Adding UEFI Entries
Add the UEFI entries for our images so that we can select them in our motherboard:
efibootmgr --create --disk /dev/sdX --part 1 --label "Arch Linux" --loader 'EFI\Linux\archlinux-linux.efi' --unicode
efibootmgr --create --disk /dev/sdX --part 1 --label "Arch Linux Fallback" --loader 'EFI\Linux\archlinux-linux-fallback.efi' --unicode
At this point, your system should be bootable.
Setting Up Secure Boot
Now that our system can boot, we will set up secure boot.
Secure Boot is a security mechanism that verifies the integrity of the kernel and modules loaded during boot. It ensures that the loaded files are signed with a cryptographic key, thereby ensuring they haven’t been tampered with by an attacker.
The first step to setting up secure boot is to install a tool for managing signatures and keys. We will install sbctl
:
pacman -S sbctl
Next, we will generate keys that will be used to sign our efi files:
sbctl create-keys
Then, we will enroll our keys in the motherboard:
sbctl enroll-keys
You may need to restart your system and enter the UEFI to enter setup or audit mode (depending on how your motherboard manufacturer calls it) to have the opportunity to enroll the keys.
If enrolling the keys fails, you can try using
sbctl enroll-keys --microsoft
to enroll the Microsoft keys in addition to the ones you have generated. Some devices have their firmware signed with Microsoft keys, and your computer might refuse to boot without them…
Now, we will sign our efi files:
sbctl sign -s /boot/efi/EFI/Linux/archlinux-linux.efi
sbctl sign -s /boot/efi/EFI/Linux/archlinux-linux-fallback.efi
We can now verify that the files are properly signed:
sbctl verify
Finally, reboot, enable secure boot in the UEFI, and verify that the system boots correctly.
To ensure that everything is properly configured, you can use sbctl status
to check if secure boot is enabled and the keys are enrolled.
The
sbctl sign-all
command can be used to sign all previously signed efi files. If you have modified any of the efi files, for example withmkinitcpio
, you can use this command to sign them again.
Disk Decryption via SSH
If you want to be able to use your computer remotely (e.g., by turning it on via Wake-on-LAN), you will need to decrypt the disks before even launching the kernel. For this, we can use Dropbear, an SSH client that starts with the bootloader.
Installing Dependencies
pacman -S mkinitcpio-dropbear mkinitcpio-utils mkinitcpio-netconf
Generating Keys
Generate an Ed25519 key pair that will be used for SSH login:
ssh-keygen -t ed25519 -f ~/.ssh/dropbear_ssh
Copy the public key to the Dropbear configuration folder:
cp ~/.ssh/dropbear_ssh.pub /etc/dropbear/root_key
Copy the private key to the device you will use to decrypt your disk.
Configuring mkinitcpio
Add the netconf
, dropbear
, and encryptssh
hooks to the `/etc/mk
initcpio.conf` file:
HOOKS=(base udev autodetect modconf block netconf dropbear keyboard keymap encryptssh filesystems fsck)
Modify the /etc/kernel/cmdline
file and add an ip
field:
ip=192.168.1.1:::::eth0:none
The ip
field has the following format: ip=<ip>::<gateway>:<mask>::<interface>:<none|dhcp>
.
For example, to have your computer start with the IP address 192.168.1.22
on the 192.168.1.0/24
network with a gateway at 192.168.1.254
:
ip=192.168.1.22::192.168.1.254:255.255.255.0::eth0:none
To automatically detect the network configuration and use DHCP:
ip=:::::eth0:none
Generating EFI Images
Regenerate the EFI files:
mkinitcpio -p linux-hardened
Disk Decryption
On the next boot, you will have the option to connect to your computer via SSH using the generated key. You only need to enter the decryption key for your disk, and your computer will boot.
Installing Base Packages and Tweaks
Network
If you want to install additional packages on your installation, you will need internet access. Before exiting the installation media, install NetworkManager
:
pacman -S networkmanager
systemctl enable NetworkManager
Hardened Malloc
If you want additional protection against memory-related security vulnerabilities (such as buffer overflow), you can replace the glibc malloc implementation with the one provided by the GrapheneOS team: hardened_malloc.
Hardened Malloc is not available in the official Arch Linux repositories but can be found in the community repositories (https://aur.archlinux.org/packages/hardened_malloc). Unfortunately, at the time of writing this tutorial, the maintainer’s signature is no longer valid. Therefore, we will install it manually.
Clone the repository and compile the library:
git clone https://github.com/GrapheneOS/hardened_malloc/
cd hardened_malloc
git checkout tags/11 # use the latest version (in this case, version 11)
make # compile hardened_malloc
To ensure there are no issues, increase the number of usable memory maps on the system:
echo 'vm.max_map_count = 1048576' > /etc/sysctl.d/hardened_malloc.conf
Install the library on the system:
cp out/libhardened_malloc.so /usr/local/lib/libhardened_malloc.so
Ensure that hardened_malloc is loaded for each program:
echo '/usr/local/lib/libhardened_malloc.so' >> /etc/ld.so.preload
Congratulations! You have installed hardened_malloc on your system. To verify, you can run a program and check if libhardened_malloc.so
is being loaded:
cat /proc/<PID>/maps | grep hardened_malloc