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