Skip to content

Instantly share code, notes, and snippets.

@danielmt
Forked from craSH/bulletproof_arch.md
Last active July 25, 2022 23:48
Show Gist options
  • Save danielmt/f0c69c8bee87831a0ad74e0fbfff8c9c to your computer and use it in GitHub Desktop.
Save danielmt/f0c69c8bee87831a0ad74e0fbfff8c9c to your computer and use it in GitHub Desktop.
Bulletproof Arch - Minimal Clean Install

Bulletproof Arch

These are working notes on the installation of Arch Linux. I've just completed this install on a notebook (@altercation: Lenovo P50. @craSH: Dell XPS 13" 9380) but the setup should work for most laptop/desktop configurations.

Some assumptions/notes:

  1. This isn't a dual boot configuration. I can see some of the appeal and still work in Adobe from time to time, but given the increasing complexity of EFI and the way Windows/MS manhandles the EFI partition during upgrades, I really would recommend steering clear of dual boot. Just my two cents here.
  2. We're encrypting with LUKS. I've used so-called "self encrypting drives" as well (and linux has multiple ways of dealing with these keys and the encryption thereof). They are fast and as secure as you trust your UEFI bios manufacturer and harddrive manufacturer. Honestly, not a bad solution and would eliminate the need for LUKS. You do need UEFI bios support for these drives, though that's pretty common now. For me it feels a little too proprietery at this point and I don't trust that there isn't a big ol' backdoor in the drive firmware.
  3. We're avoiding LVM. This talk by Marc Merlins changed my views on a lot of things, including LVM: Video: https://www.youtube.com/watch?v=6DplcPrQjvA Slides: http://marc.merlins.org/linux/talks/2015/Btrfs-LCA2015/Btrfs.pdf Besides the performance hit, there is just no need. Swap can now be handled within a Btrfs subvolume with hibernation support, and no additional luks device is then required. LVM gets us nothing at this point given that we'll be using Btrfs subvolumes as they were truly meant to be.

Preparation

Boot Arch USB

Download and burn USB drive as per instructions on ArchLinux.org

Pick Install Drive

# lsblk

If you don't already use lsblk a lot, you should. I love it. Here's a way to quickly list all your partitions along with their partition labels, filesystem labels, mount points, and UUIDS/PARTUUIDS. lsblk -o +LABEL,PARTLABEL,UUID,PARTUUID

NOTE: in the following sections, DRIVEID might be "sda", for example, while PARTID might be "sda1". I used to just append partition numbers to a $DRIVE variable in my scripts, but this is no longer safe with the advent of NVMe drives, which enumerate using a different format.

Wipe Drive (optional)

This is a full, secure wipe of the drive. If you aren't worried about the NSA, APTs, your mom, whatever, then you can skip this, but why not have some peace of mind and do it? Note that even though we're sourcing /dev/zero for the input, it's scrambled on disk due to the use of LUKS.

# sgdisk --zap-all /dev/DRIVEID
# cryptsetup open --type plain /dev/DRIVEID container --key-file /dev/random
# dd if=/dev/zero of=/dev/mapper/container status=progress
# cryptsetup close container

Partition Drive

# export DRIVE=/dev/whatever
# export MOUNT=/mnt

Create all partitions. See http://www.rodsbooks.com/efi-bootloaders/principles.html for an explanation of why 550MiB is a minimum size for the EFI system partition. We'll use a Btrfs sub-volume for swap, setup later.

# sgdisk --clear \
      --new=1:0:+550MiB --typecode=1:ef00 --change-name=1:ESP \
      --new=2:0:0       --typecode=2:8300 --change-name=2:cryptsystem \
      $DRIVE

Encrypt System Partition

Encrypt the main partition. Use a good passphrase. Then open the partition.

# cryptsetup luksFormat --key-size 512 --cipher aes-xts-plain64 /dev/disk/by-partlabel/cryptsystem
# cryptsetup open /dev/disk/by-partlabel/cryptsystem system

Format EFI System Partition

ESP now needs to be formatted as FAT32, as per the standard.

# mkfs.fat -F32 -n ESP /dev/disk/by-partlabel/ESP

Create Btrfs Subvolumes

Notes:

  1. While it is possible to just create a single subvolume (the so-called "top level subvolume") and use that as root, we'd be throwing away the real power of Btrfs. Our subvolume structure will in fact be this:
TOPLEVEL
  |
  |--rootfs
  |--home
  |--swap
  `--snapshots
       |
       |--rootfs_snapshot_datehere
       |--rootfs_snapshot_datehere
       |--rootfs_snapshot_datehere
       |--home_snapshot_datehere
       `--home_snapshot_datehere
  1. The snapshots should all be made as read-only Btrfs snapshots. At any time we can boot into a shell where our rootfs or home isn't mounted and rename our "rootfs" subvolume to something like "rootfs_old" and then simply create a new rw snapshot from any of the snapshots, naming it rootfs and placing it under the top-level subvolume. This will be a perfect restore of the root filesystem (sans /home, as we want to keep / and /home decoupled for snapshotting purposes).

  2. In time I may decide to make parts of /var or other branches of the filesystem separate subvolumes so that they are not being snapshotted along with the rest of root. For example, any directory contain the bulk of my VM images. /var/lib/docker is also a great candidate combined with Docker's Btrfs storage driver.

From https://btrfs.wiki.kernel.org/index.php/FAQ#Does_Btrfs_support_TRIM.2Fdiscard.3F -

"-o discard" can have some negative consequences on performance on some SSDs or at least whether it adds worthwhile performance is up for debate depending on who you ask, and makes undeletion/recovery near impossible while being a security problem if you use dm-crypt underneath (see http://asalor.blogspot.com/2011/08/trim-dm-crypt-problems.html ), therefore it is not enabled by default. You are welcome to run your own benchmarks and post them here, with the caveat that they'll be very SSD firmware specific.

Top-level subvolume first:

# mkfs.btrfs --force --label system /dev/mapper/system
# export o=defaults,x-mount.mkdir
# export o_btrfs=$o,compress=lzo,ssd,noatime
# export o_btrfs_swap=$o,ssd,noatime

Mount this temporarily so that we can create the subvolumes that will be our actual mounted volumes:

# mount -t btrfs LABEL=system $MOUNT

Create the subvolumes under the top-level subvolume:

Subvolumes will now be created, we'll unmount, then remount the subvolumes at the correct mountpoints. Specifically, this creates separate subvolumes for / (rootfs) and /home for easier snapshot and rollback, along with a container subvolume for the snapshots themselves. Finally, a sub-volume is created to house our swap file (which is also then encrypted, without requiring additional LUKS operations).

# btrfs subvolume create $MOUNT/rootfs

Note: Unfortunately the btrfs subvolume set-default command only takes in a subvolume ID, not a name - so the shell trickery below grabs it from the rootfs subvolume.

# btrfs subvolume set-default ${$(btrfs subvolume list $MOUNT)[(w)2]} $MOUNT
# btrfs subvolume create $MOUNT/home
# btrfs subvolume create $MOUNT/swap
# btrfs subvolume create $MOUNT/snapshots

Unmount everything…

# umount -R $MOUNT

Now, remount them at their correct locations

NOTE: mkdir $MOUNT/boot etc. is NOT needed since we are using the x-mount.mkdir mount option which will create the mount point directories if they do not already exist.

# mount -t btrfs -o subvol=rootfs,$o_btrfs LABEL=system $MOUNT
# mount -t btrfs -o subvol=home,$o_btrfs LABEL=system $MOUNT/home
# mount -t btrfs -o subvol=swap,$o_btrfs_swap LABEL=system $MOUNT/.swap
# mount -t btrfs -o subvol=snapshots,$o_btrfs LABEL=system $MOUNT/.snapshots
# mount -o $o LABEL=ESP $MOUNT/boot/EFI

Configure swap file

We'll be using a swap file within a non-compressed, non-snapshotted Btrfs subvolume.

These steps are from https://wiki.archlinux.org/title/Btrfs#Swap_file

# cd $MOUNT/.swap
# truncate -s 0 $MOUNT/.swap/swapfile
# chattr +C $MOUNT/.swap/swapfile
# btrfs property set $MOUNT/.swap/swapfile compression none

And these steps are from https://wiki.archlinux.org/title/Swap#Swap_file_creation

# dd if=/dev/zero of=$MOUNT/.swap/swapfile bs=1M count=$((8*1024)) status=progress
# chmod 600 $MOUNT/.swap/swapfile
# mkswap $MOUNT/.swap/swapfile
# swapon $MOUNT/.swap/swapfile

When you later configure your /etc/fstab file, you must manually add the entry for the swap file, and it must point to the location on the filesystem.

/.swap/swapfile none swap defaults 0 0

Remember to include the swap Btrfs subvolume in /etc/fstab as well.

Configure Hibernation with the above Btrfs swap file per https://wiki.archlinux.org/title/Power_management/Suspend_and_hibernate#Hibernation_into_swap_file_on_Btrfs - but at the moment, that is an exercise left to the reader.

Proceed with Arch installation

Follow the Arch guide for the more "basic" installation steps now, customized as you wish: https://wiki.archlinux.org/title/Installation_guide#Installation

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