Last modified: 2023-03-29
ArchLinux: Installation

Source: wiki.archlinux.org
This installation is mostly bare-bone installation, with following features:
- encrypted disk (
/bootis not encrypted) - local and remote unlock o encrypted disk via ssh
- btrfs as file-system
doasinstead ofsudo- micro-code updates
- secured
GRUBwith password (tampering prevention) - secured boot with
chkboot(tampering detection) - early KMS to initialize graphics card
Verify the boot mode
Check if the motherboard supports UEFI (and if it is enabled).
\$ ls /sys/firmware/efi/efivars
If the directory does not exist, the system is likely booted in BIOS mode.
Update system clock
\$ timedatectl set-ntp true
To check service status:
\$ timedatectl status
Local time: Sun 2019-12-08 10:55:34 CET
Universal time: Sun 2019-12-08 09:55:34 UTC
RTC time: Sun 2019-12-08 09:55:34
Time zone: Europe/Prague (CET, +0100)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Prepare the disk
Since this installation is going to be encrypted, I recommend to wipe the disk.
HDD or SSD with deniable encryption
Write random data to it:
# dd if=/dev/urandom of=/dev/sdX bs=4M status=progress
or use shred:
# shred -v -n 1 /dev/sdX
SSD
Use secure-erase if available. To check availability look for supported: enhanced erase in Security:
# hdparm -I /dev/sdX
/dev/sda:
ATA device, with non-removable media
Model Number: XXXXXXX
Serial Number: XXXXXXX
Firmware Revision: XXXXXXX
Transport: Serial, ATA8-AST, SATA 1.0a, SATA II Extensions, SATA Rev 2.5, SATA Rev 2.6, SATA Rev 3.0
Standards:
...
Configuration:
...
Capabilities:
...
Commands/features:
...
Security:
Master password revision code = 65534
supported
not enabled
not locked
frozen
not expired: security count
supported: enhanced erase
XXmin for SECURITY ERASE UNIT. XXmin for ENHANCED SECURITY ERASE UNIT.
Logical Unit WWN Device Identifier: XXXXXXX
...
Checksum: correct
Secure-erase:
# hdparm --user-master u --security-set-pass NULL /dev/sdX
# hdparm --user-master u --security-erase NULL /dev/sdX
Partition the disk
I will be using single partition, encrypted with dm-crypt in LUKS2 mode and btrfs. The layout will be as follows:
/dev/sdX
|-- /dev/sdX1
`-- ext4 : /boot
`-- /dev/sdX2
`-- luks : hostname_system_luks
`-- btrfs : hostname_system_btrfs
|-- @ : /
| |-- @/var/cache/pacman/pkg
| |-- @/var/lib/libvirt/iso_images
| |-- @/var/lib/libvirt/virtual_machines
| |-- @/var/log
| `-- @/var/tmp
|-- @home : /home
`-- @snapshots : /.snapshots
# fdisk /dev/sdX
| BIOS | UEFI | |
|---|---|---|
| Partition table | ||
o | g | |
/boot partition | ||
n | n | |
default (p) | ||
default (1) | default (1) | |
default | default | |
+100M | +100M | |
a | t | |
default (1) | ||
1 | ||
/ partition | ||
n | n | |
default (p) | ||
default (2) | default (2) | |
default | default | |
default | default |
dm-crypt
Create a LUKS2 encrypted container:
# cryptsetup -v --type luks2 luksFormat /dev/sdX2
WARNING!
========
This will overwrite data on /dev/sdX2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/sdX2:
Verify passphrase:
Command successful.
18.77s user 1.18s system 99% cpu 20.051 total
Open the container:
# cryptsetup open /dev/sdX2 hostname_system_luks
SSD: Enable TRIM and disable workqueue:
# cryptsetup --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue --persistent open /dev/sdX2 hostname_system_luks
Verify status:
# cryptsetup -v status hostname_system_luks
/dev/mapper/hostname_system_luks is active and is in use.
type: LUKS2
cipher: aes-xts-plain64
keysize: 512 bits
key location: keyring
device: /dev/sdX2
sector size: 512
offset: 19888128 sectors
mode: read/write
Command successful.
Create file-systems
/boot partition for BIOS
Format the /boot partition:
# mkfs.ext4 -L hostname_boot /dev/sdX1
/boot partition for UEFI
Format the /boot partition:
# mkfs.fat -F32 /dev/sdX1
btrfs for the rest
Format the open LUKS2 container to btrfs:
# mkfs.btrfs -L hostname_system_btrfs /dev/mapper/hostname_system_luks
Create subvolumes:
# mount /dev/mapper/hostname_system_luks /mnt
# btrfs subvolume create /mnt/@
# btrfs subvolume create /mnt/@home
# btrfs subvolume create /mnt/@snapshots
# mkdir -p /mnt/@/var/cache/pacman
# btrfs subvolume create /mnt/@/var/cache/pacman/pkg
# mkdir -p /mnt/@/var/lib/libvirt
# btrfs subvolume create /mnt/@/var/lib/libvirt/iso_images
# btrfs subvolume create /mnt/@/var/lib/libvirt/virtual_machines
# btrfs subvolume create /mnt/@/var/log
# btrfs subvolume create /mnt/@/var/tmp
Verify:
# btrfs subvolume list /mnt
Remount everything to correct locations:
# umount /mnt
# mount -o compress=zstd,subvol=@ /dev/mapper/hostname_system_luks /mnt
# mkdir -p /mnt/{home,.snapshots,boot}
# mount /dev/sdX1 /mnt/boot
# mount -o compress=zstd,subvol=@home /dev/mapper/hostname_system_luks /mnt/home
# mount -o compress=zstd,subvol=@snapshots /dev/mapper/hostname_system_luks /mnt/.snapshots
TRIM: To enable asynchronous discard add mount option discard=async
Disable btrfs COW (Copy on Write) and compression for certain directories:
# chattr +C /mnt/var/lib/libvirt/iso_images
# chattr +C /mnt/var/lib/libvirt/virtual_machines
Can be verified with (for explanation of parameters check man chattr):
# lsattr -a /mnt/var/lib/libvirt/virtual_machines
Mirrors
Edit list of mirrors and move those geographically close to your location up in the list. The list will be copied into the newly installed system.
It might be useful to setup a pacman proxy cache, especially if you are installing or updating multiple machines.
Edit list manually:
# nano /etc/pacman.d/mirrorlist
To use local mirror or cache, simply add at the top (don't forget to replace IP address and port):
Server = http://192.168.8.1:7878/\$repo/os/\$arch
Or use reflector to benchmark and select fastest mirrors:
# reflector --verbose --age 24 --country Germany --protocol https --sort rate --save /etc/pacman.d/mirrorlist
Installation bare-minimum
Add sudo into list of ignored packages in pacman configuration:
# nano /etc/pacman.conf
...
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
#IgnorePkg =
IgnorePkg = sudo
#IgnoreGroup =
...
Update package databases:
# pacman -Syy
Install following:
# pacstrap /mnt base base-devel linux linux-headers linux-firmware doas nano git btrfs-progs bash-completion grub openssh amd-ucode intel-ucode
Generate an /etc/fstab file:
# genfstab -U /mnt >> /mnt/etc/fstab
Configure some basics
chroot into the new system:
# arch-chroot /mnt
Set the time zone (replace REGION and CITY):
# ln -sf /usr/share/zoneinfo/REGION/CITY /etc/localtime
Run hwclock to generate /etc/adjtime:
# hwclock --systohc
Uncomment en_US.UTF-8 UTF-8 and other needed locales in /etc/locale.gen:
# nano /etc/locale.gen
The generate:
# locale-gen
Create the /etc/locale.conf file, and set the LANG variable accordingly:
# echo "LANG=en_US.UTF-8" > /etc/locale.conf
If you set the keyboard layout, make the changes persistent in /etc/vconsole.conf:
# echo "KEYMAP=en" > /etc/vconsole.conf
Create the /etc/hostname file:
# echo "HOSTNAME" > /etc/hostname
Add matching entries to /etc/hosts man/hosts:
# nano /etc/hosts
Example:
127.0.0.1 localhost
::1 localhost
127.0.1.1 hostname.localdomain hostname
If the system has a permanent IP address, it should be used instead of 127.0.1.1.
Set the root password if required:
# passwd
Add non-root user and set password:
# useradd -m username
# passwd username
# gpasswd -a username wheel
Give wheel group dias privileges (configuration file must end with newline!):
# nano /etc/doas.conf
permit setenv { XAUTHORITY LANG LC_ALL } :wheel
Symlink doas to where sudo would be:
# ln -s \$(which doas) /usr/bin/sudo
Edit ~/.bashrc and add useful things (or global /etc/bash.bashrc):
\$ nano ~/.bashrc
alias sudo='doas'
alias sudoedit='doas rnano'
# Bash tab completion
complete -cf doas
Install AUR helper
I am going with yay, but feel free to choose another one.
Use non-privileged user:
# su username
\$ export EDITOR=nano
\$ cd /tmp
\$ git clone https://aur.archlinux.org/yay.git
\$ cd yay
\$ makepkg -si
Install additional packages
\$ yay -S pacman-cleanup-hook systemd-cleanup-pacman-hook
Install bootloader GRUB
grub-install for BIOS
Keep in mind to select entire disk and not only partition:
# grub-install --target=i386-pc /dev/sdX
grub-install for UEFI
# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB --removable
GRUB configuration
In configuration file /etc/default/grub change kernel parameters in GRUB_CMDLINE_LINUX_DEFAULT by adding parameter for LUKS:
# nano /etc/default/grub
The format of the new parameter is:
cryptdevice=UUID=<uuid>:devicemapper_name cryptkey=<path>
Where devicemapper_name is the device-mapper name given to the device after decryption, which will be available as /dev/mapper/devicemapper_name. cryptkey is optional.
dm-crypt device UUID can be found with:
# lsblk -f
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
loop0 squashfs 4.0
sr0 iso9660 Joliet Extension ARCH_202202 2022-02-01-17-06-09-00
vda
├─vda1 ext4 1.0 aex_boot c68779d4-96f2-4b17-9725-0e207d659ecb 1006.4M 6% /boot
└─vda2 crypto_LUKS 2 c7f63f65-2904-4f20-bfbb-db2c66cb6fb0
└─aex_system_luks
btrfs aes_system_btrfs 734cce8c-f3b9-4b2c-aaf5-effe0ec98c2e 26.6G 7% /.snapshots
/home
/
The said UUID in this example is c7f63f65-2904-4f20-bfbb-db2c66cb6fb0.
Example of GRUB configuration:
# GRUB boot loader configuration
GRUB_DEFAULT=0
GRUB_TIMEOUT=3
GRUB_DISTRIBUTOR="Arch"
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 cryptdevice=UUID=c7f63f65-2904-4f20-bfbb-db2c66cb6fb0:hostname_system_luks ip=dhcp netconf_timeout=10"
GRUB_CMDLINE_LINUX=""
CONFIG_BLK_DEV_INITRD=y
CONFIG_MICROCODE=y
CONFIG_MICROCODE_INTEL=y
CONFIG_MICROCODE_AMD=y
Install additional helpful packages:
\$ yay -S update-grub grub-reboot-poweroff grub-netboot-archlinux
Useful only with full-disk-encryption (/boot is also encrypted):
# pacman -S grub-btrfs
Add password protection to GRUB menu:
# grub-mkpasswd-pbkdf2
[...]
Your PBKDF2 is grub.pbkdf2.sha512.10000.C8ABD3E93C4DFC83138B0C7A3D719BC650E6234310DA069E6FDB0DD4156313DA3D0D9BFFC2846C21D5A2DDA515114CF6378F8A064C94198D0618E70D23717E82.509BFA8A4217EAD0B33C87432524C0B6B64B34FBAD22D3E6E6874D9B101996C5F98AB1746FE7C7199147ECF4ABD8661C222EEEDB7D14A843261FFF2C07B1269A
Add this hashed password and username into /etc/grub.d/40_custom, where password is the string generated by grub-mkpasswd_pbkdf2:
# nano /etc/grub.d/40_custom
set superusers="username"
password_pbkdf2 username password
Edit /etc/grub.d/10_linux and add --unrestricted to the CLASS. This will allow boot, but prevent changes. You can also add --unrestricted to other files such as /etc/grub.d/90_reboot or /etc/grub.d/91_poweroff.
# nano /etc/grub.d/10_linux
CLASS="--class gnu-linux --class gnu --class os --unrestricted"
Now generate main configuration file:
# grub-mkconfig -o /boot/grub/grub.cfg
Or use update-grub:
# update-grub
Configure initramfs
Normally the initramfs was already generated and would be ready to go, however because of system disk encryption there are some changes required.
Disable generating of fallback initramfs (I have never used it and it only takes up time):
# nano /etc/mkinitcpio.d/linux.preset
PRESETS=('default')
# rm /boot/initramfs-linux-fallback.img
Install few additional items:
\$ yay -S chkboot
# systemctl enable chkboot
# pacman -S mkinitcpio-netconf mkinitcpio-tinyssh mkinitcpio-utils
Add pacman hook to update chkboot hashes on update:
# nano /etc/pacman.d/hooks/chkboot.hook
[Trigger]
Operation = Upgrade
Type = Package
Target = *
[Action]
Description = Updating hashes of all files in /boot
Depends = chkboot
When = PostTransaction
Exec = /usr/bin/chkboot --update
AbortOnFail
Optionally also install mkinitcpio-numlock to enable numlock on boot:
\$ yay -S mkinitcpio-numlock
Edit /etc/mkinitcpio.conf:
# nano /etc/mkinitcpio.conf
Make following changes:
- Add support for
btrfsto enable use ofbtrfs-checkbyBINARIES=("/usr/bin/btrfs") - Add remote
dm-cryptunlock byHOOKS=(base udev autodetect modconf block netconf tinyssh encryptssh filesystems keyboard fsck) - Add early KMS start
amdgpufor AMDGPU, orradeonwhen using the legacy ATI driveri915for Intel graphicsnouveaufor the open-source Nouveau drivernvidia nvidia_modeset nvidia_uvm nvidia_drmfor nvidia driver. See NVIDIA#DRM kernel mode setting for details.mgag200for Matrox graphics- Depending on QEMU graphics in use (
qemuoption-vga typeorlibvirt <video><model type='type'>):bochsforstd(qemu) andvga/bochs(libvirt)virtio-gpuforvirtioqxlforqxlvmwgfxforvmware(qemu) andvmvga(libvirt)cirrusforcirrus
- Depending on VirtualBox graphics controller:
vmwgfxfor VMSVGAvboxvideofor VBoxVGA or VBoxSVGA
# vim:set ft=sh
# MODULES
# The following modules are loaded before any boot hooks are
# run. Advanced users may wish to specify all system modules
# in this array. For instance:
# MODULES=(piix ide_disk reiserfs)
MODULES=()
# BINARIES
# This setting includes any additional binaries a given user may
# wish into the CPIO image. This is run last, so it may be used to
# override the actual binaries included by a given hook
# BINARIES are dependency parsed, so you may safely ignore libraries
BINARIES=("/usr/bin/btrfs")
# FILES
# This setting is similar to BINARIES above, however, files are added
# as-is and are not parsed in any way. This is useful for config files.
FILES=()
# HOOKS
# This is the most important setting in this file. The HOOKS control the
# modules and scripts added to the image, and what happens at boot time.
# Order is important, and it is recommended that you do not change the
# order in which HOOKS are added. Run 'mkinitcpio -H <hook name>' for
# help on a given hook.
# 'base' is _required_ unless you know precisely what you are doing.
# 'udev' is _required_ in order to automatically load modules
# 'filesystems' is _required_ unless you specify your fs modules in MODULES
# Examples:
## This setup specifies all modules in the MODULES setting above.
## No raid, lvm2, or encrypted root is needed.
# HOOKS=(base)
#
## This setup will autodetect all modules for your system and should
## work as a sane default
# HOOKS=(base udev autodetect block filesystems)
#
## This setup will generate a 'full' image which supports most systems.
## No autodetection is done.
# HOOKS=(base udev block filesystems)
#
## This setup assembles a pata mdadm array with an encrypted root FS.
## Note: See 'mkinitcpio -H mdadm' for more information on raid devices.
# HOOKS=(base udev block mdadm encrypt filesystems)
#
## This setup loads an lvm2 volume group on a usb device.
# HOOKS=(base udev block lvm2 filesystems)
#
## NOTE: If you have /usr on a separate partition, you MUST include the
# usr, fsck and shutdown hooks.
HOOKS=(base udev autodetect modconf block netconf tinyssh encryptssh filesystems keyboard fsck)
# COMPRESSION
# Use this to compress the initramfs image. By default, zstd compression
# is used. Use 'cat' to create an uncompressed image.
#COMPRESSION="zstd"
#COMPRESSION="gzip"
#COMPRESSION="bzip2"
#COMPRESSION="lzma"
#COMPRESSION="xz"
#COMPRESSION="lzop"
#COMPRESSION="lz4"
# COMPRESSION_OPTIONS
# Additional options for the compressor
#COMPRESSION_OPTIONS=()
Copy public key of ed25519 key-pair into /etc/tinyssh/root_key (equivalent of ~/.ssh/authorized_keys) to allow remote ssh unlock. To generate one, you can use following command:
\$ ssh-keygen -t ed25519 -f "\${HOME}/.ssh/\${HOSTNAME}/\${HOSTNAME}_<TARGET>.key" -C "\${HOSTNAME} -> <TARGET>"
Generate tinyssh server keys:
# mkdir /etc/tinyssh/openssh_keys
# ssh-keygen -t ed25519 -f /etc/tinyssh/openssh_keys/\${HOSTNAME}.key -C "\${HOSTNAME} tinyssh key"
# tinyssh-convert /etc/tinyssh/sshkeydir < /etc/tinyssh/openssh_keys/\${HOSTNAME}.key
Generate new initramfs:
# mkinitcpio -P
Update GRUB:
# update-grub
Network configuration (optional)
Create systemd network configuration file:
# nano /etc/systemd/network/lan.network
[Match]
Name=e*
[Network]
DHCP=yes
Enable systemd-networkd:
# systemctl enable systemd-networkd
# systemctl enable systemd-resolved
Optionally also enable ssh service:
# systemctl enable sshd
Reboot
Exit chroot:
# exit
Optionally umount all in /mnt and close dm-crypt device:
# umount -R /mnt
# cryptsetup close hostname_system_luks
# reboot