KDE Neon Laptop Build Standard - With ZfsBootMenu
Note on encrypted root
I'm extremely picky but, I don't like the "official" method of allowing boot with only a single prompt for the ZFS encryption passphrase. The passphrase exists on your machine. Yes, it's on an encrypted dataset and in a file with acls so only the root user can access it but... I'd rather the passphrase exist only in my head. If that means I need to enter the passphrase twice, it's not ideal, but it's good enough for now.
Actually, I'm thinking that along side ZfsBootMenu, I could install the Ubuntu kernel and initramfs as a UKI directly into uEFI. That way, the default action could be to boot the Ubuntu kernel directly and the secondary action, if F9 is pressed, could be to boot ZfsBootMenu for additional features.
Note on sudo
in the commands below
Many of the commands below are prefixed with sudo
, even if they are to be
executed in a root shell (e.g. once you change into the chroot). This is simply
to make it easier if you need to just re-run select commands later and might not
be in the same environment. It's never used to give unnecessary access to a
command.
Linux OS Partitions
Reference: https://docs.zfsbootmenu.org/en/v2.3.x/guides/ubuntu/uefi.html
export ID="NeonJammy"
export DISK="/dev/nvme0n1"
export ESP_PART="1"
export ESP_SIZE="512m"
export ESP_DEVICE="${DISK}p${BOOT_PART}"
export POOL_PART="2"
export POOL_SIZE="24g"
export POOL_NAME="SSD1"
export POOL_DEVICE="${DISK}p${POOL_PART}"
export TMP_PART="3"
export TMP_SIZE="24g"
export TMP_DEVICE="${DISK}p${TMP_PART}"
# Future ChromeOS Partition?
#export COS_PART="8"
#export COS_SIZE="32g"
#export COS_DEVICE="${DISK}p${COS_PART}"
export SWAP_PART="9"
export SWAP_SIZE="16g"
export SWAP_DEVICE="${DISK}p${SWAP_PART}"
sudo blkdiscard "${DISK}" -f
sudo sgdisk -n "${ESP_PART}:1m:+${ESP_SIZE}" -t "${ESP_PART}:ef00" -c "0:ESP" "${DISK}"
sudo sgdisk -n "${POOL_PART}:0:+${POOL_SIZE}" -t "${POOL_PART}:bf00" -c "0:${POOL_NAME}" "${DISK}"
sudo sgdisk -n "${TMP_PART}:0:+${TMP_SIZE}" -t "${TMP_PART}:8303" -c "0:TMP" "${DISK}"
sudo mkfs.fat -F 32 -n ESP "${DISK}p${ESP_PART}"
sudo parted ${DISK} p
Model: SAMSUNG MZVL4512HBLU-00BH1 (nvme)
Disk /dev/nvme0n1: 512GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1049kB 538MB 537MB fat32 ESP boot, esp
2 538MB 26.3GB 25.8GB SSD1
3 26.3GB 52.1GB 25.8GB TMP
later:
sgdisk -n "${SWAP_PART}:0:-${SWAP_SIZE}" -t "${SWAP_PART}:8200" -c "0:SWAP" "${DISK}"
Save your the encryption password to a temporary file. This is not written to disk (it's in a memory overlay that will disappear once the installation OS is shut down).
echo 'SomeKeyphrase' > /tmp/${POOL_NAME}.key
chmod 000 /tmp/${POOL_NAME}.key
sudo zpool create -f -o ashift=12 \
-O compression=lz4 \
-O acltype=posixacl \
-O xattr=sa \
-O relatime=on \
-O encryption=aes-256-gcm \
-O keylocation=file:///tmp/${POOL_NAME}.key \
-O keyformat=passphrase \
-o autotrim=on \
-o compatibility=openzfs-2.1-linux \
-m none ${POOL_NAME} "/dev/disk/by-partlabel/${POOL_NAME}"
sudo zfs create -o mountpoint=none "${POOL_NAME}/ROOT"
sudo zfs create -o mountpoint=/ -o canmount=noauto ${POOL_NAME}/ROOT/${ID}
sudo zpool set bootfs=${POOL_NAME}/ROOT/${ID} ${POOL_NAME}
Install KDE Neon into the TMP partition
Copy the Installed OS to the ZFS datasets
sudo zpool export ${POOL_NAME}
sudo mkdir /target
sudo zpool import -R /target ${POOL_NAME}
sudo zfs set overlay=on ${POOL_NAME}/ROOT/${ID}
sudo zfs set mountpoint=/ ${POOL_NAME}/ROOT/${ID}
sudo zpool export ${POOL_NAME}
sudo zpool import -R /target ${POOL_NAME}
sudo zfs load-key ${POOL_NAME}
sudo zfs mount ${POOL_NAME}/ROOT/${ID}
sudo mkdir /source
sudo mount --bind --make-slave /tmp/calamares-root-* /source
sudo rsync -avPX /source/. /target/.
Make the OS in the ZFS datasets bootable
We bind certain system directories into the /target directory and then chroot into it.
sudo mount /dev/disk/by-partlabel/ESP /target/boot/efi
sudo mount -t efivarfs efivarfs /sys/firmware/efi/efivars || true
sudo mount --rbind /dev /target/dev
sudo mount --rbind /proc /target/proc
sudo mount --rbind /sys /target/sys
sudo mount --rbind /dev/pts /target/dev/pts
sudo mount --rbind /var/log /target/var/log
sudo mount --make-rslave /target/dev
sudo mount --make-rslave /target/proc
sudo mount --make-rslave /target/sys
sudo mount --make-rslave /target/dev/pts
sudo mount --make-rslave /target/var/log
sudo chroot /target
The following is run inside the new KDE Neon installation as a chroot
sudo unlink /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
. /var/log/installation-settings
sudo apt install -y zfsutils-linux zfs-initramfs openssh-server curl efibootmgr
sudo systemctl enable zfs.target
sudo systemctl enable zfs-import-cache
sudo systemctl enable zfs-mount
sudo systemctl enable zfs-import.target
sudo update-initramfs -c -k all
sudo rm -fr /boot/efi/EFI/*
sudo mkdir -p /boot/efi/EFI/ZBM
sudo curl -o /boot/efi/EFI/ZBM/VMLINUZ.EFI -L https://get.zfsbootmenu.org/efi
sudo cp /boot/efi/EFI/ZBM/VMLINUZ.EFI /boot/efi/EFI/ZBM/VMLINUZ-BACKUP.EFI
sudo efibootmgr -c -d "${DISK}" -p "${ESP_PART}" \
-L "ZFSBootMenu (Backup)" \
-l '\EFI\ZBM\VMLINUZ-BACKUP.EFI'
sudo efibootmgr -c -d "${DISK}" -p "${ESP_PART}" \
-L "ZFSBootMenu" \
-l '\EFI\ZBM\VMLINUZ.EFI'
echo "/dev/disk/by-partlabel/ESP /boot/efi vfat defaults 0 0" | sudo tee -a /etc/fstab
TODO Get the command line from .... oh, the grub config is gone.
zfs set org.zfsbootmenu:commandline="quiet splash" ${POOL_NAME}/ROOT
Setup encrypted swap
sudo sgdisk -n "${SWAP_PART}:-${SWAP_SIZE}:-10m" -t "${SWAP_PART}:8200" -c "0:SWAP" "${DISK}"
echo "swap /dev/disk/by-partlabel/SWAP /dev/urandom swap,cipher=aes-cbc-essiv:sha256,size=256,plain" | sudo tee -a /etc/crypttab
echo "/dev/mapper/swap none swap defaults 0 0" | sudo tee -a /etc/fstab
echo "RESUME=none" |sudo tee /etc/initramfs-tools/conf.d/resume
Limit ZFS ARC Size
echo "options zfs zfs_arc_max=4294967296" | sudo tee /etc/modprobe.d/zfs.config
Update everything
apt update; apt -y full-upgrade; apt autoremove
Take a snapshot of the fresh installation
sudo zfs snapshot ${POOL_NAME}/ROOT@fresh-install
Exit from the chroot
exit
Remove the TMP partition
sudo umount $(mount | grep "${TMP_DEVICE}" | awk '{print $3}')
sudo blkdiscard "${TMP_DEVICE}" -f
sudo sgdisk -d ${TMP_PART} ${DISK}
Backup Image
If you want to take a backup of the fresh install for re-use, consider a command like this:
sudo umount $(mount | grep "${ESP_DEVICE}" | awk '{print $3}')
sudo umount -R /target || true
sudo zpool trim --wait ${POOL_NAME}
sudo zpool export ${POOL_NAME} || true
sudo dd if=${DISK} bs=512 count=51382272s | tee >(sha256sum >/dev/stderr) | zstd -10 | tee >(sha256sum >/dev/stderr) | mbuffer | ssh [user]@[some other machine] "mbuffer >/path/to/FreshInstall.dd.zst"
sudo dd if=/dev/nvme0n1 bs=512 count=76171264 | tee >(sha256sum >/dev/stderr) | zstd -10 | tee >(sha256sum >/dev/stderr) | mbuffer | ssh [email protected] "mbuffer >/mnt/zfs/RaidZ3Disk/shares/Backups/spare-e7470/FreshInstall.dd.zst"
sudo dd if=/dev/nvme0n1 bs=512 count=76171264 | tee >(sha256sum >/dev/stderr) | zstd -10 | tee >(sha256sum >/dev/stderr) | mbuffer | ssh [email protected] "mbuffer >/mnt/zfs/RaidZ3Disk/shares/Backups/spare-e7470/WorkInstall.dd.zst"
To Restore the above image:
ssh ssh [user]@[some other machine] "dd if=/path/to/FreshInstall.dd.zst bs=512 | mbuffer" | mbuffer zstd -d -c | tee >(sha256sum >/dev/stderr) sudo dd if=/dev/nvme0n1 bs=512