Added ZFS install for alpine-server.
This commit is contained in:
parent
e6bae601d3
commit
822f0bd55e
14 changed files with 205 additions and 120 deletions
|
@ -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-<version> 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
|
||||
```
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -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/<disk>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/<volume-uuid> / btrfs defaults,noatime,subvol=/@root 0 1
|
||||
/dev/disk/by-uuid/<volume-uuid> /home btrfs defaults,noatime,nodev,nosuid,subvol=/@home 0 2
|
||||
/dev/disk/by-uuid/<volume-uuid> /var btrfs defaults,nodev,nosuid,noexec,subvol=/@var 0 2
|
||||
/dev/disk/by-uuid/<volume-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 `<volume-uuid>` 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="<partition-uuid>"=luks root=/dev/disk/by-uuid/<volume-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 `<partition-uuid>` and `<volume-uuid>` have to be replaced with the uuid of the root partition and volume respectively.
|
||||
|
||||
```
|
||||
# blkid /dev/<disk>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.
|
||||
```
|
|
@ -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/<disk>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/<disk>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/<disk>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
|
||||
```
|
1
docs/alpine-server-setup/post-install/drivers.md
Symbolic link
1
docs/alpine-server-setup/post-install/drivers.md
Symbolic link
|
@ -0,0 +1 @@
|
|||
alpine-desktop-setup/post-install/drivers.md
|
0
docs/alpine-server-setup/post-install/logging.md
Normal file
0
docs/alpine-server-setup/post-install/logging.md
Normal file
0
docs/alpine-server-setup/post-install/podman.md
Normal file
0
docs/alpine-server-setup/post-install/podman.md
Normal file
0
docs/alpine-server-setup/post-install/repositories.md
Normal file
0
docs/alpine-server-setup/post-install/repositories.md
Normal file
1
docs/alpine-server-setup/post-install/security.md
Symbolic link
1
docs/alpine-server-setup/post-install/security.md
Symbolic link
|
@ -0,0 +1 @@
|
|||
alpine-desktop-setup/post-install/security.md
|
1
docs/alpine-server-setup/post-install/swap.md
Symbolic link
1
docs/alpine-server-setup/post-install/swap.md
Symbolic link
|
@ -0,0 +1 @@
|
|||
alpine-desktop-setup/post-install/swap.md
|
0
docs/alpine-server-setup/post-install/users.md
Normal file
0
docs/alpine-server-setup/post-install/users.md
Normal file
125
docs/alpine-server-setup/provisioning.md
Normal file
125
docs/alpine-server-setup/provisioning.md
Normal file
|
@ -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/<id-disk-1> ... /dev/disk/by-id/<id-disk-n>"
|
||||
```
|
||||
|
||||
with `<id-disk-n>` 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 <n> /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=<home-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 `<home-quota>` 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
|
||||
```
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue