Files
website-hadow.fr/_posts/2026-01-25-archlinux-uefi-lvm-on-luks-with-selinux-installation-guide.md

462 lines
13 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
```
If you also want to enroll hardware security keys, use [systemd-cryptenroll](https://wiki.archlinux.org/title/Systemd-cryptenroll)
#### 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.