Files
website-hadow.fr/_posts/2026-01-25-archlinux-uefi-lvm-on-luks-with-selinux-installation-guide.md
2026-02-16 10:53:49 +01:00

471 lines
14 KiB
Markdown

---
layout: post
author: Sam Hadow
tags: archlinux selinux sysadmin
---
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.
## preparing installation media
Follow the initial steps from archlinux [wiki](https://wiki.archlinux.org/title/Installation_guide) to prepare your installation media, the most convenient solution is to copy the ISO to a [ventoy](https://www.ventoy.net/en/index.html) USB key.
Then boot your archlinux ISO and first check you have internet access on your computer:
```bash
ping archlinux.org
```
If using WiFi, you'll need to use [iwctl](https://wiki.archlinux.org/title/Iwd#iwctl)
# installation steps
## 1) setting up LVM and preparing volumes
### 1.1) loading the required kernel modules
```bash
modprobe efivarfs
modprobe dm-crypt
modprobe dm-mod
```
### 1.2) partition table
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 disk
Write the partition table to the disk.
### 1.3) setting up the encrypted volume
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.)
```bash
cryptsetup luksFormat --type luks2 --pbkdf pbkdf2 /dev/vda2
```
Then open it with the passphrase chosen.
```bash
cryptsetup luksOpen /dev/vda2 luks
```
### 1.4) setting up LVM
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.
```bash
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
```
### 1.5) formating and mounting the volumes
I'll use a BTRFS filesystem but use the filesystem you prefer.
```bash
#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.
```bash
genfstab -pU /mnt >> /mnt/etc/fstab
```
## 2) base installation
### 2.1) installing required packages
We'll use reflector to generate the mirrorlist. Adapt the country code to pick the closest mirrors.
```bash
reflector -c FR -p https -a 12 --sort rate --save /etc/pacman.d/mirrorlist
```
Then install the required packages and some useful packages.
```bash
pacstrap -i /mnt base base-devel linux linux-firmware linux-headers pacman-contrib man-pages btrfs-progs vim git bash-completion
```
### 2.2) chroot
Chroot into the system to continue the installation.
```bash
arch-chroot /mnt /bin/bash
```
#### 2.2.1) locales, hostname, clock and timezone
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:
```ini
KEYMAP=us
FONT=lat9w-16
```
And in `/etc/locale.conf` (LC_COLLATE=C for case sensitive sorting):
```ini
LANG=en_US.UTF-8
LC_COLLATE=C
```
And finally generate the locales:
```bash
locale-gen
```
You can set your hostname in `/etc/hostname`.
For your clock and timezone: (adapt to your timezone)
```bash
ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime
hwclock --systohc --utc
```
#### 2.2.2) root password and user creation
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:
```bash
useradd -m -G wheel sam
passwd sam
```
and uncomment the line containing `%wheel ALL=(ALL:ALL) ALL` in `/etc/sudoers`
#### 2.2.3) SELinux installation (from AUR)
Log in as your created user and install an AUR helper to make things easier:
```bash
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.
```bash
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:
```bash
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:
```bash
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](https://bugs.archlinux.org/task/39767) 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:
```bash
yay -S selinux-alpm-hook dbus-selinux selinux-refpolicy-arch
```
If the check fails for `dbus-selinux` build it with `--nocheck` option:
```bash
cd ~/.cache/yay/dbus-selinux
makepkg -si --nocheck
```
If you want to apply a fedora-style user context (as root):
```bash
semanage login -m -s unconfined_u __default__
```
#### 2.2.4) necessary packages
Network for next boot:
```bash
pacman -S networkmanager
systemctl enable NetworkManager
```
grub and lvm2:
```bash
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*
```bash
pacman -S sbctl
sbctl create-keys
sbctl enroll-keys -m
```
##### linking TPM and LUKS key
Without this step, the computer is still vulnerable to evild maid attacks and a software keylogger could be installed by disabling the secure boot (clear CMOS for example). To make sure the encrypted device is linked to the TPM, we need to run the following command:
```bash
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 /dev/vda2
```
[systemd-cryptenroll](https://wiki.archlinux.org/title/Systemd-cryptenroll) also support hardware security keys like FIDO2 tokens, in this case you can refer to the archwiki for the command syntax.
systemd-boot is also the recommended option instead of using grub when linking the TPM and LUKS key, you can then only have a PIN and also be protected in more scenarios.
#### 2.2.5) opional packages
If you have an intel CPU:
```bash
pacman -S intel-ucode
```
If you have a laptop and want battery optimization:
```bash
pacman -S tlp
systemctl enable tlp
```
If you need bluetooth:
```bash
pacman -S bluez
systemctl enable bluetooth
```
If you want 32bits apps (necessary for some packages), in `/etc/pacman.conf` uncomment these lines:
```ini
#[multilib]
#include = /etc/pacman.d/mirrorlist
```
#### 2.2.5) generating boot images
in `/etc/mkinitcpio.conf` on the `MODULES=()` line, add `dm-mod` and in the `HOOKS=()` line you should have:
```ini
HOOKS=(base udev autodetect modconf block keyboard encrypt lvm2 filesystems fsck)
```
Then:
```bash
mkinitcpio -p linux
```
sbctl post-hook should automatically sign `/boot/vmlinuz-linux`. Otherwise run:
```bash
sbctl sign -s /boot/vmlinuz-linux
```
Then in `/etc/default/grub`
+ uncomment the line `GRUB_UNABLE_CRYPTODISK=y`
+ in `GRUB_CMDLINE_LINUX=""` add `cryptdevice=/dev/vda2:system root=/dev/mapper/system-root`
+ in `GRUB_CMDLINE_LINUX_DEFAULT=""` add `lsm=selinux,landlock,lockdown,yama,integrity,bpf`
Then:
```bash
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.*
### 3) exit and reboot
```bash
exit # until you exit the chroot
umount -R /mnt
swapoff -a
reboot
```
### 4) post reboot steps
#### 4.1) relabel files
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:
```bash
restorecon -RFv /
```
you might want to clean yay cache before doing that to have less files to relabel:
```bash
rm -rf ~/.cache/yay/*
```
#### 4.2) enable auditd
enable auditd service to read AVC denials from SELinux later.
```bash
systemctl enable --now auditd
```
#### 4.3) create and load necessary policy
Create a file `requiredmod.te` with the following content:
```text
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:
```bash
checkmodule -m -o requiredmod.mod requiredmod.te
semodule_package -o requiredmod.pp -m requiredmod.mod
semodule -i requiredmod.pp
```
And set a few booleans:
```bash
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.