Archlinux Root on Btrfs with LUKS encryption

by Maurice Zhou, posted in Linux

Caution

Requirements

Inside Live System

d=/dev/disk/by-id/nvme-PM951_NVMe_SAMSUNG_256GB__S29NNX0H516379
username='YOUR USERNAME'
hostname_target='YOUR HOSTNAME'
pw='YOUR DISK ENCRYPTION PASSWORD'
DISTRO='archlinux'
blkdiscard -f $d
sleep 3
sgdisk --zap-all $d # clear partition table
sgdisk -n1:0:256M $d # create 256MB EFI partition
sgdisk -n2:0:0 $d # create system partition with remaining space
sleep 2
partuuid=`blkid -s PARTUUID -o value $d-part2`
mapper_name=luks1-partuuid-$partuuid
mapper_path=/dev/mapper/$mapper_name
echo -n $pw | cryptsetup luksFormat --type luks1 $d-part2 -
echo -n $pw | cryptsetup open $d-part2 $mapper_name -
sleep 2
mkfs.vfat $d-part1
mkfs.btrfs $mapper_path
mount $mapper_path /mnt
# https://en.opensuse.org/SDB:BTRFS
cd /mnt
btrfs subvolume create $DISTRO
btrfs subvolume create $DISTRO/@
btrfs subvolume create $DISTRO/@/0
btrfs subvolume create $DISTRO/@home
btrfs subvolume create $DISTRO/@opt
btrfs subvolume create $DISTRO/@root
btrfs subvolume create $DISTRO/@srv
btrfs subvolume create $DISTRO/@tmp
btrfs subvolume create $DISTRO/@usr
btrfs subvolume create $DISTRO/@usr/local
btrfs subvolume create $DISTRO/@var
btrfs subvolume create $DISTRO/@swap
cd ~
umount /mnt
mount $mapper_path /mnt -o subvol=/$DISTRO/@/0
mkdir -p /mnt/.snapshots
mkdir -p /mnt/home
mkdir -p /mnt/opt
mkdir -p /mnt/root
mkdir -p /mnt/srv
mkdir -p /mnt/tmp
mkdir -p /mnt/usr/local
mkdir -p /mnt/var
mkdir -p /mnt/swap
mount $mapper_path /mnt/.snapshots/ -o subvol=$DISTRO/@,compress=zstd
mount $mapper_path /mnt/home/ -o subvol=$DISTRO/@home,compress=zstd
mount $mapper_path /mnt/opt/ -o subvol=$DISTRO/@opt,compress=zstd
mount $mapper_path /mnt/root/ -o subvol=$DISTRO/@root,compress=zstd
mount $mapper_path /mnt/srv/ -o subvol=$DISTRO/@srv,compress=zstd
#uncomment to use btrfs for /tmp. default to tmpfs mounted by systemd
#mount $mapper_path /mnt/tmp -o subvol=$DISTRO/@tmp,compress=zstd
mount $mapper_path /mnt/usr/local -o subvol=$DISTRO/@usr/local,compress=zstd
mount $mapper_path /mnt/var/ -o subvol=$DISTRO/@var,compress=zstd
mount $mapper_path /mnt/swap -o subvol=$DISTRO/@swap
mkdir -p /mnt/boot/efi
mount $d-part1 /mnt/boot/efi/
mkdir -p /mnt/lukskey
dd bs=512 count=8 if=/dev/urandom of=/mnt/lukskey/crypto_keyfile.bin
echo -n $pw | cryptsetup luksAddKey $d-part2 /mnt/lukskey/crypto_keyfile.bin -
pacstrap /mnt base linux-lts networkmanager gdisk arch-install-scripts debootstrap man nano sway swayidle swaylock grim wofi i3status kitty qt5ct qt5-wayland gnome-themes-extra pulseaudio pavucontrol-qt brightnessctl keepassxc firefox zathura lximage-qt pcmanfm-qt featherpad proxychains-ng youtube-dl hugo git btrfs-progs dosfstools cryptsetup cryptsetup snapper sudo mkinitcpio grub efibootmgr grub-btrfs nano firefox-dark-reader firefox-extension-https-everywhere firefox-umatrix gammastep mpv gvfs-smb gvfs-mtp libva-intel-driver xarchiver p7zip unzip arj lhasa lrzip lzip lzop ncompress unrar zip zathura-pdf-poppler kvantum-qt5 snap-pac thunderbird
pacstrap /mnt xorg-server-xwayland fcitx5  fcitx5-config-qt fcitx5-chinese-addons fcitx5-gtk
genfstab -U /mnt >> /mnt/etc/fstab
echo $hostname_target > /mnt/etc/hostname
cp /etc/pacman.d/mirrorlist /mnt/etc/pacman.d/mirrorlist 
arch-chroot /mnt /usr/bin/env d=$d username=$username DISTRO=$DISTRO bash --login 

Inside chroot

useradd -s /bin/bash -U -G wheel,video -m $username
# interactively set user password
passwd $username
partuuid=`blkid -s PARTUUID -o value $d-part2`
mapper_name=luks1-partuuid-$partuuid
mapper_path=/dev/mapper/$mapper_name
cryptkey=/lukskey/crypto_keyfile.bin
echo "$mapper_name PARTUUID=$partuuid $cryptkey luks,discard" >> /etc/crypttab
echo "GRUB_ENABLE_CRYPTODISK=y" >> /etc/default/grub
echo "GRUB_CMDLINE_LINUX=\"cryptdevice=PARTUUID=$partuuid:$mapper_name root=$mapper_path cryptkey=rootfs:$cryptkey\"" >> /etc/default/grub
chmod 600 /lukskey/*
pacman-key --init
pacman-key --populate
export EDITOR=nano
visudo
# Uncomment the following line to allow the new user to run sudo
#  %wheel ALL=(ALL) ALL
nano /etc/mkinitcpio.conf
# change these lines
# BINARIES=(/usr/bin/btrfs)
# FILES=(/lukskey/crypto_keyfile.bin)
# add "encrypt" hook
# HOOKS=(base udev autodetect modconf block encrypt filesystems keyboard fsck)
mkinitcpio -P
grub-install --bootloader-id=$DISTRO
grub-mkconfig -o /boot/grub/grub.cfg
systemctl enable NetworkManager.service
echo "HandlePowerKey=suspend" >> /etc/systemd/logind.conf
touch /swap/swapfile
truncate -s 0 /swap/swapfile
chattr +C /swap/swapfile
btrfs property set /swap/swapfile compression none
dd if=/dev/zero of=/swap/swapfile bs=1M count=8192 status=progress
chmod 600 /swap/swapfile
mkswap /swap/swapfile
swapon /swap/swapfile
echo "/swap/swapfile none swap defaults 0 0" >> /etc/fstab
# set timezone
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
hwclock --systohc

Unmount and reboot

exit
mount | grep '/mnt/' | tac | awk '/\/mnt/ {print $3}' |     xargs -i{} umount -lf {}
umount /mnt
cryptsetup close $mapper_name

Enable snapper after reboot

# After reboot
umount /.snapshots/
rmdir /.snapshots/
snapper -c root create-config /
rmdir /.snapshots/
mkdir /.snapshots/
mount /.snapshots/
snapper -c home create-config /home/
systemctl enable --now /lib/systemd/system/snapper-*
# edit /etc/snapper/configs/* to allow other users to access snapper

Install cronie to generate grub.cfg after every pacman transaction

/boot/grub/grub.cfg contains information about snapshots when grub-btrfs is installed, together with snap-pac you can pick a snapshot in GRUB menu to boot from should any update break your system.

mkdir -p /etc/pacman.d/hooks
nano /etc/pacman.conf
# uncomment `HookDir     = /etc/pacman.d/hooks/`
tee /etc/pacman.d/hooks/grub-add-snapshot.hook << EOF
[Trigger]
Operation = Upgrade
Operation = Install
Operation = Remove
Type = Package
Target = *

[Action]
Description = Regenerate grub.cfg for new snapshots
When = PostTransaction
Exec = /usr/bin/grub-mkconfig -o /boot/grub/grub.cfg
EOF

Rollback root filesystem

# snapper -c root list
  # | Type   | Pre # | Date                     | User | Cleanup  | Description                             | Userdata     
----+--------+-------+--------------------------+------+----------+-----------------------------------------+--------------
 0  | single |       |                          | root |          | current                                 |              
36  | single |       | Mon Nov 16 12:00:33 2020 | root | timeline | timeline                                |              
# snapper --ambit classic rollback 36
Ambit is classic
Creating read-only snapshot of current system. (Snapshot 55.)
Creating read-write snapshot of snapshot 56. (Snapshot 56.)
Setting default subvolume to snapshot 56.
# snapper -c root list
  # | Type   | Pre # | Date                     | User | Cleanup  | Description                             | Userdata     
----+--------+-------+--------------------------+------+----------+-----------------------------------------+--------------
 0  | single |       |                          | root |          | current                                 |              
36  | single |       | Mon Nov 16 12:00:33 2020 | root | timeline | timeline                                |                            
55  | single |       | Tue Nov 17 12:53:40 2020 | root | number   | rollback backup                         | important=yes
56* | single |       | Tue Nov 17 12:53:40 2020 | root |          | writable copy of #36                    |              
# grub-mkconfig -o /boot/grub/grub.cfg
## ... generate grub.cfg with new snapshots ...
## comment out the line about root filesystem in /etc/fstab
## UUID=fbe161e1-fcac-4995-a886-8daf2f654fdd	/         	btrfs     	rw,relatime,ssd,space_cache,subvolid=258,subvol=/archlinux/@/0,subvol=archlinux/@/0,compress=zstd	0 0
## reboot, select snapshot 56 within GRUB menu
## after reboot, regenerate grub.cfg and reinstall GRUB
# grub-mkconfig -o /boot/grub/grub.cfg
# grub-install

edited: