Skip to content

Instantly share code, notes, and snippets.

@tobi-wan-kenobi
Last active April 17, 2024 06:57
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save tobi-wan-kenobi/bff3af81eac27e210e1dc88ba660596e to your computer and use it in GitHub Desktop.
Save tobi-wan-kenobi/bff3af81eac27e210e1dc88ba660596e to your computer and use it in GitHub Desktop.
Void Linux installation (NVMe, btrfs, LVM, full disk encryption using LUKS, 2FA-ish, SSD TRIM)

Void Linux installation (NVMe, btrfs, LVM, full disk encryption using LUKS, 2FA-ish, SSD TRIM)

Here's a record of my experiences when setting up Void Linux for the first time, maybe it contains useful information for somebody :-)

Basics

  • Laptop: Lenovo IdeaPad S340
  • Void Linux installer version: 20191109 (x86_64 musl)

Features

This guide explains how to set up Void Linux:

  • On an NVMe disk
  • Using full disk encryption - including /boot, with LUKS + LVM
  • Uses btrfs as filesystem
  • Offers a poor-man's 2FA using Yubikey's static password feature

Important notes

  • SSD/NVMe trimming only works if it is enabled on all intermediate layers, which in this case means LUKS, LVM and btrfs
  • What I call "2FA" is not really 2FA, but rather a simplistic way to get "something you know" (a passphrase) and "something you have" (a Yubikey token, in my case). Yubikey has a feature to emit a static password upon pressing a button on the token. This is modeled as a USB keyboard, so it supports a wide range of hardware. The approach is to make the LUKS passphrase composed of two pieces: First, a password you have to remember, followed directly by the passphrase generated by the token.

The process

Pre-chroot

# boot Void live system and log in using root:voidlinux

loadkeys de

cp /etc/wpa_supplicant/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant-<interface>.conf
wpa_passphrase <SSID> <password> >> /etc/wpa_supplicant/wpa_supplicant-<interface>.conf
wpa_supplicant -B -i <interface> -c /etc/wpa_supplicant/wpa_supplicant-<interface>.conf

fdisk /dev/nvme0n1
# g to create a new GTP partition ta
# n new partition with +200M
# t 1 to set partition type to EFI
# n new partition with remaining space

# set up encryption
# here's the 2FA "trick":
# prepare a YubiKey that emits a *static* passphrase
# when pressing its touch button (best use the maximum
# passphrase length offered - 64 characters at the time
# of writing).
# when asked for the passphrase, *first* enter a
# password you need to *know*, then press the button
# on the Yubikey - voila, you have a passphrase requiring
# something you know and something to have
# not sufficient for government-grade security, maybe, but
# for my humble home laptop, it's OK
cryptsetup luksFormat --type=luks1 /dev/nvme0n1p2
cryptsetup open /dev/nvme0n1p2 crypt

# prepare LVM
vgcreate vg0 /dev/mapper/crypt
lvcreate --name swap -L 25G vg0
lvcreate --name void -l +100%FREE vg0

# filesystems
mkfs.vfat -n BOOT -F 32 /dev/nvme0n1p1
mkswap /dev/mapper/vg0-swap
mkfs.btrfs -L void /dev/mapper/vg0-void

mount -o rw,noatime,ssd,compress=lzo,space_cache,commit=60 /dev/mapper/vg0-void /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
btrfs subvolume create /mnt/@snapshots
umount /mnt
mount -o rw,noatime,ssd,compress=lzo,space_cache,commit=60,subvol=@ /dev/mapper/vg0-void /mnt
mkdir /mnt/home
mkdir /mnt/.snapshots
mount -o rw,noatime,ssd,compress=lzo,space_cache,commit=60,subvol=@home /dev/mapper/vg0-void /mnt/home/
mount -o rw,noatime,ssd,compress=lzo,space_cache,commit=60,subvol=@snapshots /dev/mapper/vg0-void /mnt/.snapshots/
mkdir -p /mnt/boot/efi
mount -o rw,noatime /dev/nvme0n1p1 /mnt/boot/efi/
mkdir -p /mnt/var/cache
btrfs subvolume create /mnt/var/cache/xbps
btrfs subvolume create /mnt/var/tmp
btrfs subvolume create /mnt/srv
export XBPS_ARCH=x86_64-musl
xbps-install -Sy -R https://alpha.de.repo.voidlinux.org/current/musl -r /mnt base-system btrfs-progs cryptsetup grub-x86_64-efi lvm2
mount -t proc proc /mnt/proc/
mount -t sysfs sys /mnt/sys/
mount -o bind /dev /mnt/dev
mount -t devpts pts /mnt/dev/pts
cp -L /etc/resolv.conf /mnt/etc/
cp -L /etc/wpa_supplicant/wpa_supplicant-<interface>.conf /mnt/etc/wpa_supplicant/
chroot /mnt /bin/bash

Post-chroot

passwd root
chown root:root /
chmod 755 /
echo <hostname> > /etc/hostname
cat <<EOF > /etc/rc.rc.conf
# /etc/rc.conf - system configuration for void

HOSTNAME="<hostname>"
HARDWARECLOCK="UTC"
TIMEZONE="Europe/Amsterdam"
KEYMAP="de"
EOF
echo 'en_US.UTF-8 UTF-8' > /etc/default/libc-locales
echo LANG=en_US.UTF-8 > /etc/locale.conf
export UEFI_UUID=$(blkid -s UUID -o value /dev/nvme0n1p1)
export LUKS_UUID=$(blkid -s UUID -o value /dev/nvme0n1p2)
export ROOT_UUID=$(blkid -s UUID -o value /dev/mapper/vg0-void)
export SWAP_UUID=$(blkid -s UUID -o value /dev/mapper/vg0-swap)
cat <<EOF > /etc/fstab
UUID=$ROOT_UUID / btrfs rw,noatime,ssd,compress=lzo,space_cache,commit=60,subvol=@ 0 1
UUID=$ROOT_UUID /home btrfs rw,noatime,ssd,compress=lzo,space_cache,commit=60,subvol=@home 0 2
UUID=$ROOT_UUID /.snapshots btrfs rw,noatime,ssd,compress=lzo,space_cache,commit=60,subvol=@snapshots 0 2
UUID=$UEFI_UUID /boot/efi vfat defaults,noatime 0 2
UUID=$SWAP_UUID none swap defaults 0 1
tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0
EOF
cat <<EOF >> /etc/default/grub
GRUB_ENABLE_CRYPTODISK=y
EOF
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/s/\"$/ rd.auto=1 cryptdevice=UUID=$LUKS_UUID:lvm:allow-discards&/" /etc/default/grub
dd bs=512 count=4 if=/dev/urandom of=/boot/volume.key
cryptsetup luksAddKey /dev/nvme0n1p2 /boot/volume.key
chmod 000 /boot/volume.key
chmod -R g-rwx,o-rwx /boot
cat <<EOF >> /etc/crypttab
crypt /dev/nvme0n1p2 /boot/volume.key luks
EOF
cat <<EOF >> /etc/dracut.conf.d/10-crypt.conf
install_items+=" /boot/volume.key /etc/crypttab "
EOF
echo 'add_dracutmodules+=" crypt btrfs lvm resume "' >> /etc/dracut.conf
echo 'tmpdir=/tmp' >> /etc/dracut.conf
dracut --force --hostonly --kver 5.4.26_1
mkdir /boot/grub
grub-mkconfig -o /boot/grub/grub.cfg
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=void --boot-directory=/boot  --recheck
ln -s /etc/sv/dhcpcd /etc/runit/runsvdir/default
ln -s /etc/sv/wpa_supplicant/ /etc/runit/runsvdir/default
sed -i 's/issue_discards = 0/issue_discards = 1/' /etc/lvm/lvm.conf

References

As per "standing on the shoulders of giants", most of the information in this guide was no discovered by myself, but rather assembled from various existing guides, which I want to list here to give credit where credit is due:

[1] https://gist.github.com/gbrlsnchs/9c9dc55cd0beb26e141ee3ea59f26e21

[2] https://wiki.voidlinux.org/Full_Disk_Encryption_w/Encrypted_Boot

[3] https://gist.github.com/mattiaslundberg/8620837

[4] http://blog.neutrino.es/2013/howto-properly-activate-trim-for-your-ssd-on-linux-fstrim-lvm-and-dmcrypt/

@Logarithmus
Copy link

Logarithmus commented Feb 24, 2021

Great guide! I'm building my own setup based on it, but I use BTRFS on top of LUKS on top of LVM, because I want to support creating multiple users with /home's encrypted separately from each other, so multiple people could use the same installation without stealing someone else's data.
I also want root partition to be encrypted as well, via multiple key slots for each user.
For each user, user's passphrase should be used both for root keyslot and for home.

With help of cryptsetup-keyscript, keyctl_keyscript & PAM, you can eliminate the need to enter passphrase twice. PC turns on, you enter your own passphrase, then both / and /home/username are opened, after that display manager logs you in without asking passphrase again.

Unfortunately, it seems that cryptsetup-keyscript is based on Debian/Ubuntu specific patch.
But it's possible to package it for xbps, as well as keyctl_keyscript.

I used to use ZFS for half a year, but it was slow (150 MiB/s max), because my laptop's CPU doesn't support AES-NI. At first I've thought about encrypting only important stuff instead of everything. Luckily, I've discovered Adiantum encryption algorithm. cryptsetup benchmark -c xchacha12,aes-adiantum -s 256 gives me whopping 500 MiB/s per thread, which almost matches the speed of my mSATA SSD.

@tobi-wan-kenobi
Copy link
Author

@Logarithmus glad it is useful for you! Feel free to link your instructions, once they are done, I would be interested to know more :)

@Sollovin
Copy link

Excellent scripts save me from reading tons of documents. Thanks a lot!

@tobi-wan-kenobi
Copy link
Author

@Sollovin Great to hear, thanks for letting me know! One thing: Please note that until right now, the dracut modules list was missing "lvm", which can cause issues when updating the kernel.

Copy link

ghost commented Mar 29, 2021

Great work, thx 4 sharing!

@StellarOrbit
Copy link

This guide is perfect. Glad it exists. I made some changes for myself, like using glibc instead of musl and using servercentral to download the base packages instead of the default repo. Amazing guide, man!

@Goorzhel
Copy link

Solid guide, thanks. Just a heads-up about this line:

echo 'add_dracutmodules+="crypt btrfs lvm resume"' >> /etc/dracut.conf

dracut: WARNING: <key>+=" <values> ": <values> should have surrounding white spaces!
dracut: WARNING: This will lead to unwanted side effects! Please fix the configuration file.

@tobi-wan-kenobi
Copy link
Author

@Goorzhel Thanks for pointing that out, will update this right away!

@boki1
Copy link

boki1 commented Jun 25, 2022

ln -s /etc/sv/dhcpcd /var/service
ln -s /etc/sv/wpa_supplicant/ /var/service

Shouldn't these be linked to /etc/runit/runsvdir/default/, since the system is not yet booted.

reference.

@StellarOrbit
Copy link

ln -s /etc/sv/dhcpcd /var/service
ln -s /etc/sv/wpa_supplicant/ /var/service

Shouldn't these be linked to /etc/runit/runsvdir/default/, since the system is not yet booted.

reference.

Yes, that's what I thought as well. I always do that when using this guide because otherwise it fails to link on startup.

@tobi-wan-kenobi
Copy link
Author

@boki1 @StellarOrbit Sorry, that comment somehow slipped me by.

I seem to remember that for some reason, it worked like that for me, but I will gladly update the configuration :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment