From 822f0bd55ef8a096d741f05e3440e3f5a61ea654 Mon Sep 17 00:00:00 2001 From: Luc Date: Sat, 10 Aug 2024 21:54:34 +0200 Subject: [PATCH] Added ZFS install for alpine-server. --- .../post-install/user-packages.md | 2 +- .../post-install/users.md | 2 +- docs/alpine-server-setup/index.md | 4 +- .../{installation => }/installation.md | 112 ++++++++++------ .../installation/provisioning.md | 73 ---------- .../post-install/drivers.md | 1 + .../post-install/logging.md | 0 .../post-install/podman.md | 0 .../post-install/repositories.md | 0 .../post-install/security.md | 1 + docs/alpine-server-setup/post-install/swap.md | 1 + .../alpine-server-setup/post-install/users.md | 0 docs/alpine-server-setup/provisioning.md | 125 ++++++++++++++++++ mkdocs.yml | 4 + 14 files changed, 205 insertions(+), 120 deletions(-) rename docs/alpine-server-setup/{installation => }/installation.md (55%) delete mode 100644 docs/alpine-server-setup/installation/provisioning.md create mode 120000 docs/alpine-server-setup/post-install/drivers.md create mode 100644 docs/alpine-server-setup/post-install/logging.md create mode 100644 docs/alpine-server-setup/post-install/podman.md create mode 100644 docs/alpine-server-setup/post-install/repositories.md create mode 120000 docs/alpine-server-setup/post-install/security.md create mode 120000 docs/alpine-server-setup/post-install/swap.md create mode 100644 docs/alpine-server-setup/post-install/users.md create mode 100644 docs/alpine-server-setup/provisioning.md diff --git a/docs/alpine-desktop-setup/post-install/user-packages.md b/docs/alpine-desktop-setup/post-install/user-packages.md index bcc8add..788b44d 100644 --- a/docs/alpine-desktop-setup/post-install/user-packages.md +++ b/docs/alpine-desktop-setup/post-install/user-packages.md @@ -24,7 +24,7 @@ For `nix` to be able to install packages it is necessary to add a few channels/r ``` https://nixos.org/channels/nixos- nixpkgs -https://nixos.org/channels/nixos-unstable nixpkgs-unstable +https://nixos.org/channels/nixpkgs-unstable nixpkgs-unstable https://github.com/nix-community/nixGL/archive/main.tar.gz nixgl ``` diff --git a/docs/alpine-desktop-setup/post-install/users.md b/docs/alpine-desktop-setup/post-install/users.md index 16aa362..d81edc7 100644 --- a/docs/alpine-desktop-setup/post-install/users.md +++ b/docs/alpine-desktop-setup/post-install/users.md @@ -2,7 +2,7 @@ It might be nice to add a user to your system. -## doas +## Wheel Before creating the user install `doas`, to use when root is required: diff --git a/docs/alpine-server-setup/index.md b/docs/alpine-server-setup/index.md index 317bcea..16491aa 100644 --- a/docs/alpine-server-setup/index.md +++ b/docs/alpine-server-setup/index.md @@ -1,3 +1,5 @@ # An Alpine Linux server installation -This guide will demonstrate how to install [Alpine Linux](https://www.alpinelinux.org/) for server application. With tpm encryption, secureboot, a copy on write filesystem and Podman to handle containers. Alpine Linux makes a good base for a server because of its simplicity, lightweightness and security. Check out the [Alpine Linux wiki](https://wiki.alpinelinux.org/wiki/Main_Page) for additional resources and information. +This guide will demonstrate how to install [Alpine Linux](https://www.alpinelinux.org/) for server application. Alpine Linux will run on a raid configured ZFS filesystem with an encrypted home dataset, user services with runsvdir and user containers with podman. + +Alpine Linux makes a good base for a server because of its simplicity, lightweightness and security. Check out the [Alpine Linux wiki](https://wiki.alpinelinux.org/wiki/Main_Page) for additional resources and information. \ No newline at end of file diff --git a/docs/alpine-server-setup/installation/installation.md b/docs/alpine-server-setup/installation.md similarity index 55% rename from docs/alpine-server-setup/installation/installation.md rename to docs/alpine-server-setup/installation.md index 937b377..4806a74 100644 --- a/docs/alpine-server-setup/installation/installation.md +++ b/docs/alpine-server-setup/installation.md @@ -1,20 +1,36 @@ # Installation -To install the Alpine Linux distribution on the system, the root subvolume and the efi partition have to be mounted to the main system. +To install the Alpine Linux distribution on the system, the datasets of the system pool and the EFI partitions have to be mounted to the main system. + +First import the system pool ``` -# mount -o subvol=@root /dev/mapper/luks /mnt -t btrfs -# mkdir /mnt/efi -p -# mount /dev/1 /mnt/efi -t vfat +# zpool import -N -R /mnt tank ``` -Then set up the base system using `setup disk`: +Mount the datasets in the system pool and decrypt the home dataset + +``` +# zfs mount tank/root/alpine +# zfs load-key -L prompt tank/home +# zfs mount tank/home +# zfs mount tank/var +``` + +Mount the ESP + +``` +# mkdir /mnt/esp +# mount /dev/md/esp /mnt/esp -t vfat +``` + +Then install Alpine Linux ``` # setup-disk -m sys /mnt ``` -This will also add grub as bootloader which will be replaced but for now it will reside on the efi partition. +This will also add `grub` as bootloader which will be replaced but for now it will reside on the ESP. To make it possible to chroot into the system, mount the other directories: @@ -34,7 +50,8 @@ The other setup scripts can be used to configure key aspects of the system. Besi # setup-ntp openntpd # rc-update add acpid default # rc-update add seedrng boot -# rm -rf /var/tmp ; ln -s /tmp /var/tmp +# rm -rf /var/tmp +# ln -s /tmp /var/tmp # passwd root ``` @@ -48,22 +65,29 @@ clock_hctosys="NO" clock_systohc="NO" ``` +Configure the ESP raid array to mount + +``` +# modprobe raid1 +# echo raid1 >> /etc/modules-load.d/raid1.conf +# mdadm --detail --scan >> /etc/mdadm.conf +# rc-update add mdadm boot +# rc-update add mdadm-raid boot +``` + +Configure ZFS to mount + +``` +rc-update add zfs-import sysinit +rc-update add zfs-mount sysinit +``` + Edit `/etc/fstab` for correct mounts: ``` -/dev/disk/by-label/efi /efi vfat defaults,nodev,nosuid,noexec 0 2 -/dev/disk/by-uuid/ / btrfs defaults,noatime,subvol=/@root 0 1 -/dev/disk/by-uuid/ /home btrfs defaults,noatime,nodev,nosuid,subvol=/@home 0 2 -/dev/disk/by-uuid/ /var btrfs defaults,nodev,nosuid,noexec,subvol=/@var 0 2 -/dev/disk/by-uuid/ /nix btrfs defaults,noatime,nodev,nosuid,subvol=/@nix 0 2 -tmpfs /tmp tmpfs rw,size=4G,nr_inodes=5k,noexec,nodev,nosuid,mode=1777 0 0 -proc /proc proc nosuid,nodev,noexec,hidepid=2 0 0 -``` - -Here `` has to be replaced with the uuid of the root volume: - -``` -# blkid /dev/mapper/luks >> /etc/fstab +/dev/md/esp /esp vfat defaults,nodev,nosuid,noexec 0 2 +tmpfs /tmp tmpfs rw,size=4G,nr_inodes=5k,nodev,nosuid,noexec,mode=1777 0 0 +proc /proc proc nodev,nosuid,noexec,hidepid=2 0 0 ``` By default, Alpine Linux uses `mkinitfs` to create an initial ram filesystem, although it is minimal that also means that it lacks some functionality which is needed for a proper setup. Because of this `mkinitfs` and `grub-efi `will be replaced with `booster` and `secureboot-hook`. @@ -76,6 +100,7 @@ By default, Alpine Linux uses `mkinitfs` to create an initial ram filesystem, al To configure booster edit `/etc/booster.yaml`: ``` +enable_zfs: true busybox: false modules: vfat,nls_cp437,nls_iso8859_1 ``` @@ -95,34 +120,31 @@ to: and configure `/etc/kernel-hooks.d/secureboot.conf` for cmdline and secureboot. ``` -cmdline="rw rd.luks.name=""=luks root=/dev/disk/by-uuid/ rootflags=subvol=/@root quiet splash" +cmdline="rw zfs=tank/root/alpine quiet splash" signing_cert="/usr/share/secureboot/keys/db/db.pem" signing_key="/usr/share/secureboot/keys/db/db.key" -output_dir="/efi/EFI/Linux" - +output_dir="/esp/efi/linux" output_name="alpine-linux-{flavor}.efi" ``` -Here `` and `` have to be replaced with the uuid of the root partition and volume respectively. - -``` -# blkid /dev/2 >> /etc/kernel-hooks.d/secureboot.conf -# blkid /dev/mapper/luks >> /etc/kernel-hooks.d/secureboot.conf -``` - Use `sbctl` to create secureboot keys and sign them. ``` # sbctl create-keys # sbctl enroll-keys -... ``` > Whilst enrolling the keys it might be necessary to add the `--microsoft` flag if you are unable to use custom keys. -Now to see if everything went succesfully run: +Set the cache-file of the ZFS pool + +``` +# zpool set cachefile=/etc/zfs/zpool.cache tank +``` + +Now to see if everything went successfully, run: ``` # apk fix kernel-hooks @@ -134,21 +156,26 @@ As discussed earlier `grub` will be replaced, install `gummiboot` as a bootloade ``` # apk add gummiboot -# gummiboot install --path=/efi -# sbctl sign -s /efi/EFI/gummiboot/gummibootx64.efi -# sbctl sign -s /efi/EFI/Boot/BOOTX64.EFI +# mkdir /esp/loader +# mkdir /esp/efi/boot +# cp /usr/lib/gummiboot/gummibootx64.efi /esp/efi/boot/bootx64.efi +``` + +Sign the bootloader with `sbctl` + +``` +# sbctl sign -s /esp/efi/boot/bootx64.efi ``` And also remove some remnants of `grub`. ``` -# rm -rf /efi/EFI/alpine -# rm -rf /efi/grub +# rm -rf /boot/grub # rm -rf /etc/default -# cd /boot && unlink boot +# cd /boot && unlink boot && cd .. ``` -`gummiboot` can be configured with the file `/efi/loader/loader.conf` with which the timeout and the default OS can be specified. +`gummiboot` can be configured with the file `/esp/loader/loader.conf` with which the timeout and the default OS can be specified. ``` default alpine-linux-lts.efi @@ -161,9 +188,6 @@ Now exit the chroot and you should be able to reboot into a working Alpine syste ``` # exit # umount -lf /mnt +# zpool export tank # reboot -``` - -When booting up your screen might appear blank, this is the encryption prompt. Enter the encryption key and press enter to boot. - -> Do note that "Linux Boot Manager" will have to be set to load first in your bios. \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/alpine-server-setup/installation/provisioning.md b/docs/alpine-server-setup/installation/provisioning.md deleted file mode 100644 index d121712..0000000 --- a/docs/alpine-server-setup/installation/provisioning.md +++ /dev/null @@ -1,73 +0,0 @@ -# Provisioning - -After flasing the Alpine Linux extended ISO, partition a disk. For this action internet is required since `gptfdisk` is not included on the extended ISO, therefore it needs to be obtained from the repository. - -To set it up `setup-interfaces` and `setup-apkrepos` will be used. - -``` -# setup-interfaces -ar -# setup-apkrepos -c1 -``` - -A few packages will have to be installed first: - -``` -# apk add cryptsetup lsblk btrfs-progs gptfdisk dosfstools acpid -``` - -The drive should be partitioned using `gdisk` (or `cfdisk`). It should have at least two partitions with one `EFI System` partition and one `Linux filesystem` partition and look something like this: - -| Number of partition | Size | Type | -|:-----:|:-----:|:-----:| -| 1 | 512 MB or more | EFI System | -| 2 | Rest of the drive | Linux filesystem | - -Then to create the filesystem on the efi partition. - -``` -# mkfs.fat -F 32 -n efi /dev/1 -``` - -The root partition of the system is going to be encrypted using `cryptsetup`. First generate a key that will be used to encrypt the device and save it temporarily to the file `/tmp/crypt-key.txt` with: - -``` -# cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1 > /tmp/crypt-key.txt && cat /tmp/crypt-key.txt -``` - -Later on in the guide `clevis` will be used for automatic decryption, so this key only has to be entered a few times. However, if any changes are made to the bios or secureboot then this key will be needed again so make sure to write it down. - -Then format the partition using `cryptsetup`: - -``` -# cryptsetup luksFormat /dev/2 --type luks2 --cipher aes-xts-plain64 --hash sha512 --iter-time 4000 --key-size 512 --pbkdf argon2id --verify-passphrase -[Enter the generated key] -# cryptsetup open --type luks /dev/2 luks -``` - -This creates a formatted partition on `\dev\mapper\luks` which is denoted as the root volume. A btrfs filesystem will be created on the root volume by: - -``` -# mkfs.btrfs -L alpinelinux -n 32k /dev/mapper/luks -``` - -with `-n` the `nodesize`, larger nodesize gives better packing and less fragmentation at the cost of more expensive memory operations while updating metadata blocks. The default is 16k. - -To access the root volume it needs to be mounted. - -``` -# mount /dev/mapper/luks /mnt -t btrfs -``` - -Then to create the necessary subvolumes on the root volume, we use: - -``` -for i in root home var nix; do -> btrfs subvolume create /mnt/@$i -> done -``` - -Now unmount the root volume and provisioning is finished. - -``` -# umount -lf /mnt -``` \ No newline at end of file diff --git a/docs/alpine-server-setup/post-install/drivers.md b/docs/alpine-server-setup/post-install/drivers.md new file mode 120000 index 0000000..848471e --- /dev/null +++ b/docs/alpine-server-setup/post-install/drivers.md @@ -0,0 +1 @@ +alpine-desktop-setup/post-install/drivers.md \ No newline at end of file diff --git a/docs/alpine-server-setup/post-install/logging.md b/docs/alpine-server-setup/post-install/logging.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/alpine-server-setup/post-install/podman.md b/docs/alpine-server-setup/post-install/podman.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/alpine-server-setup/post-install/repositories.md b/docs/alpine-server-setup/post-install/repositories.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/alpine-server-setup/post-install/security.md b/docs/alpine-server-setup/post-install/security.md new file mode 120000 index 0000000..988cd0a --- /dev/null +++ b/docs/alpine-server-setup/post-install/security.md @@ -0,0 +1 @@ +alpine-desktop-setup/post-install/security.md \ No newline at end of file diff --git a/docs/alpine-server-setup/post-install/swap.md b/docs/alpine-server-setup/post-install/swap.md new file mode 120000 index 0000000..2cba21c --- /dev/null +++ b/docs/alpine-server-setup/post-install/swap.md @@ -0,0 +1 @@ +alpine-desktop-setup/post-install/swap.md \ No newline at end of file diff --git a/docs/alpine-server-setup/post-install/users.md b/docs/alpine-server-setup/post-install/users.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/alpine-server-setup/provisioning.md b/docs/alpine-server-setup/provisioning.md new file mode 100644 index 0000000..bdfce6e --- /dev/null +++ b/docs/alpine-server-setup/provisioning.md @@ -0,0 +1,125 @@ +# Provisioning + +After flashing the Alpine Linux extended ISO, partition the disks. For this action internet is required since `zfs` and `sgdisk` are not included on the extended ISO, therefore it needs to be obtained from the repository. + +To set it up `setup-interfaces` and `setup-apkrepos` will be used. + +``` +# setup-interfaces -ar +# setup-apkrepos -c1 +``` + +A few packages will have to be installed first: + +``` +# apk add zfs lsblk sgdisk wipefs dosfstools acpid mdadm +``` + +and load the ZFS kernel module + +``` +# modprobe zfs +``` + +Define the disks you want to use for this install + +``` +# export disks="/dev/disk/by-id/ ... /dev/disk/by-id/" +``` + +with `` for $n \in \mathbb{N}$ the `id` of the disk. + +> According to [openzfs-FAQ](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/FAQ.html) using `/dev/disk/by-id/` is the best practice for small pools. For larger pools, using serial Attached SCSI (SAS) and the like, see [vdev_id](https://openzfs.github.io/openzfs-docs/man/master/5/vdev_id.conf.5.html) for proper configuration. + +Wipe the existing disk partitions + +``` +# for disk in $disks; do +> zpool labelclear -f $disk +> wipefs -a $disk +> sgdisk --zap-all $disk +> done +``` + +Create on each disk an `EFI system` partition (ESP) and a `Linux filesystem` partition + +``` +# for disk in $disks; do +> sgdisk -n 1:1m:+512m -t 1:ef00 $disk +> sgdisk -n 2:0:-10m -t 2:8300 $disk +> done +``` + +Create device nodes + +``` +# mdev -s +``` + +Define the EFI partitions + +``` +# export efiparts="" +# for disk in $disks; do +> efipart=${disk}-part-1 +> efiparts="$efiparts $efipart" +> done +``` + +Create a `mdraid` array on the EFI partitions + +``` +# modprobe raid1 +# mdadm --create --level 1 --metadata 1.0 --raid-devices /dev/md/esp $efiparts +# mdadm --assemble --scan +``` + +Format the array with a FAT32 filesystem + +``` +# mkfs.fat -F 32 /dev/md/esp +``` + +## ZFS pool creation + +Define the pool partitions + +``` +# export poolparts="" +# for disk in $disks; do +> poolpart=${disk}-part-2 +> poolparts="$poolparts $poolpart" +> done +``` + +Create the system pool + +``` +# zpool create -f \ + -o ashift=12 \ + -O compression=lz4 \ + -O acltype=posix \ + -O xattr=sa \ + -O dnodesize=auto \ + -m none \ + tank raidz1 $poolparts +``` + +> Additionally, the `spare` option can be used to indicate spare disks. If more redundancy is preferred than `raidz2` and `raidz3` are possible [alternatives](https://openzfs.github.io/openzfs-docs/man/master/7/zpoolconcepts.7.html) for `raidz1`. If a single disk is used the `raidz` option can be left aside. For further information see [zpool-create](https://openzfs.github.io/openzfs-docs/man/master/8/zpool-create.8.html). + +Then create the system datasets + +``` +# zfs create -o mountpoint=none tank/root +# zfs create -o canmount=noauto -o mountpoint=/ -o atime=off -o quota=24g tank/root/alpine +# zfs create -o mountpoint=/home -o atime=off -o setuid=off -o devices=off -o quota= -o encryption=on -o keyformat=passphrase tank/home +# zfs create -o mountpoint=/var -o exec=off -o setuid=off -o devices=off -o quota=16g tank/var +``` + +> Setting the `` depends on the total size of the pool, generally try to reserve some empty space in the pool. + +Finally, export the zpool + +``` +# zpool export tank +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index d090ffc..2a17bf3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -74,8 +74,12 @@ nav: - 'Installation': alpine-server-setup/installation/installation.md - 'Post installation': - 'Repositories': alpine-server-setup/post-install/repositories.md + - 'Firmware and drivers': alpine-server-setup/post-install/drivers.md - 'Security': alpine-server-setup/post-install/security.md + - 'Logging': alpine-server-setup/post-install/logging.md + - 'Swap': alpine-server-setup/post-install/swap.md - 'Users': alpine-server-setup/post-install/users.md + - 'Podman': alpine-server-setup/post-install/podman.md - 'Void-desktop setup': - void-desktop-setup/index.md