by Sam Hadow
This post is a guide to install archlinux in UEFI with full disk encryption (LVM on LUKS), SELinux enabled and optionally secure boot.
Warning: apparmor is much easier to use on archlinux, SELinux on archlinux will require you to write some policies manually. Fedora is a better solution if you want SELinux working out of the box and don’t want to fight SELinux writing policies. This guide is mostly for advanced users.
Follow the initial steps from archlinux wiki to prepare your installation media, the most convenient solution is to copy the ISO to a ventoy USB key.
Then boot your archlinux ISO and first check you have internet access on your computer:
ping archlinux.org
If using WiFi, you’ll need to use iwctl
modprobe efivarfs
modprobe dm-crypt
modprobe dm-mod
Find the disk you want to use to install your system with lsblk. For me it’s vda as I’m using a virtual machine to write this guide.
important: Select a GPT parition table type and not MBR.
Create a partition table with cfdisk /dev/vda (or your prefered tool) and create 2 partitions:
/dev/vda1 as ef EFI (FAT-12/16/32) type, a size of 512MB is fine/dev/vda2 as 83 Linux filesystem type for the rest of the diskWrite the partition table to the disk.
Create the encrypted volume, it will ask you for a confirmation and let you choose a passphrase. (Note that PBKDF2 key derivation algorithm is weaker than Argon2 but is required when using the default grub bootloader, the installation using systemd-boot will not be covered in this guide.)
cryptsetup luksFormat --type luks2 --pbkdf pbkdf2 /dev/vda2
Then open it with the passphrase chosen.
cryptsetup luksOpen /dev/vda2 luks
We will create 3 volumes, one for the SWAP, one for the ROOT filesystem and one for the HOME filesystem. Adapt the sizes to what is appropriate for you, in my case I’m using a virtual machine with a 25GB disk so I won’t create a huge root volume.
pvcreate /dev/mapper/luks
vgcreate system /dev/mapper/luks
lvcreate -L 1G -n swap system
lvcreate -L 10G -n root system
lvcreate -l 100%FREE -n home system
I’ll use a BTRFS filesystem but use the filesystem you prefer.
#formatting
mkfs.fat -F32 /dev/vda1
fatlabel /dev/vda1 BOOT
mkswap -L SWAP /dev/mapper/system-swap
mkfs.btrfs -L ROOT /dev/mapper/system-root
mkfs.btrfs -L HOME /dev/mapper/system-home
#mounting
swapon /dev/mapper/system-swap
mount -o compress=zstd /dev/mapper/system-root /mnt
mkdir -p /mnt/{home,boot,boot/efi,etc}
mount -o compress=zstd /dev/mapper/system-home /mnt/home
mount /dev/vda1 /mnt/boot/efi
Then create the fstab.
genfstab -pU /mnt >> /mnt/etc/fstab
We’ll use reflector to generate the mirrorlist. Adapt the country code to pick the closest mirrors.
reflector -c FR -p https -a 12 --sort rate --save /etc/pacman.d/mirrorlist
Then install the required packages and some useful packages.
pacstrap -i /mnt base base-devel linux linux-firmware linux-headers pacman-contrib man-pages btrfs-progs vim git bash-completion
Chroot into the system to continue the installation.
arch-chroot /mnt /bin/bash
In the file /etc/locale.gen, uncomment your locales (for example en_US.UTF-8 UTF-8)
You can also set your preferences for the virtual console in /etc/vconsole.conf, for example for a QWERTY layout:
KEYMAP=us
FONT=lat9w-16
And in /etc/locale.conf (LC_COLLATE=C for case sensitive sorting):
LANG=en_US.UTF-8
LC_COLLATE=C
And finally generate the locales:
locale-gen
You can set your hostname in /etc/hostname.
For your clock and timezone: (adapt to your timezone)
ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime
hwclock --systohc --utc
Set your root password with passwd
Then create a user (named sam here) in the wheel group (to use sudo) and set its password with:
useradd -m -G wheel sam
passwd sam
and uncomment the line containing %wheel ALL=(ALL:ALL) ALL in /etc/sudoers
Log in as your created user and install an AUR helper to make things easier:
su sam
cd
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
cd
Then install the required packages for SELinux to work. It will ask you for replacements with conflicting packages, answer yes every time.
yay -S libsepol libselinux checkpolicy secilc setools libsemanage semodule-utils policycoreutils selinux-python python-ipy mcstrans restorecond
yay -S pam-selinux pambase-selinux coreutils-selinux findutils-selinux iproute2-selinux logrotate-selinux openssh-selinux psmisc-selinux shadow-selinux cronie-selinux
yay -S sudo-selinux
Note: If sudo-selinux fails to build, build it with --nocheck option:
cd ~/.cache/yay/sudo-selinux
makepkg -si --nocheck
Then exit your user, remodify /etc/sudoers to uncomment the line containing %wheel ALL=(ALL:ALL) ALL and relog as your user. After that:
sudo pacman -S less
yay -S systemd-selinux systemd-libs-selinux util-linux-selinux util-linux-libs-selinux
cd ~/.cache/yay/systemd-selinux
makepkg -si --nocheck
Due to a cyclic dependency which won’t be fixed the complete build will fail at first, that’s why you need to manually build systemd-selinux again after. For some reason less is also required for the build but not a build dependency.
Then more SELinux packages and a policy:
yay -S selinux-alpm-hook dbus-selinux selinux-refpolicy-arch
If the check fails for dbus-selinux build it with --nocheck option:
cd ~/.cache/yay/dbus-selinux
makepkg -si --nocheck
If you want to apply a fedora-style user context (as root):
semanage login -m -s unconfined_u __default__
Network for next boot:
pacman -S networkmanager
systemctl enable NetworkManager
grub and lvm2:
pacman -S grub efibootmgr lvm2
Secure boot:
Note: If you don’t want to bother with secure boot, do not run sbctl related command, and later when generating the boot image with grub, remove --modules="tpm" --disable-shim-lock from the command
pacman -S sbctl
sbctl create-keys
sbctl enroll-keys -m
If you also want to enroll hardware security keys, use systemd-cryptenroll
If you have an intel CPU:
pacman -S intel-ucode
If you have a laptop and want battery optimization:
pacman -S tlp
systemctl enable tlp
If you need bluetooth:
pacman -S bluez
systemctl enable bluetooth
If you want 32bits apps (necessary for some packages), in /etc/pacman.conf uncomment these lines:
#[multilib]
#include = /etc/pacman.d/mirrorlist
in /etc/mkinitcpio.conf on the MODULES=() line, add dm-mod and in the HOOKS=() line you should have:
HOOKS=(base udev autodetect modconf block keyboard encrypt lvm2 filesystems fsck)
Then:
mkinitcpio -p linux
sbctl post-hook should automatically sign /boot/vmlinuz-linux. Otherwise run:
sbctl sign -s /boot/vmlinuz-linux
Then in /etc/default/grub
GRUB_UNABLE_CRYPTODISK=yGRUB_CMDLINE_LINUX="" add cryptdevice=/dev/vda2:system root=/dev/mapper/system-rootGRUB_CMDLINE_LINUX_DEFAULT="" add lsm=selinux,landlock,lockdown,yama,integrity,bpfThen:
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=arch_grub --modules="tpm" --disable-shim-lock --recheck
grub-mkconfig -o /boot/grub/grub.cfg
sbctl sign -s /boot/efi/EFI/arch_grub/grubx64.efi
Please note that in a virtual machine you’ll need an emulated TPM for the virtual machine to boot if you want secure boot enabled.
exit # until you exit the chroot
umount -R /mnt
swapoff -a
reboot
check SELinux status with sestatus, it should be in permissive with refpolicy-arch as the loaded policy.
Relabel all the files correctly with the following command:
restorecon -RFv /
you might want to clean yay cache before doing that to have less files to relabel:
rm -rf ~/.cache/yay/*
enable auditd service to read AVC denials from SELinux later.
systemctl enable --now auditd
Create a file requiredmod.te with the following content:
module requiredmod 1.0;
require {
type auditd_etc_t;
type getty_t;
type var_run_t;
type tmpfs_t;
type local_login_t;
type systemd_tmpfiles_t;
type init_runtime_t;
type devpts_t;
type kernel_t;
type device_t;
type udev_t;
type hugetlbfs_t;
type udev_tbl_t;
type policy_config_t;
type tmp_t;
type unconfined_t;
type var_lib_t;
type systemd_userdbd_runtime_t;
type systemd_user_runtime_dir_t;
type systemd_sessions_t;
type systemd_userdbd_t;
type etc_runtime_t;
type systemd_logind_t;
type file_context_t;
type semanage_t;
type selinux_config_t;
type initrc_runtime_t;
type sshd_t;
class dir { write add_name remove_name getattr open read search };
class file { getattr open read write create getattr ioctl lock relabelfrom relabelto setattr unlink };
class sock_file write;
class unix_stream_socket { read write ioctl connectto};
class capability2 block_suspend;
class filesystem { associate quotaget quotamod };
class key { link search };
class process { noatsecure rlimitinh siginh transition };
}
#============= getty_t ==============
allow getty_t tmpfs_t:dir { getattr open read };
allow getty_t var_run_t:file { getattr open read };
allow getty_t initrc_runtime_t:dir { getattr open read };
#============= local_login_t ==============
allow local_login_t init_runtime_t:sock_file write;
allow local_login_t systemd_logind_t:unix_stream_socket connectto;
allow local_login_t var_lib_t:dir { add_name remove_name };
allow local_login_t var_lib_t:file { create getattr lock open read setattr unlink write };
#============= sshd_t ==============
allow sshd_t local_login_t:key { link search };
allow sshd_t systemd_logind_t:unix_stream_socket connectto;
allow sshd_t unconfined_t:process { noatsecure rlimitinh siginh };
#============= systemd_tmpfiles_t ==============
allow systemd_tmpfiles_t auditd_etc_t:dir search;
allow systemd_tmpfiles_t auditd_etc_t:file getattr;
#============= systemd_sessions_t ==============
allow systemd_sessions_t kernel_t:dir search;
allow systemd_sessions_t kernel_t:file { getattr ioctl open read };
#============= systemd_user_runtime_dir_t ==============
allow systemd_user_runtime_dir_t etc_runtime_t:file { open read };
allow systemd_user_runtime_dir_t kernel_t:dir search;
allow systemd_user_runtime_dir_t kernel_t:file { getattr ioctl open read };
allow systemd_user_runtime_dir_t systemd_userdbd_runtime_t:sock_file write;
allow systemd_user_runtime_dir_t systemd_userdbd_t:unix_stream_socket connectto;
allow systemd_user_runtime_dir_t tmp_t:dir read;
allow systemd_user_runtime_dir_t tmpfs_t:filesystem { quotaget quotamod };
#============= systemd_userdbd_t ==============
allow systemd_userdbd_t initrc_runtime_t:dir { getattr open read search };
#============= devpts_t ==============
allow devpts_t device_t:filesystem associate;
#============= hugetlbfs_t ==============
allow hugetlbfs_t device_t:filesystem associate;
#============= kernel_t ==============
allow kernel_t self:capability2 block_suspend;
#============= tmpfs_t ==============
allow tmpfs_t device_t:filesystem associate;
#============= udev_t ==============
allow udev_t kernel_t:unix_stream_socket { read write ioctl };
allow udev_t udev_tbl_t:dir { write add_name };
allow udev_t var_run_t:sock_file write;
Run the following command to compile and load the module:
checkmodule -m -o requiredmod.mod requiredmod.te
semodule_package -o requiredmod.pp -m requiredmod.mod
semodule -i requiredmod.pp
And set a few booleans:
setsebool -P allow_polyinstantiation on
setsebool -P systemd_tmpfiles_manage_all on
setsebool -P ssh_sysadm_login on
Then in /etc/selinux/config, set SELinux mode to enforcing instead of permissive and reboot.
And that’s it, you now have a minimal archlinux installation in UEFI with full disk encryption, secure boot, and SELinux in enforcing mode.