Arch Linux - Install Encrypted Guide
The idea of this guide is to achieve the following:
- Full disk encryption, except for the ESP which obviously cannot be encrypted.
- BTRFS partitions for benefits such as compression and snapshots.
- Multiple disks
- Root partition on the ssd
- Home partition on the hdd
- Adding boot images to secure boot
- While also enabling hibernation support
This will be an exercise in how to set this up. Apparently there may be issues setting BTRFS on a LUKS2 encrypted partition. We shall see. grub currently does not have proper LUKS2, currently waiting for the patches to be merged. Until then root will use LUKS1 as it is supported.
I thought about exploring doing Secure boot and/or encrypted boot to make sure that the PC was as secure as possible.
This should allow for a pretty fancy and bulletproof computer, aside from the vulnerability present in all modern computers of course.
Summary of security on a computer
- Legacy BIOS became has limitations that were sought to be addressed by UEFI.
- UEFI has a feature written into the standard called Secure Boot.
- Secure Boot only allows a list cryptographically signed binaries to be booted.
- This ensures that boot images are not maliciously compromised such as with the Evil Maid Attack where a unattended PC is tampered with.
- This should be used in conjunction with encryption as encryption alone does not protect a device entirely. For instance if malicious code is put into the boot image such as a keylogger when requesting the passphrase for the encrypted drive.
Steps in this process from a high level
This tutorial is based on: https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#Btrfs_subvolumes_with_swap
It has been modified to use a swap file instead of a swap partition and to allow for multiple disks, among some other minor adjustments for my preferences.
Configure wifi
Use the iwctl tool:
iwctl
# You will be presented with the following prompt:
[iwd]#
# To get name of wireless adapter
station list
# Scan for networks
station <adapter e.g. wlan0> scan
station wlan0 get-networks
# Connect to network
station wlan0 connect <network name>
# Exit back to shell prompt
exit
# Confirm connection
ping -c 5 archlinux.org
Connect over ssh
If wanting to install from another system.
# Set root password and reload ssh daemon
passwd
systemctl reload sshd
# Get ip address
ip add
On the other system ssh in normally.
Wipe drives
This step is only necessary if the drive wasn’t encrypted before. If it was encrypted, it would be indistinguishable from garbage data anyway. If the drives are over a terabyte it may take several hours.
shred -v -n 1 /dev/sda
shred -v -n 1 /dev/sdb
Partition the disks
Recommended to use gdisk
, however it is difficult if reorganising an existing
partition.
cfdisk
is a nice TUI tool that makes it easier although is not GPT aware as
far as I know.
After formatting may need to reboot before the changes are recognised by the
system.
My installation refused to do the luksFormat step because it believed the
partitions were still in use.
Disk layout
For SSD which is /dev/sdb
:
- /efi - 512 MB
- / - Rest of the disk, which will include /boot as well so that it is encrypted
For HDD which is /dev/sda
:
- /home - Use full disk
Encryption of devices
# Encrypt drives
cryptsetup luksFormat --type luks1 /dev/sda1 # HDD used for home partition
# Confirm
# Passphrase
# Passphrase again
cryptsetup luksFormat --type luks1 /dev/sdb2 # SSD root partition
# Generate a 2048 byte key file to be used to encrypt the data
dd bs=512 count=4 if=/dev/random of=/tmp/key.bin iflag=fullblock
chmod 600 /tmp/key.bin # So only root can read it
# Add keyfile
cryptsetup luksAddKey /dev/sda1 /tmp/key.bin
cryptsetup luksAddKey /dev/sdb2 /tmp/key.bin
# Verify the LUKS header
# If a key was added there should be two keys in the keyslots
cryptsetup luksDump /dev/sda1
cryptsetup luksDump /dev/sdb2
Unlock LUKS containers
Decrypt the drives and make it accessible.
# --allow-discards is for SSD devices
cryptsetup luksOpen /dev/sdb2 root --key-file /tmp/key.bin --allow-discards
cryptsetup luksOpen /dev/sda1 home --key-file /tmp/key.bin
The decrypted drives will now be accessible through
/dev/mapper/root
and /dev/mapper/home
.
Note: When using an SSD the cryptsetup luksOpen
command should have the
option --allow-discards
to allow TRIM support.
Formatting the decrypted drives
mkfs.vfat -F32 /dev/sda1 # unencrypted ESP
mkfs.btrfs -L root /dev/mapper/root
mkfs.btrfs -L home /dev/mapper/home
Check filesystem formats
lsblk -f
Create btrfs sub-volumes
# Mount the partition so that it can be used
mount /dev/mapper/root /mnt
# Create the sub-volumes
btrfs subvolume create /mnt/{@,@var,@snapshots,@swap}
# Unmount the partition so that the sub-volumes may now be mounted instead
umount /mnt
# Do the same for the device containing home
mount /dev/mapper/home /mnt
btrfs sub-volume create /mnt/@home
umount /mnt
Mount filesystem
# Mount the sub-volumes with options specified, also create folders for sub-volumes to be mounted to
mount -o noatime,compress=zstd,ssd,discard=async,space_cache=v2,subvol=/@ /dev/mapper/root /mnt
mkdir -p /mnt/{efi,boot,home,var,swap,.snapshots}
mkdir -p /mnt/.snapshots
mount -o noatime,compress=zstd,ssd,discard=async,space_cache=v2,subvol=/@var /dev/mapper/root /mnt/var
mount -o noatime,compress=zstd,ssd,discard=async,space_cache=v2,subvol=/@snapshots /dev/mapper/root /mnt/.snapshots
mount -o noatime,nodatacow,compress=no,ssd,discard=async,space_cache=v2,subvol=/@swap /dev/mapper/root /mnt/swap
mount -o noatime,compress=zstd,ssd,discard=async,space_cache=v2,subvol=/@home /dev/mapper/home /mnt/home
# Mount ESP
mount /dev/sda1 /mnt/efi
For more information on the options see here.
Copy key file to root partition
Copy the key file generated in the encryption step.
The root directory will be used to hold the key used to encrypt the drive.
According to here
/crypto_keyfile.bin
is the default place that the encrypt kernel hook uses.
cp /tmp/key.bin /mnt/crypto_keyfile.bin
Pacstrap the installation
Use the following command to install base packages to the system:
pacstrap /mnt base base-devel linux linux-firmware linux-headers grub efibootmgr intel-ucode btrfs-progs os-prober reflector net-tools networkmanager wpa_supplicant dialog neovim xdg-utils xdg-user-dirs man-db bc git curl openssh
Generating the fstab
The fstab file defines how disk partitions and other devices should be mounted to the system. To generate one for the mounted device structure:
genfstab -U /mnt >> /mnt/etc/fstab
Check the fstab to see if it set the mount options correctly.
Add home partition to crypttab
Similar to fstab which sets up the mounted partitions, crypttab decrypts devices at boot up and runs before fstab. This needs to be done for the home partition as it is not decrypted automatically. Since root is decrypted automatically due to the encrypt hook in the initramfs it does not need to be added.
Grab the UUID of the LUKS partition using this handy command, might need to adjust it for you case:
blkid | grep sda1 | cut -d\" -f 2
Edit /etc/crypttab
and add the partition UUID like so:
# <name> <device> <password> <options>
home UUID=6ac7f2de-d94a-4686-baa0-9d10f289f690 /crypto_keyfile.bin
Chroot into the new system
This step will quickly go through the initial setup steps from the new system:
arch-chroot /mnt
timedatectl list-timezones # to find your timezone
ln -sf /usr/share/zoneinfo/REGION/CITY /etc/localtime
hwclock --systohc
nvim /etc/locale.gen # Uncomment en_US.UTF-8
locale-gen # Generate locales
nvim /etc/locale.conf # Add LANG=en_US.UTF-8
nvim /etc/hostname # Set to arch-computer or whatever
nvim /etc/hosts # Add the following below:
# Static table lookup for hostnames.
# See hosts(5) for details.
127.0.0.1 localhost
::1 localhost
127.0.1.1 arch-computer.localdomain arch-computer
Edit mkinitcpio.conf
Configure initramfs with the following options by editing /etc/mkinitcpio.conf
:
- add to MODULES, btrfs (if you have an intel processor)
- add to BINARIES, btrfs - for recovery options, if it fails boot the
btrfs check
tool will be available in the command line that is
presented
- add to FILES, /crypto_keyfile.bin
- add to HOOKS, udev, keymap, consolefont and encrypt
e.g. HOOKS=(base udev autodetect modconf kms keyboard keymap consolefont block encrypt lvm2 filesystems fsck)
Then regenerate with mkinitcpio -P
or mkinitcpio -p linux
.
Installing GRUB
Apparently there are issues with GRUB and LUKS2 support: https://wiki.archlinux.org/title/GRUB#Encrypted_/boot
Enable crypto disk option in GRUB config
Add the following options in /etc/default/grub
:
GRUB_ENABLE_CRYPTODISK=y
GRUB_DISABLE_OS_PROBER=false
Grab the UUID of the encrypted root using lsblk -f
.
Edit the GRUB_CMDLINE_LINUX_DEFAULT
variable and add the following kernel
parameters, (note: each newline should be a space, it is a newline for
readability):
mem_sleep_default=deep
cryptdevice=UUID=b35bae6b-8c18-452d-9990-b4d4cf68fd38:root:allow-discards
root=/dev/mapper/root
cryptkey=rootfs:/crypto_keyfile.bin
Note: the cryptkey option doesn’t need to be specified as it by default looks for this file but it’s better to be explicit rather than implicit.
Install GRUB
grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=GRUB
Generate GRUB config
grub-mkconfig -o /boot/grub/grub.cfg
Enable systemd services
systemctl enable NetworkManager
systemctl enable fstrim.timer
# Configure reflector to your liking
nvim /etc/xdg/reflector/reflector.conf
systemctl enable reflector.timer
# To trigger reflector to pick the closest mirrors manually
systemctl start reflector
Set a root password
passwd
Unmount and Reboot
# exit chroot
exit
umount -R /mnt
reboot
Reboot and a password screen should be presented.
Brand new system
With luck everything should work and you will be presented with a login prompt.
If it hangs at Starting systemd-udevd version...
, it most likely means that
the home partition is not available as was the case with me the first time.
This means that crypttab
needs to be configured to decrypt the device holding
the home partition.
Fix “watchdog did not stop!” message
Edit /etc/systemd/system.conf
, set the following variable
RebootWatchdogSec=0
Create user
useradd -m USERNAME
usermod -aG wheel USERNAME
usermod -aG rfkill USERNAME
chfn -f "Full Name" USERNAME
passwd USERNAME
Allow superuser privileges
Edit /etc/sudoers
with the visudo
command, vim
or nano
will need to be
installed, and uncomment the line at the bottom beginning with %wheel.
Logout and login as new user
This will prove the new user account is working as intended.
Also try a command with sudo
like sudo ls
just to make sure it’s working.
Disable root login
Once a user account with super user privileges is created the root account is no longer necessary and presents a security risk.
passwd --lock root
Setup AUR package manager
cd /tmp
git clone https://aur.archlinux.org/paru.git
cd paru
makepkg -si
Install desktop environment
Install whatever desktop environment you like, this will install cinnamon:
sudo pacman -Syu cinnamon xorg-xinit xorg-xrandr arandr xreader firefox lightdm lightdm-slick-greeter nvidia nvidia-settings nvidia-utils pipewire pipewire-jack wireplumber noto-fonts noto-fonts-cjk noto-fonts-emoji gnome-keyring mtpfs gvfs gvfs-mtp ntfs-3g bluez bluez-utils blueberry arc-gtk-theme arc-icon-theme gnome-multi-writer grub-btrfs galculator archlinux-wallpaper mpv vlc breeze kolourpaint rsync libreoffice-fresh cronie gnome-system-monitor vim unzip htop
paru -S xviewer gnome-terminal-transparency gnome-icon-theme nerd-fonts-fantasque-sans-mono
Fix for cinnamon
mkdir -p $HOME/.cache/cs_themes/ # Fix for cinnamon themes application crashing
paru -S gnome-icon-theme # Fix for missing start menu icons
Configure NVIDIA drivers
Run the following python script to configure it automatically:
paru -S envycontrol
sudo envycontrol --switch nvidia --dm lightdm
Or follow this guide: https://www.youtube.com/watch?v=Pn2iUgW3l6w
Encrypted Swap File
Creating the swapfile
Enter the following commands to create the swap:
sudo su # Become root it you are not
cd /swap
touch ./swapfile # Create the swapfile
# Disable CoW, this should already be disabled in fstab
chattr +C ./swapfile
# Disable compression, again, this already should be disabled in fstab
# This line will fail if set in fstab
btrfs property set ./swapfile compression none
# Create swapfile same size as RAM to allow hibernation
dd if=/dev/zero of=./swapfile bs=1M count=8192 status=progress
chmod 600 ./swapfile # Only root should be able to read/write swap
mkswap ./swapfile
swapon /swap/swapfile # use swap in current session
Adding swap to fstab
Now add the following to /etc/fstab
:
/swap/swapfile none swap defaults 0 0
This will configure the system to use the swapfile as swap on next boot.
Configuring hibernate
Editing mkinitcpio.conf
The system needs to know where to load the existing session that was saved to
disk during the last hibernation.
First the system needs to know that this is possible by having the appropriate
system hook.
To do so edit mkinitcpio.conf
and add resume
after filesystems
and lvm2
in the HOOKS section:
HOOKS=(... encrypt filesystems resume ...)
Then regenerate the initramfs with mkinitcpio -P
.
Calculating the offset on disk to load the swap file
The resume
hook now needs to be configured with where the location of the
system swap where the last session was written to disk.
This section is incredibly jank but is the recommended way to do this: https://wiki.archlinux.org/title/Power_management/Suspend_and_hibernate#Hibernation_into_swap_file_on_Btrfs I assume this is because there is no way in the resume variable of grub to point to a btrfs sub-volume or a file within the sub-volume, and so a memory offset to its location on disk is required.
While this seems risky it should be fine as the swapfile location shouldn’t change as CoW is disabled, meaning that it won’t shift around on disk.
Perform the following:
cd /tmp
curl -o btrfs_map_physical.c https://raw.githubusercontent.com/osandov/osandov-linux/master/scripts/btrfs_map_physical.c
gcc -O2 -o btrfs_map_physical btrfs_map_physical.c
# Need to grab the PHYSICAL OFFSET of the swapfile on disk
sudo ./btrfs_map_physical /swap/swapfile | cut -f 9 | head -2 | tail -1
getconf PAGESIZE
# Divide physical offset of swapfile by page size to get resume_offset value
echo "6773809152 / 4096" | bc
# 1653762
# Find UUID of device containing the swapfile, in this case the
# /dev/mapper/root device
lsblk -f | grep root
# Edit /etc/default/grub and add resume and resume_offset values to the
# GRUB_CMDLINE_LINUX_DEFAULT variable.
# e.g. resume=UUID=a8c33cc9-ec0f-4afb-be27-9080195b9b01
# e.g. resume_offset=1653762
sudo nvim /etc/default/grub
# Regenerate grub config
sudo grub-mkconfig -o /boot/grub/grub.cfg
Hibernate functionality will now work on next boot as the swapfile is now configured with the resume hook of the initramfs.
Configure snapper
Snapper is a tool for managing btrfs snapshots. So if something in the system gets corrupted it can be reverted. There are also a number of other packages that provide a GUI interface and also automatically create a snapshot pre and post system upgrades.
sudo pacman -S snapper
sudo systemctl enable snapper-timeline.timer # Enable automatic snapshots
sudo systemctl enable snapper-cleanup.timer
sudo umount /.snapshots # Need to delete .snapshot folder that was made
sudo rm -rf /.snapshots # as snapper creates it and errors if it exists
sudo snapper -c root create-config /
sudo chmod a+rx /.snapshots # Configure permissions
sudo mount -a # Remount everything is fstab
Edit the root configuration file that was just created and change the
following:
- Add your user to the ALLOW_USERS
variable
- Change the timeline variables to your preference
Then reboot.
sudo nvim /etc/snapper/configs/root
sudo reboot
Add additional snapper packages:
- grub-btrfs - adds support for selecting snapshots at the grub menu
- snap-pac and snap-pac-grub - Automatically creates pre and post upgrade snapshots of system, additionally updates grub entries
- snapper-gui - GUI frontend for snapper
paru -S snap-pac snap-pac-grub grub-btrfs snapper-gui
Done
With snapper now configured, the system should now be a fully disk encrypted device using btrfs with compression and snapshots.
Have fun!