--- 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.