Last modified: 2023-03-29
ArchLinux: Installation
Source: wiki.archlinux.org
This installation is mostly bare-bone installation, with following features:
- encrypted disk (
/boot
is not encrypted) - local and remote unlock o encrypted disk via ssh
- btrfs as file-system
doas
instead ofsudo
- micro-code updates
- secured
GRUB
with 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
btrfs
to enable use ofbtrfs-check
byBINARIES=("/usr/bin/btrfs")
- Add remote
dm-crypt
unlock byHOOKS=(base udev autodetect modconf block netconf tinyssh encryptssh filesystems keyboard fsck)
- Add early KMS start
amdgpu
for AMDGPU, orradeon
when using the legacy ATI driveri915
for Intel graphicsnouveau
for the open-source Nouveau drivernvidia nvidia_modeset nvidia_uvm nvidia_drm
for nvidia driver. See NVIDIA#DRM kernel mode setting for details.mgag200
for Matrox graphics- Depending on QEMU graphics in use (
qemu
option-vga type
orlibvirt <video><model type='type'>
):bochs
forstd
(qemu) andvga/bochs
(libvirt)virtio-gpu
forvirtio
qxl
forqxl
vmwgfx
forvmware
(qemu) andvmvga
(libvirt)cirrus
forcirrus
- Depending on VirtualBox graphics controller:
vmwgfx
for VMSVGAvboxvideo
for 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