Skip to main content

KDENeonLaptopBuildStandard

Linux OS Partitions

export DEV=/dev/nvme0n1
blkdiscard "${DEV}" -f
sudo parted "${DEV}"
mklabel gpt
mkpart bios_grub ext2 2048s 8M
set 1 bios_grub on
mkpart ESP fat32 8M 1G
set 2 ESP on
mkpart BOOT ext2 1G 3G
mkpart SWAP linux-swap 3G 19G
mkpart SSD1 ext2 19G 39G
mkpart TMP ext2 39G 63G
q
mkfs.fat -F 32 "${DEV}p2"

Install KDE Neon

TODO

ZFS Datasets

bpool

Possible improvement: Using "copies=2" makes it so that all data in a certain dataset is stored twice. This allows ZFS to automatically recover from corrupted blocks. This uses twice the space and, because both copies are on the same physical device, does not help in the event of device failure. This might be useful on bpool/BOOT/ROOT and SSD1/OS/Neon/ROOT.

The "bpool" dataset needs to be compatible with the GRUB bootloader so it needs to be created with a minimal subset of ZFS features. Why not just use ext4 or even the existing FAT32 EFI partition? ZFS still has checksuming (so you will know if your kernel image/initrd have become corrupted) and snapshots (for rollback).

sudo apt install -y zfsutils-linux
sudo zpool create -d \
-o feature@async_destroy=enabled \
-o feature@empty_bpobj=enabled \
-o feature@spacemap_histogram=enabled \
-o feature@enabled_txg=enabled \
-o feature@hole_birth=enabled \
-o feature@bookmarks=enabled \
-o feature@embedded_data=enabled \
-o feature@large_blocks=enabled \
-O mountpoint=/mnt/zfs/bpool \
bpool /dev/disk/by-partlabel/BOOT
sudo zfs create bpool/BOOT
sudo zfs create bpool/BOOT/ROOT
sudo zfs set mountpoint=/boot bpool/BOOT/ROOT

SSD1

TODO: use zstd instead of lz4?

sudo zpool create -o ashift=12 \
-o feature@async_destroy=enabled \
-o feature@encryption=enabled \
-o feature@bookmarks=enabled \
-o feature@embedded_data=enabled \
-o feature@empty_bpobj=enabled \
-o feature@enabled_txg=enabled \
-o feature@extensible_dataset=enabled \
-o feature@filesystem_limits=enabled \
-o feature@hole_birth=enabled \
-o feature@large_blocks=enabled \
-o feature@lz4_compress=enabled \
-o feature@spacemap_histogram=enabled \
-o feature@userobj_accounting=enabled \
-O acltype=posixacl \
-O compression=lz4 \
-O devices=off \
-O normalization=formD \
-O relatime=on \
-O xattr=sa \
-O mountpoint=/mnt/zfs/SSD1 SSD1 /dev/disk/by-partlabel/SSD1
sudo zfs create SSD1/OS
sudo zfs create -o encryption=aes-256-gcm -o keyformat=passphrase -o compression=on SSD1/OS/NeonJammy
sudo zfs load-key SSD1/OS/NeonJammy
sudo zfs create SSD1/OS/NeonJammy/ROOT

Copy the Installed OS to the ZFS datasets

sudo zpool export SSD1
sudo zpool export bpool
sudo mkdir /target
sudo zpool import -R /target SSD1
sudo zpool import -R /target bpool
sudo zfs set overlay=on SSD1/OS/NeonJammy/ROOT
sudo zfs set mountpoint=/ SSD1/OS/NeonJammy/ROOT
sudo zpool export bpool
sudo zpool export SSD1
sudo zpool import -R /target SSD1
sudo zfs load-key SSD1/OS/NeonJammy
sudo zfs mount SSD1/OS/NeonJammy/ROOT
sudo zpool import -R /target bpool

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 --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 --make-rslave /target/dev
sudo mount --make-rslave /target/proc
sudo mount --make-rslave /target/sys
sudo mount --make-rslave /target/dev/pts
sudo chroot /target
export DEV=/dev/nvme0n1
unlink /etc/resolv.conf
echo "nameserver 8.8.8.8" >/etc/resolv.conf

Not relevant yet: If your hardware needs a kernel newer than the one provided (5.15) you can install the HWE kernel (X.X) now:

apt install linux-image-generic-hwe-XX.XX linux-headers-generic-hwe-XX.XX
sudo apt install zfsutils-linux zfs-initramfs openssh-server
sudo update-initramfs -c -k all

The following error messages are normal (we are not using dmcrypt so they are harmless):

cryptsetup: ERROR: Couldn't resolve device SSD1/OS/NeonFocal/ROOT
cryptsetup: WARNING: Couldn't determine root device
sudo grub-install --bootloader-id=neon --efi-directory=/boot/efi "${DEV}"
sudo grub-install --bootloader-id=ubuntu --efi-directory=/boot/efi "${DEV}"

Setup encrypted swap

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
sudo zfs snapshot SSD1/OS/NeonJammy/ROOT@fresh-install
sudo zfs snapshot bpool/BOOT/ROOT@fresh-install
sudo update-grub
exit
sudo umount /target/boot/efi
sudo umount -R /target
sudo zfs umount SSD1/OS/NeonJammy/ROOT
sudo zpool export bpool
sudo zpool export SSD1

The following errors can be ignored.

umount: /target/mnt/zfs/SSD1/OS: no mount point specified.
cannot unmount '/target/mnt/zfs/SSD1/OS': umount failed

Now we reboot, hopefully into our newly installed OS.

sudo reboot

You should be prompted to unplug the USB boot media during shutdown. If not, unplug it during BIOS startup, before the machine boots.

Disable System Wide suspend on Laptop Lid Close

sudo vim /etc/systemd/logind.conf

Chenge this:

#HandleLidSwitchExternalPower=suspend

To this

HandleLidSwitchExternalPower=lock

IPv6 Valid lifetime for deprecated IPs

Work in progress

All of my IPv6 addressing is allocated automatically. Sometimes the prefixes assigned to each internal VLAN change. This leaves devices with new IPv6 addresses and deprecated addresses in different subnets and then they have trouble connecting to each other. The following causes "deprecated" addresses to be removed after one hour, instead of one week.

printf '%s' "net.ipv6.conf.default.temp_valid_lft = 3600" >/etc/sysctl.d/11-ipv6-valid_lft.conf
printf '%s' "net.ipv6.conf.all.temp_valid_lft = 3600" >>/etc/sysctl.d/11-ipv6-valid_lft.conf

TODO: We still get routes left behind and I don't know how to fix that.

Backup Image

If you want to take a backup of the fresh install for re-use, consider a command like this:

zpool trim --wait SSD1
sudo dd if=/dev/nvme0n1 bs=512 count=76171264 | 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

Before "gold" image

Security Hardening

  • In Firefox "about:config" set "network.IDN_show_punycode" to "true"

TODO