collapse into one blog page
This commit is contained in:
parent
4cc79798f0
commit
b5e73a228d
13 changed files with 4 additions and 44 deletions
353
docs/posts/01-void-install.md
Normal file
353
docs/posts/01-void-install.md
Normal file
|
@ -0,0 +1,353 @@
|
|||
---
|
||||
title: A simple Void Linux base installation
|
||||
slug: simple-void-base-install
|
||||
date: 2022-08-10
|
||||
draft: false
|
||||
authors:
|
||||
- luc
|
||||
- nils
|
||||
tags:
|
||||
- Void Linux
|
||||
categories:
|
||||
- Base installation
|
||||
---
|
||||
|
||||
This blog entry will demonstrate how to install a `luks` encrypted `x86_64` [Void Linux](https://voidlinux.org/) `musl`/`UEFI signed UKI` operating system on a `ext4` filesystem. This entry is based on the [Void Handbook](https://docs.voidlinux.org/about/index.html) and the [Void man pages](https://man.voidlinux.org/man-pages.7).
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## Provisioning
|
||||
|
||||
Flash the Void Linux `musl` ISO. After booting the ISO, partition the disk with either `fdisk` or `cfdisk`. Create an `EFI System` partition (ESP) and a `Linux Filesystem` partition (LFP).
|
||||
|
||||
It should look something like this:
|
||||
|
||||
| Partition | Size | Type |
|
||||
| :-------: | :--: | :--: |
|
||||
| 1 | 512MB | EFI System |
|
||||
| 2 | Rest | Linux filesystem |
|
||||
|
||||
Format the ESP with a `FAT 32` filesystem:
|
||||
|
||||
``` shell-session
|
||||
sh# mkfs.fat -F 32 -n esp /dev/<disk>1
|
||||
```
|
||||
|
||||
Encrypt the LFP with `luks`:
|
||||
|
||||
``` shell-session
|
||||
sh# cryptsetup luksFormat /dev/<disk>2 --type luks2
|
||||
```
|
||||
|
||||
Open the encrypted partition and format it with a `ext4` filesystem:
|
||||
|
||||
``` shell-session
|
||||
sh# cryptsetup open --type luks /dev/<disk2> root
|
||||
sh# mkfs.ext4 -L root /dev/mapper/root
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
To install Void Linux on the system, the ESP and LFP have to be mounted to the live (ISO) environment:
|
||||
|
||||
``` shell-session
|
||||
sh# mount -t ext4 /dev/mapper/root /mnt
|
||||
sh# mkdir /mnt/efi
|
||||
sh# mount -t vfat /dev/disk/by-label/esp /mnt/efi
|
||||
```
|
||||
|
||||
Now we may install Void Linux `musl` with `xbps-install`:
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-install -Sy -R https://repo-default.voidlinux.org/current/musl -r /mnt base-system cryptsetup openntpd
|
||||
```
|
||||
|
||||
To have a functional chroot into the system, copy `resolv.conf` and bind the system process directories:
|
||||
|
||||
``` shell-session
|
||||
sh# cp /etc/resolv.conf /mnt/etc/
|
||||
sh# for dir in dev proc sys run; do
|
||||
> mount --rbind --make-rslave /$dir /mnt/$dir
|
||||
> done
|
||||
sh# chroot /mnt
|
||||
```
|
||||
|
||||
Configure some key aspects of the system:
|
||||
|
||||
``` shell-session
|
||||
sh# echo <hostname> > /etc/hostname
|
||||
sh# ln -sf /usr/share/zoneinfo/<area>/<subarea> /etc/localtime
|
||||
sh# ln -s /etc/sv/dhcpcd /var/service/
|
||||
sh# ln -s /etc/sv/opennptd /var/service/
|
||||
sh# ln -s /etc/sv/acpid /var/service/
|
||||
sh# passwd root #(1)!
|
||||
```
|
||||
|
||||
1. The root password does not really matter because it is going to be locked after a user has been created.
|
||||
|
||||
Add the encrypted partition to the `crypttab`:
|
||||
|
||||
``` shell title="/etc/crypttab"
|
||||
root /dev/disk/by-uuid/<uuid> none #(1)!
|
||||
```
|
||||
|
||||
1. The simplest way to add the `uuid` into `/etc/crypttab` is by performing:
|
||||
|
||||
``` shell-session
|
||||
sh# blkid -o value -s UUID /dev/<disk>2 >> /etc/crypttab
|
||||
```
|
||||
|
||||
and enable the `crypttab` module for `dracut`:
|
||||
|
||||
``` shell title="/etc/dracut.conf.d/crypt.conf"
|
||||
install_items+=" /etc/crypttab "
|
||||
```
|
||||
|
||||
Edit the `fstab` to set the correct mounts:
|
||||
|
||||
``` shell title="/etc/fstab"
|
||||
/dev/disk/by-label/root / ext4 defaults,noatime 0 1
|
||||
/dev/disk/by-label/esp /efi vfat defaults,nodev,nosuid,noexec,umask=0077 0 2
|
||||
tmpfs /tmp tmpfs rw,nodev,nosuid,noexec,mode=1777 0 0
|
||||
proc /proc proc nodev,nosuid,noexec,hidepid=2 0 0
|
||||
```
|
||||
|
||||
Configure the kernel command-line:
|
||||
|
||||
``` shell title="/etc/dracut.conf.d/cmdline.conf"
|
||||
hostonly="yes"
|
||||
kernel_cmdline="rw rd.luks.name=<uuid>=root root=/dev/mapper/root quiet splash" #(1)!
|
||||
```
|
||||
|
||||
1. The simplest way to add the `uuid` into `/etc/dracut.conf.d/cmdline.conf` is by performing:
|
||||
|
||||
``` shell-session
|
||||
sh# blkid -o value -s UUID /dev/<disk>2 >> /etc/dracut.conf.d/cmdline.conf
|
||||
```
|
||||
|
||||
Install the bootloader `systemd-boot` and some hooks for `dracut` (1) necessary for building and signing the Unified Kernel Image (UKI):
|
||||
{ .annotate }
|
||||
|
||||
1. The initramfs builder.
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-install systemd-boot dracut-uefi sbctl sbsigntool
|
||||
```
|
||||
|
||||
> Verify that secureboot mode is in `setup mode` with `sbctl status`.
|
||||
|
||||
Replace the default `dracut` kernel hooks with those provided by `dracut-uefi`:
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-alternatives -s dracut-uefi
|
||||
```
|
||||
|
||||
and set the directory where the UKI will be deposited:
|
||||
|
||||
``` shell title="/etc/default/dracut-uefi-hook"
|
||||
UEFI_BUNDLE_DIR="/efi/EFI/Linux"
|
||||
```
|
||||
|
||||
Create and enroll the secureboot keys into the system:
|
||||
|
||||
``` shell-session
|
||||
sh# sbctl create-keys
|
||||
sh# sbctl enroll-keys #(1)!
|
||||
```
|
||||
|
||||
1. Whilst enrolling the keys it might be necessary to add the `--microsoft` flag if you are unable to use custom keys.
|
||||
|
||||
Set the key and certificate required for signing the UKI:
|
||||
|
||||
``` shell title="/etc/dracut.conf.d/uki.conf"
|
||||
uefi_secureboot_cert="/var/lib/sbctl/keys/db/db.pem"
|
||||
uefi_secureboot_key="/var/lib/sbctl/keys/db/db.key"
|
||||
```
|
||||
|
||||
Install the bootloader:
|
||||
|
||||
``` shell-session
|
||||
sh# bootctl install
|
||||
```
|
||||
|
||||
Configure the bootloader:
|
||||
|
||||
``` shell title="/efi/loader/loader.conf"
|
||||
timeout 3
|
||||
editor no
|
||||
```
|
||||
|
||||
Sign the bootloader with `sbctl`:
|
||||
|
||||
``` shell-session
|
||||
sh# sbctl sign -s /efi/EFI/Boot/BOOTX64.efi
|
||||
```
|
||||
|
||||
Finally, reconfigure the kernel to execute the `dracut-uefi` hook:
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-reconfigure -f linux<version>
|
||||
```
|
||||
|
||||
> One may verify the signed files by running `sbctl verify`.
|
||||
|
||||
Now exit the chroot, unmount the filesystem and reboot:
|
||||
|
||||
``` shell-session
|
||||
sh# exit
|
||||
sh# umount -lf /mnt
|
||||
sh# reboot
|
||||
```
|
||||
|
||||
## Post installation
|
||||
|
||||
### Firmware and drivers
|
||||
|
||||
Install the device firmware for either AMD or Intel:
|
||||
|
||||
=== "AMD"
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-install linux-firmware-amd
|
||||
```
|
||||
|
||||
=== "Intel"
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-install void-repo-nonfree
|
||||
sh# xbps-install -S intel-ucode
|
||||
```
|
||||
|
||||
### Swap
|
||||
|
||||
Add swap by creating a swapfile:
|
||||
|
||||
``` shell-session
|
||||
sh# dd if=/dev/zero of=/swapfile bs=8m count=512 status=progress #(1)!
|
||||
```
|
||||
|
||||
1. To create a swapfile of different size (now 4 GB), change the `count` to the desirable size.
|
||||
|
||||
Assign the correct permissions to the swapfile and make swap from the swapfile:
|
||||
|
||||
``` shell-session
|
||||
sh# chmod 600 /swapfile
|
||||
sh# mkswap /swapfile
|
||||
```
|
||||
|
||||
Enable the swap:
|
||||
|
||||
``` shell-session
|
||||
sh# swapon /swapfile
|
||||
```
|
||||
|
||||
and make it persistent by adding it to the `fstab`:
|
||||
|
||||
``` shell title="/etc/fstab"
|
||||
/swapfile none swap defaults 0 0
|
||||
```
|
||||
|
||||
Reconfigure the kernel:
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-reconfigure -f linux<version>
|
||||
```
|
||||
### Users
|
||||
|
||||
To run processes securely, in an environment with fewer privileges, a user is necessary.
|
||||
|
||||
Before creating the user, install `doas`, to be able to "do as" root when it is required:
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-install opendoas
|
||||
```
|
||||
|
||||
and configure `doas` by editing:
|
||||
|
||||
``` shell title="/etc/doas.conf"
|
||||
permit persist :wheel as root
|
||||
```
|
||||
|
||||
The alternative package `sudo` that is present in the `base-system` will be removed, since it is bloatware. To persist this, that is sudo will not be installed ever again on the system, insert:
|
||||
|
||||
``` shell title="/etc/xbps.d/nosudo.conf"
|
||||
ignorepkg=sudo
|
||||
```
|
||||
|
||||
and remove `sudo`:
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-remove -R sudo
|
||||
```
|
||||
|
||||
Create a symbolic link from `doas` to `sudo` to impose backward compatiblility:
|
||||
|
||||
``` shell-session
|
||||
sh# ln -s /bin/doas /bin/sudo
|
||||
```
|
||||
|
||||
We can add a user, set its password and add it to the `wheel` group with:
|
||||
|
||||
``` shell-session
|
||||
sh# useradd <user>
|
||||
sh# passwd <user>
|
||||
sh# usermod --append --groups wheel <user>
|
||||
```
|
||||
|
||||
You may have to change the shell of the user in `/etc/passwd` from `/sbin/nologin` to a shell from `/etc/shells`. Void Linux comes with `/bin/bash` by default:
|
||||
|
||||
``` shell title="/etc/passwd"
|
||||
<username>:x:1234:1234:<Full Name>:/home/<username>:/bin/<shell>
|
||||
```
|
||||
|
||||
If you have checked that doas works with the user then you can lock the root account because it imposes security risks if it is kept open. This can be done with:
|
||||
|
||||
``` shell-session
|
||||
sh# passwd -l root
|
||||
```
|
||||
|
||||
and by changing its login shell to:
|
||||
|
||||
``` shell title="/etc/passwd"
|
||||
root:x:0:0:root:/root:/sbin/nologin
|
||||
```
|
||||
|
||||
### Networking
|
||||
|
||||
For desktop use `NetworkManager` is preferred over `dhcpcd` as network daemon, due to its versatility, i.e. Wi-Fi and VPN compatibility, MAC randomisation, et cetera. Install `NetworkManager` with:
|
||||
|
||||
``` shell-session
|
||||
sh# xbps-install NetworkManager
|
||||
```
|
||||
|
||||
Configure `NetworkManager` with MAC randomisation:
|
||||
|
||||
``` shell title="/etc/NetworkManager/NetworkManager.conf"
|
||||
[main]
|
||||
hostname-mode=none
|
||||
plugins=ifupdown,keyfile
|
||||
|
||||
[ifupdown]
|
||||
managed=true
|
||||
|
||||
[device]
|
||||
wifi.scan-rand-mac-address=yes
|
||||
|
||||
[connection-mac-randomization]
|
||||
ethernet.cloned-mac-address=random
|
||||
wifi.cloned-mac-address=random
|
||||
```
|
||||
|
||||
Disable `dhcpcd` and enable the `NetworkManager` daemon and its dependency, the `dbus` daemon:
|
||||
|
||||
``` shell-session
|
||||
sh# rm -rf /var/service/dhcpcd
|
||||
sh# ln -s /etc/sv/dbus /var/service/
|
||||
sh# ln -s /etc/sv/NetworkManager /var/service/
|
||||
```
|
||||
|
||||
For users to be able to modify connections on the system they will have to be added to the `network` group.
|
||||
|
||||
## Concluding remarks
|
||||
|
||||
This is the bare minimum for a Void Linux desktop system. Some additional features such as bluetooth, laptop battery management, printer compatiblity, et cetera, have been documented well in the [Void Handbook](https://docs.voidlinux.org/about/index.html), and can thus be found there. The next steps are the improvement of the security of the system and the configuration of the graphical user interface.
|
459
docs/posts/02-alpine-install.md
Normal file
459
docs/posts/02-alpine-install.md
Normal file
|
@ -0,0 +1,459 @@
|
|||
---
|
||||
title: An Alpine Linux base installation
|
||||
slug: alpine-linux-base-install
|
||||
date: 2024-08-12
|
||||
draft: false
|
||||
authors:
|
||||
- luc
|
||||
- nils
|
||||
tags:
|
||||
- Alpine Linux
|
||||
categories:
|
||||
- Base installation
|
||||
---
|
||||
|
||||
This blog entry will demonstrate how to install `x86_64` [Alpine Linux](https://www.alpinelinux.org/) for a server application. Alpine Linux will run on a raid configured encrypted ZFS filesystem with automatic decryption using TPM. 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.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## Provisioning
|
||||
|
||||
Flash the Alpine Linux extended ISO and make sure the secureboot keys are reset and TPM is enabled in the BIOS of the host.
|
||||
|
||||
After booting the Alpine Linux extended ISO, partition the disks. For this action internet is required since `zfs`, `sgdisk` and various other necessary packages are not included on the extended ISO, therefore they need to be obtained from the alpine package repository.
|
||||
|
||||
To set it up the `setup-interfaces` and `setup-apkrepos` scripts present on the Alpine Linux ISO will be used.
|
||||
|
||||
``` shell-session
|
||||
sh# setup-interfaces -ar #(1)!
|
||||
sh# setup-apkrepos -c1
|
||||
```
|
||||
|
||||
1. To use Wi-Fi simply run `setup-interfaces -r` and select `wlan0` or similar.
|
||||
|
||||
A few packages will have to be installed first.
|
||||
|
||||
``` shell-session
|
||||
sh# apk add zfs lsblk sgdisk wipefs dosfstools mdadm zlevis
|
||||
```
|
||||
|
||||
and load the ZFS kernel module:
|
||||
|
||||
``` shell-session
|
||||
sh# modprobe zfs
|
||||
```
|
||||
|
||||
Define the disks you want to use for this install:
|
||||
|
||||
``` shell-session
|
||||
sh# export disks="/dev/disk/by-id/<id-disk-1> ... /dev/disk/by-id/<id-disk-n>"
|
||||
```
|
||||
|
||||
with `<id-disk-n>` for `n` an integer, 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, the best practice changes to using serial Attached SCSI (SAS), 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:
|
||||
|
||||
``` shell-session
|
||||
sh# 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:
|
||||
|
||||
``` shell-session
|
||||
sh# for disk in $disks; do
|
||||
> sgdisk -n 1:1m:+512m -t 1:ef00 $disk
|
||||
> sgdisk -n 2:0:-10m -t 2:8300 $disk
|
||||
> done
|
||||
```
|
||||
|
||||
Reload the device nodes:
|
||||
|
||||
``` shell-session
|
||||
sh# mdev -s
|
||||
```
|
||||
|
||||
Define the EFI partitions:
|
||||
|
||||
``` shell-session
|
||||
sh# export efiparts=""
|
||||
sh# for disk in $disks; do
|
||||
> efipart=${disk}-part-1
|
||||
> efiparts="$efiparts $efipart"
|
||||
> done
|
||||
```
|
||||
|
||||
Create a `mdraid` array on the EFI partitions:
|
||||
|
||||
``` shell-session
|
||||
sh# modprobe raid1
|
||||
sh# mdadm --create --level 1 --metadata 1.0 --raid-devices <n> /dev/md/esp $efiparts
|
||||
sh# mdadm --assemble --scan
|
||||
```
|
||||
|
||||
Format the array with a FAT32 filesystem:
|
||||
|
||||
``` shell-session
|
||||
sh# mkfs.fat -F 32 /dev/md/esp
|
||||
```
|
||||
|
||||
## ZFS pool creation
|
||||
|
||||
Define the pool partitions:
|
||||
|
||||
``` shell-session
|
||||
sh# export poolparts=""
|
||||
sh# for disk in $disks; do
|
||||
> poolpart=${disk}-part-2
|
||||
> poolparts="$poolparts $poolpart"
|
||||
> done
|
||||
```
|
||||
|
||||
The ZFS system pool is going to be encrypted. First generate an encryption key and save it temporarily to the file `/tmp/rpool.key` with:
|
||||
|
||||
``` shell-session
|
||||
sh# cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1 > /tmp/rpool.key && cat /tmp/rpool.key
|
||||
```
|
||||
|
||||
> While `zlevis` is used for automatic decryption, this key is required when changes are made to the BIOS or secureboot, so make sure to save it.
|
||||
|
||||
Create the system pool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool create -f \
|
||||
-o ashift=12 \
|
||||
-O compression=lz4 \
|
||||
-O acltype=posix \
|
||||
-O xattr=sa \
|
||||
-O dnodesize=auto \
|
||||
-O encryption=on \
|
||||
-O keyformat=passphrase \
|
||||
-O keylocation=prompt \
|
||||
-m none \
|
||||
rpool 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:
|
||||
|
||||
``` shell-session
|
||||
sh# zfs create -o mountpoint=none rpool/root
|
||||
sh# zfs create -o mountpoint=legacy -o quota=24g rpool/root/alpine
|
||||
sh# zfs create -o mountpoint=legacy -o quota=16g rpool/root/alpine/var
|
||||
sh# zfs create -o mountpoint=/home -o atime=off -o setuid=off -o devices=off -o quota=<home-quota> rpool/home
|
||||
```
|
||||
|
||||
> Setting the `<home-quota>` depends on the total size of the pool, generally try to reserve some empty space in the pool.
|
||||
|
||||
Write the encryption key to TPM with `zlevis`:
|
||||
|
||||
``` shell-session
|
||||
sh# zlevis encrypt rpool '{"pcr_ids":"0,5,7"}' < /tmp/rpool.key #(1)!
|
||||
```
|
||||
|
||||
1. See [zlevis functionality](https://docs.ampel.dev/zlevis/functionality/) to see the functionality of each `pcr_id`, and the other options that can be set.
|
||||
|
||||
> To check if it worked, perform `zlevis decrypt rpool`.
|
||||
|
||||
Finally, export the zpool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool export rpool
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
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 live (ISO) environment.
|
||||
|
||||
First import and decrypt the system pool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool import -N -R /mnt rpool
|
||||
sh# zfs load-key -L file:///tmp/rpool.key rpool
|
||||
```
|
||||
|
||||
Then mount the datasets and the ESP on `/mnt`:
|
||||
|
||||
``` shell-session
|
||||
sh# mount -t zfs rpool/root/alpine /mnt
|
||||
sh# mkdir /mnt/var
|
||||
sh# mount -t zfs rpool/root/alpine/var /mnt/var
|
||||
sh# mkdir /mnt/efi
|
||||
sh# mount -t vfat /dev/md/esp /mnt/efi
|
||||
|
||||
```
|
||||
|
||||
Now we may install Alpine Linux with the `setup-disk` script:
|
||||
|
||||
``` shell-session
|
||||
sh# export BOOTLOADER=none
|
||||
sh# setup-disk -m sys /mnt
|
||||
```
|
||||
|
||||
To have a functional chroot into the system, bind the system process directories:
|
||||
|
||||
``` shell-session
|
||||
sh# for dir in dev proc sys run; do
|
||||
> mount --rbind --make-rslave /$dir /mnt/$dir
|
||||
> done
|
||||
sh# chroot /mnt
|
||||
```
|
||||
|
||||
The other setup scripts can be used to configure key aspects of the system. Besides that a few necessary services have to be activated.
|
||||
|
||||
``` shell-session
|
||||
sh# setup-hostname <hostname>
|
||||
sh# setup-keymap us
|
||||
sh# setup-timezone -i <area>/<subarea>
|
||||
sh# setup-ntp openntpd
|
||||
sh# setup-sshd -c dropbear
|
||||
sh# rc-update add acpid default
|
||||
sh# rc-update add seedrng boot
|
||||
sh# passwd root #(1)!
|
||||
```
|
||||
|
||||
1. The root password does not really matter because it is going to be locked after a user has been created.
|
||||
|
||||
Set the `hwclock` to use `UTC` and disable writing the time to hardware. Running a NTP negates its usability.
|
||||
|
||||
``` shell title="/etc/conf.d/hwclock"
|
||||
clock="UTC"
|
||||
clock_hctosys="NO"
|
||||
clock_systohc="NO"
|
||||
```
|
||||
|
||||
Configure the ESP raid array to mount:
|
||||
|
||||
``` shell-session
|
||||
sh# modprobe raid1
|
||||
sh# echo raid1 >> /etc/modules-load.d/raid1.conf
|
||||
sh# mdadm --detail --scan >> /etc/mdadm.conf
|
||||
sh# rc-update add mdadm boot
|
||||
sh# rc-update add mdadm-raid boot
|
||||
```
|
||||
|
||||
Configure ZFS to mount:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add zfs-mount sysinit
|
||||
sh# rc-update add zfs-import sysinit
|
||||
sh# rc-update add zfs-load-key sysinit
|
||||
```
|
||||
|
||||
> If a faster boot time is preferred, `zfs-import` and `zfs-load-key` can be omitted in certain cases.
|
||||
|
||||
Edit the fstab to set the correct mounts:
|
||||
|
||||
``` shell title="/etc/fstab"
|
||||
rpool/root/alpine / zfs rw,noatime,xattr,posixacl,casesensitive 0 1
|
||||
rpool/root/alpine/var /var zfs rw,noatime,nodev,nosuid,xattr,posixacl,casesensitive 0 2
|
||||
/dev/md/esp /efi vfat defaults,nodev,nosuid,noexec,umask=0077 0 2
|
||||
tmpfs /tmp tmpfs rw,nodev,nosuid,noexec,mode=1777 0 0
|
||||
proc /proc proc nodev,nosuid,noexec,hidepid=2 0 0
|
||||
```
|
||||
|
||||
Install the following packages to make `mkinitfs` compatible with secureboot and TPM decryption:
|
||||
|
||||
``` shell-session
|
||||
sh# apk add secureboot-hook sbctl zlevis zlevis-mkinitfs #(1)!
|
||||
```
|
||||
|
||||
1. The `mkinitfs-zlevis` package is as of this moment not yet in the alpine package repository, for the relevant steps see the [zlevis mkinitfs-implementation](https://docs.ampel.dev/zlevis/implementation/#mkinitfs).
|
||||
|
||||
Configure `mkinitfs` to disable the trigger and to add the `zlevis` module:
|
||||
|
||||
``` shell title="/etc/mkinitfs/mkinitfs.conf"
|
||||
features="... zlevis"
|
||||
disable_trigger="yes"
|
||||
```
|
||||
|
||||
The most important step is the creation of a UKI using the `secureboot-hook` of `mkinitfs`, which also automatically signs them. Configure the `kernel-hooks` to set the kernel cmdline options and secureboot:
|
||||
|
||||
``` shell title="/etc/kernel-hooks.d/secureboot.conf"
|
||||
cmdline="rw root=ZFS=rpool/root/alpine rootflags=noatime quiet splash"
|
||||
|
||||
signing_cert="/var/lib/sbctl/keys/db/db.pem"
|
||||
signing_key="/var/lib/sbctl/keys/db/db.key"
|
||||
|
||||
output_dir="/efi/EFI/Linux"
|
||||
output_name="alpine-linux-{flavor}.efi"
|
||||
```
|
||||
|
||||
Use `sbctl` to create secureboot keys and sign them:
|
||||
|
||||
``` shell-session
|
||||
sh# sbctl create-keys
|
||||
sh# sbctl enroll-keys #(1)!
|
||||
```
|
||||
|
||||
1. Whilst enrolling the keys it might be necessary to add the `--microsoft` flag if you are unable to use custom keys.
|
||||
|
||||
Set the cache-file of the ZFS pool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool set cachefile=/etc/zfs/zpool.cache rpool
|
||||
```
|
||||
|
||||
Now to see if everything went successfully, run:
|
||||
|
||||
``` shell-session
|
||||
sh# apk fix kernel-hooks
|
||||
```
|
||||
|
||||
and it should give no warnings if done properly.
|
||||
|
||||
To install `systemd-boot` as friendly bootloader:
|
||||
|
||||
``` shell-session
|
||||
sh# apk add systemd-boot
|
||||
sh# bootctl install
|
||||
```
|
||||
|
||||
> One may verify the signed files by running `sbctl verify`.
|
||||
|
||||
Configure `systemd-boot` to specify the timeout and the default OS :
|
||||
|
||||
``` shell title="/efi/loader/loader.conf"
|
||||
default alpine-linux-lts.efi
|
||||
timeout 2
|
||||
editor no
|
||||
```
|
||||
|
||||
Now exit the chroot and you should be able to reboot into a working Alpine system.
|
||||
|
||||
``` shell-session
|
||||
sh# exit
|
||||
sh# umount -lf /mnt
|
||||
sh# zpool export rpool
|
||||
sh# reboot
|
||||
```
|
||||
|
||||
## Post installation
|
||||
|
||||
### Repositories
|
||||
|
||||
To set the correct repositories configure:
|
||||
|
||||
``` shell title="/etc/apk/repositories"
|
||||
https://dl-cdn.alpinelinux.org/alpine/latest-stable/main
|
||||
https://dl-cdn.alpinelinux.org/alpine/latest-stable/community
|
||||
```
|
||||
|
||||
This will use the latest stable repository of Alpine (for example `v3.19`). To use a different version of Alpine simply change `latest-stable` to whatever version you want. Do note that you cannot (easily) downgrade your system's version.
|
||||
|
||||
There is also the `edge` repository which contains the latest packages, but is not recommended, due to the instability it imposes on the system.
|
||||
|
||||
> If a package is not yet in a stable release one may additionally configure:
|
||||
>
|
||||
> ``` shell title="/etc/apk/repositories"
|
||||
> @<repository> https://dl-cdn.alpinelinux.org/alpine/edge/<repository>
|
||||
> ```
|
||||
>
|
||||
> for the relevant `<repository>` and perform:
|
||||
>
|
||||
> ``` shell-session
|
||||
> sh# apk add <package>@<repository>
|
||||
> ```
|
||||
>
|
||||
> for the relevant `<package>`.
|
||||
|
||||
### Firmware and drivers
|
||||
|
||||
Install the device firmware for either AMD or Intel:
|
||||
|
||||
=== "AMD"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add amd-ucode
|
||||
```
|
||||
|
||||
=== "Intel"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add intel-ucode
|
||||
```
|
||||
|
||||
To make sure it is included during boot, regenerate the UKI with:
|
||||
|
||||
``` shell-session
|
||||
sh# apk fix kernel-hooks
|
||||
```
|
||||
|
||||
### Swap
|
||||
|
||||
To configure Swap install `zram-init`:
|
||||
|
||||
``` shell-session
|
||||
sh# apk add zram-init
|
||||
```
|
||||
|
||||
Configure `zram-init` to create a swap device of size one fourth of the ram size:
|
||||
|
||||
``` shell title="/etc/conf.d/zram-init"
|
||||
load_on_start="yes"
|
||||
unload_on_stop="yes"
|
||||
num_devices="1"
|
||||
|
||||
type0="swap"
|
||||
size0=`LC_ALL=C free -m | awk '/^mem:/{print int($2/4)}'`
|
||||
maxs0=1
|
||||
algo0=zstd
|
||||
labl0=zram_swap
|
||||
```
|
||||
|
||||
and add `zram-init` to the default runlevel:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add zram-init default
|
||||
```
|
||||
|
||||
### Users
|
||||
|
||||
To run applications securely, in an environment with fewer privileges, a user is necessary.
|
||||
|
||||
Before creating the user, install `doas`. To be able to "do as" root when it is required:
|
||||
|
||||
``` shell-session
|
||||
sh# apk add doas
|
||||
```
|
||||
|
||||
and configure `doas` by editing:
|
||||
|
||||
``` shell title="/etc/doas.d/wheel.conf"
|
||||
permit persist :wheel as root
|
||||
```
|
||||
|
||||
A user can be added in Alpine Linux with the `setup-user` script. Here we can specify the name, groups and more:
|
||||
|
||||
```
|
||||
# setup-user -g wheel <username>
|
||||
# passwd <username>
|
||||
```
|
||||
|
||||
You may have to change the shell of the user in `/etc/passwd` from `/sbin/nologin` to a shell from `/etc/shells`. Alpine Linux comes with `/bin/ash` by default:
|
||||
|
||||
``` shell title="/etc/passwd"
|
||||
<username>:x:1234:1234:<Full Name>:/home/<username>:/bin/<shell>
|
||||
```
|
||||
|
||||
If you have checked that `doas` works with the user then you can lock the root account because it imposes security risks if it is kept open. This can be done with:
|
||||
|
||||
```
|
||||
# passwd -l root
|
||||
```
|
||||
|
||||
and by changing its login shell to:
|
||||
|
||||
``` shell title="/etc/passwd"
|
||||
root:x:0:0:root:/root:/sbin/nologin
|
||||
```
|
||||
|
||||
## Concluding remarks
|
||||
|
||||
This is essentially it, you now have a fully operational alpine base system running, configured for server use. The next steps are the improvement of the security of the system and the configuration of the container management software.
|
315
docs/posts/03-security-improvements.md
Normal file
315
docs/posts/03-security-improvements.md
Normal file
|
@ -0,0 +1,315 @@
|
|||
---
|
||||
title: Some Linux security improvements
|
||||
slug: linux-security-improvents
|
||||
date: 2024-08-14
|
||||
draft: false
|
||||
authors:
|
||||
- luc
|
||||
- nils
|
||||
tags:
|
||||
- Alpine Linux
|
||||
- Gentoo Linux
|
||||
categories:
|
||||
- Security
|
||||
---
|
||||
|
||||
The security of a Linux system can be further improved as will be outlined in the chapters of this blog entry. These chapters will discuss how to harden the different layers of the operating system and are based on the [Madaidans-insecurities page](https://madaidans-insecurities.github.io/guides/linux-hardening.html#kernel), various Linux man pages and the security considerations of [PlagueOS](https://0xacab.org/optout/plagueos/-/wikis/Security-Considerations) and [secureblue](https://secureblue.dev/features). Hardening the system is done to prevent as many exploits as possible. Such that in the end, you, and only you are in control of your system.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## Linux Security modules
|
||||
|
||||
Linux Security Modules (LSM) is a framework that allows the implementation of various security models in the Linux kernel.
|
||||
|
||||
These security modules may be enabled by adding them to the kernel `cmdline`:
|
||||
|
||||
``` shell title="/etc/kernel/cmdline"
|
||||
... lsm=landlock,lockdown,yama,integrity ...
|
||||
```
|
||||
|
||||
### Landlock
|
||||
|
||||
Landlock (`landlock`) is an access-control system that enables any processes to securely restrict themselves and their future children, i.e. sandboxing.
|
||||
|
||||
### Lockdown
|
||||
|
||||
Lockdown (`lockdown`) prevents both direct and indirect access to a running kernel image, attempting to protect against unauthorized modification of the kernel image and to prevent access to security and cryptographic data located in kernel memory, whilst still permitting driver modules to be loaded.
|
||||
|
||||
### Yama
|
||||
|
||||
Yama (`yama`) restricts the usage of `ptrace` (process-trace). Where `ptrace` is a system call that enables the tracing of a process or signalling to a process from within another process. Although by default (without yama) only limited communication is possible due to the small fixed-size block of memory that can be passed between the two processes. Yama attaches a `ptrace` permission level (0-3) to each process, with these levels defined as
|
||||
|
||||
| Level | Restriction |
|
||||
| :---: | ----------- |
|
||||
| 0 | No |
|
||||
| 1 | Descendants-only attach |
|
||||
| 2 | Admin-only attach |
|
||||
| 3 | No attach |
|
||||
|
||||
and therefore restricts which processes can trace or signal other processes, helping to mitigate certain types of attacks, such as privilege escalation.
|
||||
|
||||
### Integrity Policy Enforcement
|
||||
|
||||
Integrity Policy Enforcement (IPE) (`integrity`) takes a complementary approach to access control. Focusing on the immutable security properties inherent to system components. These properties are fundamental attributes or features of a system component that cannot be altered, ensuring a consistent and reliable basis for security decisions.
|
||||
|
||||
### SELinux
|
||||
|
||||
Security-Enhanced Linux implements mandatory access control (MAC) policies that restrict how processes interact with each other and with files.
|
||||
|
||||
### AppArmor
|
||||
|
||||
AppArmor is a security module that provides a simpler alternative to SELinux. It can dissallow access to files which the process would not require, as defined by the apparmor profile. Install the necessary packages: (1)
|
||||
{ .annotate }
|
||||
|
||||
1. For Gentoo Linux make sure to set the `apparmor` USE flag.
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add apparmor apparmor-utils apparmor-profiles
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a apparmor apparmor-utils apparmor-profiles
|
||||
```
|
||||
|
||||
and add it to the boot runlevel:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add apparmor boot
|
||||
```
|
||||
|
||||
Add `apparmor` to the kernel `cmdline` to make it operational:
|
||||
|
||||
``` shell title="/etc/kernel/cmdline"
|
||||
... lsm=...,apparmor apparmor=1 ...
|
||||
```
|
||||
|
||||
Then reconfigure the `kernel`:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk fix kernel-hooks
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge --config gentoo-kernel
|
||||
```
|
||||
|
||||
You can check the status of `apparmor` with `apparmor-utils`:
|
||||
|
||||
``` shell-session
|
||||
sh# aa-status
|
||||
```
|
||||
|
||||
## Kernel boot parameters
|
||||
|
||||
Boot parameters configure the bootloader to parse the relevant settings to the kernel at boot. Hardening the boot process will improve the overall security of the system. The listed boot parameters in this chapter can be parsed into the kernel `cmdline`:
|
||||
|
||||
``` shell title="/etc/kernel/cmdline"
|
||||
... slab_nomerge init_on_alloc=1 init_on_free=1 page_alloc.shuffle=1 pti=on ...
|
||||
```
|
||||
|
||||
### Mitigations of system vulnerabilities
|
||||
|
||||
* The setting `slab_nomerge` disables [slab merging](https://en.wikipedia.org/wiki/Slab_allocation) which helps to protect against heap exploitation.
|
||||
|
||||
* The settings `init_on_alloc=1 init_on_free=1` enable zeroing of memory during allocation and free time, which helps to mitigate use-after-free vulnerabilities.
|
||||
|
||||
* The setting `page_alloc.shuffle=1` randomises page allocator freelists, making page allocations less predictable. (1)
|
||||
{ .annotate }
|
||||
|
||||
1. Setting this parameter actually improves performance.
|
||||
|
||||
* The setting `pti=on` enables [kernel page-table isolation]() that mitigates the [meltdown vulnerability](https://en.wikipedia.org/wiki/Meltdown_(security_vulnerability)) and helps to protect against attempts to bypass [kernel address space layout randomisation](https://en.wikipedia.org/wiki/Address_space_layout_randomization).
|
||||
|
||||
* The setting `randomize_kstack_offset=on` randomises the kernel stack offset on each syscall, which helps to protect against attacks that rely on deterministic kernel stack layouts.
|
||||
|
||||
* The setting `vsyscall=none` disables the deprecated `vsyscalls`.
|
||||
|
||||
* The setting `debugfs=off` disables the debugfs, removing a source of sensitive information about the kernel.
|
||||
|
||||
* The setting `module.sig_enforce=1` enforces that only signed kernel modules can be loaded.
|
||||
|
||||
* The setting `lockdown=confidentiality` sets the strictest option of the `lockdown` security module.
|
||||
|
||||
* The setting `mce=0` causes the kernel to panic on uncorrectable errors in ECC memory. This setting is unnecessary for non-ECC memory. (1)
|
||||
{ .annotate }
|
||||
|
||||
1. ECC memory from a security and a redundancy perspective is always recommended. The ZFS filesystem also functions better with ECC memory.
|
||||
|
||||
### Hardware specific mitigations of vulnerabilities
|
||||
|
||||
* The setting `spectre_v2=on` enables the mitigation of [spectre](https://en.wikipedia.org/wiki/Spectre_(security_vulnerability)), a speculative execution CPU vulnerability that is present in all pre-2019 CPUs.
|
||||
|
||||
* The setting `spec_store_bypass_disable=on` disables [Speculative Store Bypass](https://en.wikipedia.org/wiki/Speculative_Store_Bypass) (SSB), in all pre-2019 CPUs there is a vulnerability in the SSB.
|
||||
|
||||
* The setting `tsx=off` disables [Transactional Synchronisation Extensions](https://en.wikipedia.org/wiki/Transactional_Synchronization_Extensions) (TSX), which is a feature of pre-2019 Intel CPUs. TSXs are vulnerable to cache [side-channel attacks](https://en.wikipedia.org/wiki/Side-channel_attack).
|
||||
|
||||
* The setting `tsx_async_abort=full` mitigates the TSX vulnerability if you are stupid enough to keep TSX enabled.
|
||||
|
||||
* The setting `mds=full` enables the mitigation of the [Micro-architectural Data Sampling](https://en.wikipedia.org/wiki/Microarchitectural_Data_Sampling) (MDS), a set of weaknesses in pre-2020 Intel x86_64 CPUs.
|
||||
|
||||
* The setting `l1tf=flush` mitigates the [L1 Terminal Fault vulnerability](https://docs.kernel.org/admin-guide/hw-vuln/l1tf.html) present in pre-2019, by conditional flushing of the Level 1 Data Cache.
|
||||
|
||||
* The setting `kvm.nx_huge_pages=force` mitigates the [iTLB multihit vulnerability](https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/multihit.html) present in pre-2019 Intel CPUs.
|
||||
|
||||
> So if you have a pre-2019 Intel CPU, throw it out of the window right now! The performance hit is quite significant with all these mitigations.
|
||||
|
||||
### Hardening the boot process
|
||||
|
||||
* The settings `quiet loglevel=0` prevent information leaks during boot and must be used in combination with the `kernel.printk` sysctl setting.
|
||||
|
||||
* The settings `rd.shell=0 rd.emergency.reboot=reboot` impose that at critical failure in the boot process the system should be rebooted and that the shell cannot be accessed at all times during this process. Hardening the boot process.
|
||||
|
||||
## Kernel sysctl settings
|
||||
|
||||
Kernel self-protection can be configured by creating:
|
||||
|
||||
``` shell title="/etc/sysctl.d/kernel.conf" linenums="1"
|
||||
kernel.kptr_restrict=2 #(1)!
|
||||
kernel.dmesg_restrict=1 #(2)!
|
||||
kernel.printk=3 3 3 3 #(3)!
|
||||
kernel.unprivileged_bpf_disabled=1 #(4)!
|
||||
net.core.bpf_jit_harden=2 #(5)!
|
||||
dev.tty.ldisc_autoload=0 #(6)!
|
||||
kernel.kexec_load_disabled=1 #(7)!
|
||||
kernel.sysrq=0 #(8)!
|
||||
kernel.perf_event_paranoid=3 #(9)!
|
||||
```
|
||||
|
||||
1. Mitigate kernel pointer leaks.
|
||||
2. Restrict kernel log to `CAP_SYSLOG` capability.
|
||||
3. Restrict kernel log in console during boot.
|
||||
4. Restrict eBPF to `CAP_SYSLOG` capability.
|
||||
5. Restrict eBPF to `CAP_SYSLOG` capability.
|
||||
6. Restrict TTY line disciplines to `CAP_SYS_MODULE` capability.
|
||||
7. Disable kexec (system call to boot another kernel during runtime).
|
||||
8. Disable SysRq key (debugging functionality).
|
||||
9. Restrict performance events to `CAP_PERFORM` capability.
|
||||
|
||||
Network protection can be configured by creating:
|
||||
|
||||
``` shell title="/etc/sysctl.d/network.conf" linenums="1"
|
||||
net.ipv4.icmp_echo_ignore_all=1 #(1)!
|
||||
net.ipv4.tcp_syncookies=1 #(2)!
|
||||
net.ipv4.tcp_rfc1337=1 #(3)!
|
||||
net.ipv4.tcp_sack=0 #(4)!
|
||||
net.ipv4.tcp_dsack=0 #(5)!
|
||||
net.ipv4.tcp_fack=0 #(6)!
|
||||
net.ipv4.conf.all.rp_filter=1 #(7)!
|
||||
net.ipv4.conf.default.rp_filter=1 #(8)!
|
||||
net.ipv4.conf.all.accept_redirects=0 #(9)!
|
||||
net.ipv4.conf.default.accept_redirects=0 #(10)!
|
||||
net.ipv4.conf.all.secure_redirects=0 #(11)!
|
||||
net.ipv4.conf.default.secure_redirects=0 #(12)!
|
||||
net.ipv6.conf.all.accept_redirects=0 #(13)!
|
||||
net.ipv6.conf.default.accept_redirects=0 #(14)!
|
||||
net.ipv4.conf.all.send_redirects=0 #(15)!
|
||||
net.ipv4.conf.default.send_redirects=0 #(16)!
|
||||
net.ipv4.conf.all.accept_source_route=0 #(17)!
|
||||
net.ipv4.conf.default.accept_source_route=0 #(18)!
|
||||
net.ipv6.conf.all.accept_source_route=0 #(19)!
|
||||
net.ipv6.conf.default.accept_source_route=0 #(20)!
|
||||
```
|
||||
|
||||
1. Ignore all ICMP requests, to avoid [Smurf attacks](https://en.wikipedia.org/wiki/Smurf_attack).
|
||||
2. Restricts resources handling SYN requests, helps protect against [SYN flood attacks](https://en.wikipedia.org/wiki/SYN_flood).
|
||||
3. Drops RST packets for sockets in the time-wait state, to avoid [time-wait assassination attacks](https://datatracker.ietf.org/doc/html/rfc1337).
|
||||
4. Disables TCP SACK, for servers it could be relevant to keep this enabled.
|
||||
5. Disables TCP SACK, for servers it could be relevant to keep this enabled.
|
||||
6. Disables TCP SACK, for servers it could be relevant to keep this enabled.
|
||||
7. Enables source validation of packets received from all interfaces, to avoid [IP spoofing](https://en.wikipedia.org/wiki/IP_address_spoofing).
|
||||
8. Enables source validation of packets received from all interfaces, to avoid [IP spoofing](https://en.wikipedia.org/wiki/IP_address_spoofing).
|
||||
9. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
10. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
11. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
12. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
13. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
14. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
15. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
16. Disables ICMP redirect acceptance and sending to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
17. Disables source routing to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
18. Disables source routing to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
19. Disables source routing to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
20. Disables source routing to prevent [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
|
||||
Protection of the user space can be configured by creating:
|
||||
|
||||
``` shell title="/etc/sysctl.d/user.conf" linenums="1"
|
||||
kernel.yama.ptrace_scope=2 #(1)!
|
||||
vm.mmap_rnd_bits=32 #(2)!
|
||||
vm.mmap_rnd_compat_bits=16 #(3)!
|
||||
fs.protected_symlinks=1 #(4)!
|
||||
fs.protected_hardlinks=1 #(5)!
|
||||
fs.protected_fifos=2 #(6)!
|
||||
fs.protected_regular=2 #(7)!
|
||||
```
|
||||
|
||||
1. Restrict `ptrace` usage to level 2 (only processes with `CAP_SYS_PTRACE` capability).
|
||||
2. Increase the entropy for mmap ASLR, compatible with `x86_64`.
|
||||
3. Increase the entropy for mmap ASLR, compatible with `x86_64`.
|
||||
4. Restricts symlink following to only well-defined owner paths, preventing [TOC/TOU races](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use).
|
||||
5. Restricts symlink following to only well-defined owner paths, preventing [TOC/TOU races](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use).
|
||||
6. Prevent file creation in high-risk environments, helps to protect against [spoofing attakcs](https://en.wikipedia.org/wiki/Spoofing_attack).
|
||||
7. Prevent file creation in high-risk environments, helps to protect against [spoofing attakcs](https://en.wikipedia.org/wiki/Spoofing_attack).
|
||||
|
||||
## Hardened memory allocator
|
||||
|
||||
The default memory allocator of `musl` is already reasonably secure but not as secure as [hardened-malloc](https://github.com/GrapheneOS/hardened_malloc/), install it with:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add hardened-malloc
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a hardened-malloc
|
||||
```
|
||||
|
||||
and set it to system-wide edit:
|
||||
|
||||
``` shell title="/etc/ld-musl-x86_64.path"
|
||||
/usr/lib/libhardened_malloc.so #(1)!
|
||||
/lib
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
```
|
||||
|
||||
1. If problems with graphical applications occur the light variant of hardened-malloc `/usr/lib/libhardened_malloc-light.so` may also be used instead of `/usr/lib/libhardened_malloc.so`.
|
||||
|
||||
To accomodate the large number of guard pages created by `hardened-malloc` impose that we should set the following:
|
||||
|
||||
``` shell title="/etc/sysctl.d/malloc.conf"
|
||||
vm.max_map_count=1048576
|
||||
```
|
||||
|
||||
## Entropy
|
||||
|
||||
Improve the security of the system by increasing the entropy with the `jitterentropy` kernel module, install it with:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add jitterentropy-library
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a jitterentropy
|
||||
```
|
||||
|
||||
and make sure that the module gets loaded:
|
||||
|
||||
``` shell title="/etc/modules-load.d/entropy.conf"
|
||||
jitterentropy_rng
|
||||
```
|
363
docs/posts/04-container-management-podman-runit.md
Normal file
363
docs/posts/04-container-management-podman-runit.md
Normal file
|
@ -0,0 +1,363 @@
|
|||
---
|
||||
title: Rootless container management with Podman and runit
|
||||
slug: rootless-container-management-with-podman-and-runit
|
||||
date: 2024-08-30
|
||||
draft: false
|
||||
authors:
|
||||
- luc
|
||||
tags:
|
||||
- Alpine Linux
|
||||
- Gentoo Linux
|
||||
categories:
|
||||
- Container management
|
||||
---
|
||||
|
||||
Containers and pods (a collection of containers in the same namespace) enables easy and secure management of hosted applications. Rootless containers and pods can be deployed on a server with [Podman](https://podman.io/) as the rootless container engine and [runit](https://smarden.org/runit/) as the user service manager. The service manager will be set-up to automatically start and update the containers and pods at boot and to periodically back-up the volumes and databases of the pods.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## User services with runsvdir
|
||||
|
||||
Using `runsvdir` requires `runit` to be installed on the system:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add runit
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a runit
|
||||
```
|
||||
|
||||
Now create an `openrc` entry that will manage `runsvdir`:
|
||||
|
||||
``` shell title="/etc/init.d/runsvdir-user"
|
||||
#!/sbin/openrc-run
|
||||
|
||||
user="${RC_SVCNAME##*.}"
|
||||
svdir="/home/${user}/.local/service"
|
||||
pidfile="/run/runsvdir-user.${user}.pid"
|
||||
|
||||
command="/usr/bin/runsvdir"
|
||||
command_args="$svdir"
|
||||
command_user="$user"
|
||||
command_background=true
|
||||
|
||||
depend()
|
||||
{
|
||||
after network-online
|
||||
}
|
||||
```
|
||||
|
||||
Make the entry executable, link user `<username>` and add the service to the default runlevel:
|
||||
|
||||
``` shell-session
|
||||
sh# chmod +x /etc/init.d/runsvdir-user
|
||||
sh# ln -s /etc/init.d/runsvdir-user /etc/init.d/runsvdir-user.<username>
|
||||
sh# rc-update add runsvdir-user.<username> default
|
||||
```
|
||||
|
||||
> This process can of course be repeated for any number of users.
|
||||
|
||||
## Container management with Podman
|
||||
|
||||
Install `podman` with:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add podman
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a podman
|
||||
```
|
||||
|
||||
Rootless `podman` requires `cgroups` to run, therefore add it to the default runlevel:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add cgroups default
|
||||
```
|
||||
|
||||
Set up the network namespace configuration for the user:
|
||||
|
||||
``` shell-session
|
||||
sh# modprobe tun
|
||||
sh# echo tun >> /etc/modules-load.d/tun.conf
|
||||
sh# for i in subuid subgid; do
|
||||
> echo <username>:100000:65536 >> /etc/$i
|
||||
> done
|
||||
```
|
||||
|
||||
Run the following container to verify if everything works:
|
||||
|
||||
``` shell-session
|
||||
sh$ podman run --rm hello-world
|
||||
```
|
||||
|
||||
### Management of containers
|
||||
|
||||
To run a single container create:
|
||||
|
||||
``` shell title="~/.config/sv/{container-name}/run"
|
||||
#!/bin/sh
|
||||
|
||||
command="/usr/bin/podman"
|
||||
command_args="run --replace --rm --name=<container-name> --network=pasta"
|
||||
env="<container-envs>"
|
||||
ports="<container-ports>"
|
||||
mounts="<container-mounts>"
|
||||
image="<container-image>"
|
||||
|
||||
exec 2>&1
|
||||
exec $command $command_args $env $ports $mounts $image
|
||||
```
|
||||
|
||||
Make it executable and link it to the service directory:
|
||||
|
||||
``` shell-session
|
||||
sh$ chmod +x ~/.config/sv/<container-name>/run
|
||||
sh$ ln -s <home>/.config/sv/<container-name> <home>/.local/service
|
||||
```
|
||||
|
||||
### Management of pods
|
||||
|
||||
To check if a pod is running, create:
|
||||
|
||||
``` shell title="~/.local/bin/checkpod"
|
||||
#!/bin/sh
|
||||
|
||||
. ./conf
|
||||
|
||||
exec 2>&1
|
||||
|
||||
state=0
|
||||
|
||||
while [ $state == 0 ]
|
||||
do
|
||||
sleep 10
|
||||
$command pod inspect ${name}-pod | grep -q '"State": "Running"' || state=1
|
||||
done
|
||||
```
|
||||
|
||||
and make it executable with:
|
||||
|
||||
``` shell-session
|
||||
sh$ chmod +x ~/.local/bin/checkpod
|
||||
```
|
||||
|
||||
To run a pod configured with `~/.config/pods/<pod-name>/<pod-name>.yml`, see [alpine-server](https://git.lucbijl.nl/luc/alpine-server) for examples, we setup the `runit` entry with a `conf`, `run` and `finish` structure. Therefore create:
|
||||
|
||||
``` shell title="~/.config/sv/{pod-name}/conf"
|
||||
name="<pod-name>"
|
||||
home="<home>"
|
||||
pod_location="${home}/.config/pods/<pod-name>"
|
||||
bin_location="${home}/.local/bin"
|
||||
command="/usr/bin/podman"
|
||||
command_args="--replace --network=pasta"
|
||||
```
|
||||
|
||||
will contain all the relevant configuration specific to the pod. Now create:
|
||||
|
||||
``` shell title="~/.config/sv/{pod-name}/run"
|
||||
#!/bin/sh
|
||||
|
||||
. ./conf
|
||||
|
||||
exec 2>&1
|
||||
$command kube play $command_args ${pod_location}/${name}-pod.yml
|
||||
exec ${bin_location}/checkpod
|
||||
```
|
||||
|
||||
and create:
|
||||
|
||||
``` shell title="~/.config/sv/{pod-name}/finish"
|
||||
#!/bin/sh
|
||||
|
||||
. ./conf
|
||||
|
||||
exec 2>&1
|
||||
exec $command kube down ${pod_location}/${name}-pod.yml
|
||||
```
|
||||
|
||||
will stay the same for any pod.
|
||||
|
||||
Make both `run` and `finish` executable:
|
||||
|
||||
``` shell-session
|
||||
sh$ chmod +x ~/.config/sv/<pod-name>/run
|
||||
sh$ chmod +x ~/.config/sv/<pod-name>/finish
|
||||
```
|
||||
|
||||
Finally, link the pod to the service directory:
|
||||
|
||||
``` shell-session
|
||||
sh$ ln -s <home>/.config/sv/<pod-name> <home>/.local/service
|
||||
```
|
||||
|
||||
### Backup of volumes and databases
|
||||
|
||||
To back up volumes of containers and postgresql databases create:
|
||||
|
||||
``` shell title="~/.local/bin/dump"
|
||||
#!/bin/sh
|
||||
|
||||
command="/usr/bin/podman"
|
||||
|
||||
# Dumps databases
|
||||
|
||||
postgres_databases="<list-of-postgres-databases>"
|
||||
|
||||
for database in $postgres_databases
|
||||
do
|
||||
exec $command exec -it ${database}-pod-postgres sh -c "pg_dumpall -U postgres | gzip > /dump/${database}.sql.gz"
|
||||
done
|
||||
|
||||
# Exports volumes
|
||||
|
||||
volumes="<list-of-volumes>"
|
||||
|
||||
for volume in $volumes
|
||||
do
|
||||
exec $command volume export $volume --output <home>/.volumes/${volume}.tar
|
||||
done
|
||||
```
|
||||
|
||||
Make it executable:
|
||||
|
||||
``` shell-session
|
||||
sh$ chmod +x ~/.local/bin/dump
|
||||
```
|
||||
|
||||
Automate it with `snooze`:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add snooze
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a snooze
|
||||
```
|
||||
|
||||
and create the corresponding `runit` entry:
|
||||
|
||||
``` shell title="~/.config/sv/dump/run"
|
||||
#!/bin/sh
|
||||
|
||||
exec 2>&1
|
||||
exec snooze -H* /home/neutrino/.local/bin/dump
|
||||
```
|
||||
|
||||
which executes dump every hour.
|
||||
|
||||
Make it executable and link it to the service directory:
|
||||
|
||||
``` shell-session
|
||||
sh$ chmod +x ~/.config/sv/dump/run
|
||||
sh$ ln -s <home>/.config/dump <home>/.local/service
|
||||
```
|
||||
|
||||
Then `restic` can be used to back up the `.dump` and `.volumes` folders to another server if necessary.
|
||||
|
||||
By creating:
|
||||
|
||||
``` shell title="~/.local/bin/load"
|
||||
#!/bin/sh
|
||||
|
||||
command="/usr/bin/podman"
|
||||
|
||||
# Loads dumped databases
|
||||
|
||||
postgres_databases="<list-of-postgres-databases>"
|
||||
|
||||
for database in $postgres_databases
|
||||
do
|
||||
exec $command exec -it ${database}-pod-postgres sh -c "gunzip -c /dump/${database}.sql.gz | psql -U postgres"
|
||||
done
|
||||
|
||||
# Imports volumes
|
||||
|
||||
volumes="<list-of-volumes>"
|
||||
|
||||
for volume in $volumes
|
||||
do
|
||||
exec $command volume import $volume <home>/.volumes/${volume}.tar
|
||||
done
|
||||
```
|
||||
|
||||
the volumes and postgresql databases can be reloaded.
|
||||
|
||||
Do not forget to make it executable:
|
||||
|
||||
``` shell-session
|
||||
sh$ chmod +x ~/.local/bin/load
|
||||
```
|
||||
|
||||
## Proxying with Caddy
|
||||
|
||||
While it would be more optimal to run a reverse proxy in a container and link the network namespaces to this container, this is unfortunately not possible with `pasta` user network namespaces. Therefore, the reverse proxy should be run in front of the containers and thus on the system.
|
||||
|
||||
Caddy is a simple and modern web-server that supports automatic HTTPS and can act as a reverse-proxy. Install `caddy` and `libcap` (necessary dependency) with:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk add caddy libcap
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a caddy libcap
|
||||
```
|
||||
|
||||
Give `caddy` privileges to accesss all ports: (1)
|
||||
{ .annotate }
|
||||
|
||||
1. Such that we are able to run `caddy` rootless.
|
||||
|
||||
``` shell-session
|
||||
sh# setcap cap_net_bind_service=+ep /usr/sbin/caddy
|
||||
```
|
||||
|
||||
Create the `caddyfile` (1) according to your needs (2). Then convert it with the following to make it persistent:
|
||||
{ .annotate }
|
||||
|
||||
1. The configuration file of `caddy`.
|
||||
2. See [alpine-server](https://git.lucbijl.nl/luc/alpine-server) for examples.
|
||||
|
||||
``` shell-session
|
||||
sh$ caddy adapt -c ~/.config/caddy/caddyfile -p > ~/.config/caddy/caddy.json
|
||||
```
|
||||
|
||||
Create the corresponding `runit` entry for `caddy`:
|
||||
|
||||
``` shell title="~/.config/sv/caddy/run"
|
||||
#!/bin/sh
|
||||
|
||||
command="/usr/sbin/caddy"
|
||||
command_args="run"
|
||||
|
||||
exec ps | grep '[${command}] ${command_args}' > /dev/null
|
||||
|
||||
if [$? != 0]; then
|
||||
exec 2>&1
|
||||
exec $command $command_args
|
||||
fi
|
||||
```
|
||||
|
||||
Make it executable and link it to the service directory:
|
||||
|
||||
``` shell-session
|
||||
sh$ chmod +x ~/.config/sv/caddy/run
|
||||
sh$ ln -s <home>/.config/sv/caddy <home>/.local/service
|
||||
```
|
602
docs/posts/05-gentoo-openrc-install.md
Normal file
602
docs/posts/05-gentoo-openrc-install.md
Normal file
|
@ -0,0 +1,602 @@
|
|||
---
|
||||
title: A hardened Gentoo-Linux/openrc base installation
|
||||
slug: gentoo-openrc-base-install
|
||||
date: 2025-07-19
|
||||
draft: false
|
||||
authors:
|
||||
- nils
|
||||
- luc
|
||||
tags:
|
||||
- Gentoo Linux
|
||||
categories:
|
||||
- Base installation
|
||||
---
|
||||
|
||||
This blog entry will demonstrate how to install a hardened `x86_64` [Gentoo Linux](https://www.gentoo.org/) `musl`/`openrc`/`UEFI signed UKI` operating system on an encrypted `ZFS` pool with automatic decryption using TPM. This entry is based on the [Gentoo `x86_64` handbook](https://wiki.gentoo.org/wiki/Handbook:AMD64) and the [Gentoo wiki](https://wiki.gentoo.org/wiki/Main_Page). Gentoo supplies the right tools to build a Linux operating system from scratch, suited to the hardware and needs of the user. This form of customizability and optimizability together with the strong community behind Gentoo makes it a good choice for a desktop operating system.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## Provisioning
|
||||
|
||||
To install Gentoo this guide will be using the [Alpine Extended ISO](https://alpinelinux.org/downloads/). It provides all of the necessary utilities for bootstrapping Gentoo. Make sure to boot with secureboot in setup mode or to already have keys ready to deploy.
|
||||
|
||||
After booting the Alpine Linux extended ISO, partition the disks. For this action internet is required since `zfs`, `sgdisk` and various other necessary packages are not included on the extended ISO, therefore they need to be obtained from the Alpine package repository.
|
||||
|
||||
Set it up with `setup-interfaces` and `setup-apkrepos`:
|
||||
|
||||
``` shell-session
|
||||
sh# setup-interfaces -ar #(1)!
|
||||
sh# setup-apkrepos -c1
|
||||
```
|
||||
|
||||
1. To use Wi-Fi simply run `setup-interfaces -r` and select `wlan0` or similar.
|
||||
|
||||
Install the necessary packages:
|
||||
|
||||
``` shell-session
|
||||
sh# apk add zfs lsblk sgdisk wipefs dosfstools zlevis #(1)!
|
||||
```
|
||||
|
||||
1. The `zlevis` package is as of this moment not yet in the alpine package repository. Try to get it into the `bin` via a different method and add its dependencies `tpm2-tools` and `jose`.
|
||||
|
||||
and load the `ZFS` kernel module:
|
||||
|
||||
``` shell-session
|
||||
sh# modprobe zfs
|
||||
```
|
||||
|
||||
Wipe the existing disk partitions:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool labelclear -f /dev/<disk>
|
||||
sh# wipefs -a /dev/<disk>
|
||||
sh# sgdisk --zap-all /dev/<disk>
|
||||
```
|
||||
|
||||
Create on the disk an `EFI system` partition (ESP) and a `Linux filesystem` partition (LFP):
|
||||
|
||||
``` shell-session
|
||||
sh# sgdisk -n 1:1m:+512m -t 1:ef00 /dev/<disk>
|
||||
sh# sgdisk -n 2:0:-10m -t 2:8300 /dev/<disk>
|
||||
```
|
||||
|
||||
Reload the device nodes:
|
||||
|
||||
``` shell-session
|
||||
sh# mdev -s
|
||||
```
|
||||
|
||||
Format the ESP with a FAT32 filesystem:
|
||||
|
||||
``` shell-session
|
||||
sh# mkfs.fat -F 32 -n esp /dev/<disk>1
|
||||
```
|
||||
|
||||
## ZFS pool creation
|
||||
|
||||
The `ZFS` system pool is going to be encrypted. First generate an encryption key and save it temporarily to the file `/tmp/rpool.key` with:
|
||||
|
||||
``` shell-session
|
||||
sh# cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1 > /tmp/rpool.key && cat /tmp/rpool.key
|
||||
```
|
||||
|
||||
> All the while we use `zlevis` for automatic decryption, this key is required when making changes are made to the BIOS or secureboot, so make sure to save it.
|
||||
|
||||
Create the system pool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool create -f \
|
||||
-o ashift=12 \
|
||||
-O compression=lz4 \
|
||||
-O acltype=posix \
|
||||
-O xattr=sa \
|
||||
-O dnodesize=auto \
|
||||
-O encryption=on \
|
||||
-O keyformat=passphrase \
|
||||
-O keylocation=prompt \
|
||||
-m none \
|
||||
rpool /dev/<disk>2
|
||||
```
|
||||
|
||||
Then create the system datasets:
|
||||
|
||||
``` shell-session
|
||||
sh# zfs create -o mountpoint=none rpool/root
|
||||
sh# zfs create -o mountpoint=legacy -o quota=48g rpool/root/gentoo
|
||||
sh# zfs create -o mountpoint=legacy -o quota=32g rpool/root/gentoo/var
|
||||
sh# zfs create -o mountpoint=/home -o atime=off -o setuid=off -o devices=off -o quota=<home-quota> rpool/home
|
||||
```
|
||||
|
||||
> Setting the `<home-quota>` depends on the total size of the pool, generally try to reserve some empty space in the pool.
|
||||
|
||||
Write the encryption key to TPM with `zlevis`:
|
||||
|
||||
``` shell-session
|
||||
sh# zlevis encrypt rpool '{"pcr_ids":"0,5,7"}' < /tmp/rpool.key
|
||||
```
|
||||
|
||||
> To check if it worked, perform `zlevis decrypt rpool`.
|
||||
|
||||
Finally, export the zpool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool export rpool
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
To install Gentoo Linux on the system, the ESP and the datasets of the system pool have to be mounted to the live (ISO) environment.
|
||||
|
||||
First import and decrypt the system pool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool import -N -R /mnt rpool
|
||||
sh# zfs load-key -L file:///tmp/rpool.key rpool
|
||||
```
|
||||
|
||||
Then mount the datasets and the ESP on `/mnt`:
|
||||
|
||||
``` shell-session
|
||||
sh# mount -t zfs rpool/root/gentoo /mnt
|
||||
sh# mkdir /mnt/var
|
||||
sh# mount -t zfs rpool/root/gentoo/var /mnt/var
|
||||
sh# mkdir /mnt/efi
|
||||
sh# mount -t vfat /dev/disk/by-label/esp /mnt/efi
|
||||
```
|
||||
|
||||
Now we are going to fetch a `stage 3` tarball, the archive containing a minimal Gentoo environment. Which will act as the seed of the Gentoo install. Replace the `<release_date>` with the latest stage file release:
|
||||
|
||||
``` shell-session
|
||||
sh# wget https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-musl-hardened/stage3-amd64-musl-hardened-<release_date>.tar.xz #(1) #(2)!
|
||||
```
|
||||
|
||||
1. It is also possible to use `links` instead of `wget` which provides a small user interface for navigation:
|
||||
|
||||
``` shell-session
|
||||
sh# links https://distfiles.gentoo.org/releases/amd64/autobuilds/current-stage3-amd64-musl-hardened
|
||||
```
|
||||
|
||||
2. There are also other mirrors like `https://ftp.snt.utwente.nl/pub/os/linux/gentoo/releases/amd64/autobuilds/current-stage3-amd64-musl-hardened/` which might provide a faster download depending on your location. Check out <https://www.gentoo.org/downloads/mirrors/> for other mirrors.
|
||||
|
||||
Unpack the stage file in the root of the system:
|
||||
|
||||
``` shell-session
|
||||
sh# tar -xpf stage3-*.tar.xz --numeric-owner -C /mnt
|
||||
```
|
||||
|
||||
To have a functional chroot into the system, copy `resolv.conf` and bind the system process directories:
|
||||
|
||||
``` shell-session
|
||||
sh# cp --dereference /etc/resolv.conf /mnt/etc/
|
||||
sh# for dir in dev proc sys run; do
|
||||
> mount --rbind --make-rslave /$dir /mnt/$dir
|
||||
> done
|
||||
sh# chroot /mnt
|
||||
```
|
||||
|
||||
Configure `portage` (1) before doing anything else, an example file of its `make.conf` is given below:
|
||||
{ .annotate }
|
||||
|
||||
1. The package manager of Gentoo Linux, which provisions build files from the [Gentoo ebuild repository](https://wiki.gentoo.org/wiki/Ebuild_repository#The_Gentoo_ebuild_repository) or any [additional ebuild repositories](https://wiki.gentoo.org/wiki/Portage#Ebuild_repositories).
|
||||
|
||||
|
||||
``` shell title="/etc/portage/make.conf" linenums="1"
|
||||
# A Gentoo installation is highly personal so diverting from this example is encouraged.
|
||||
# Please consult /usr/share/portage/config/make.conf.example for a more detailed example.
|
||||
|
||||
# Compiler flags
|
||||
COMMON_FLAGS="-march=native -O3 -pipe"
|
||||
CFLAGS="${COMMON_FLAGS}"
|
||||
CXXFLAGS="${COMMON_FLAGS}"
|
||||
FCFLAGS="${COMMON_FLAGS}"
|
||||
FFLAGS="${COMMON_FLAGS}"
|
||||
RUSTFLAGS="${RUSTFLAGS} -C target-cpu=native"
|
||||
|
||||
# Compile options
|
||||
MAKEOPTS="-j8 -l9" #(1)!
|
||||
|
||||
# WARNING: Changing your CHOST is not something that should be done lightly.
|
||||
# Please consult https://wiki.gentoo.org/wiki/Changing_the_CHOST_variable before changing.
|
||||
CHOST="x86_64-pc-linux-musl"
|
||||
|
||||
# NOTE: This stage was built with the bindist USE flag enabled
|
||||
|
||||
# This sets the language of build output to English.
|
||||
# Please keep this setting intact when reporting bugs.
|
||||
LC_MESSAGES=C.utf8
|
||||
|
||||
# Logging
|
||||
PORTAGE_ELOG_CLASSES="log warn error"
|
||||
PORTAGE_LOGDIR="/var/log/portage"
|
||||
PORTAGE_LOGDIR_CLEAN="find \"\${PORTAGE_LOGDIR}\" -type f ! -name \"summary.log*\" -mtime +7 -delete"
|
||||
|
||||
# Only accept free licenses
|
||||
ACCEPT_LICENSE="-* @FREE"
|
||||
|
||||
# USE flags
|
||||
USE="${USE} -debug -telemetry -modemmanager -ext* -ppp -systemd -elogind -X -kde -gnome -gtk-doc -webengine hardened dist-kernel udev initramfs hostonly secureboot modules-sign apparmor acpi networkmanager dbus hwaccel bash-completion man pam pipewire vulkan wayland eme-free jpeg png svg" #(2)!
|
||||
|
||||
# Emerge flags
|
||||
EMERGE_DEFAULT_OPTS="${EMERGE_DEFAULT_OPTS} --with-bdeps y --quiet-build y"
|
||||
|
||||
# Secureboot settings
|
||||
SECUREBOOT_SIGN_KEY="/var/lib/sbctl/keys/db/db.key"
|
||||
SECUREBOOT_SIGN_CERT="/var/lib/sbctl/keys/db/db.pem"
|
||||
MODULES_SIGN_KEY="${SECUREBOOT_SIGN_KEY}"
|
||||
MODULES_SIGN_CERT="${SECUREBOOT_SIGN_CERT}"
|
||||
MODULES_SIGN_HASH="sha512"
|
||||
```
|
||||
|
||||
1. The `MAKEOPTS` defines (`-j` the number of jobs/processes) and limits (`-l` the load average) how many parallel make/compilation processes can be launched from `portage`. The number of parallel processes are limited by the number of available logical CPUs and by RAM, each process can take up to `2 GB` of RAM. The load average `-l` is generally set slighly above the number of jobs `-j`.
|
||||
|
||||
2. This selection of global (removed `-`) USE flags is rather personal and should be set according to the preferences of the user.
|
||||
|
||||
Synchronise the ebuild repositories and emerge some relevant packages:
|
||||
|
||||
``` shell-session
|
||||
sh# emaint sync
|
||||
sh# emerge -a app-portage/gentoolkit app-editors/vim sys-libs/timezone-data net-misc/openntpd sys-apps/musl-locales
|
||||
```
|
||||
|
||||
Configure some key aspects of the system:
|
||||
|
||||
``` shell-session
|
||||
sh# echo <hostname> > /etc/hostname
|
||||
sh# echo TZ="/usr/share/zoneinfo/<Region>/<City>" > /etc/env.d/00tz
|
||||
sh# echo MUSL_LOCPATH="/usr/share/i18n/locales/musl" > /etc/env.d/01locales #(1)!
|
||||
sh# env-update && source /etc/profile
|
||||
sh# eselect locale set <locale> #(2)!
|
||||
sh# rc-update add ntpd default
|
||||
sh# passwd root #(3)!
|
||||
```
|
||||
|
||||
1. Musl does not support locales out of the box. They are not necessary but some programs rely on them to set the language of their application.
|
||||
|
||||
2. The correct `<locale>` can be found in the locale list:
|
||||
|
||||
``` shell-session
|
||||
sh# eselect locale list
|
||||
```
|
||||
|
||||
3. The root password does not really matter because it is going to be locked after a user has been created.
|
||||
|
||||
Edit the `fstab` to set the correct mounts:
|
||||
|
||||
``` shell title="/etc/fstab"
|
||||
rpool/root/gentoo / zfs rw,noatime,xattr,posixacl,casesensitive 0 1
|
||||
rpool/root/gentoo/var /var zfs rw,noatime,nodev,nosuid,xattr,posixacl,casesensitive 0 2
|
||||
/dev/disk/by-label/esp /efi vfat defaults,nodev,nosuid,noexec,umask=0077 0 2
|
||||
tmpfs /tmp tmpfs rw,nodev,nosuid,noexec,mode=1777 0 0
|
||||
proc /proc proc nodev,nosuid,noexec,hidepid=2 0 0
|
||||
```
|
||||
|
||||
Configure the kernel command-line to be able to boot correctly:
|
||||
|
||||
``` shell title="/etc/kernel/cmdline"
|
||||
rw root=ZFS=rpool/root/gentoo rootflags=noatime quiet splash
|
||||
```
|
||||
|
||||
Emerge `sbctl` and `sbsigntools` which will be used alongside `dracut` (1) to sign the build Unified Kernel Image (UKI):
|
||||
{ .annotate }
|
||||
|
||||
1. The initramfs builder.
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a app-crypt/sbctl app-crypt/sbsigntools
|
||||
```
|
||||
|
||||
> Verify that secureboot is in `setup mode` with `sbctl status`.
|
||||
|
||||
Create and enroll the secureboot keys into the system:
|
||||
|
||||
``` shell-session
|
||||
sh# sbctl create-keys
|
||||
sh# sbctl enroll-keys #(1)!
|
||||
```
|
||||
|
||||
1. Whilst enrolling the keys it might be necessary to add the `--microsoft` flag if you are unable to use custom keys.
|
||||
|
||||
Enable `dracut` to create a UKI with `ukify`:
|
||||
|
||||
``` shell title="/usr/lib/kernel/install.conf"
|
||||
layout=uki
|
||||
initrd_generator=dracut
|
||||
uki_generator=ukify
|
||||
```
|
||||
|
||||
and enable automatic signing with `sbsign`:
|
||||
|
||||
``` shell title="/etc/kernel/uki.conf"
|
||||
[UKI]
|
||||
SecureBootSigningTool=sbsign
|
||||
```
|
||||
|
||||
Tell `portage` to generate a UKI when installing a kernel:
|
||||
|
||||
``` shell title="/etc/portage/package.use/installkernel"
|
||||
sys-kernel/installkernel dracut ukify uki
|
||||
```
|
||||
|
||||
Set the required USE flags for `systemd-utils` such that on emerging the bootloader `systemd-boot` will be installed:
|
||||
|
||||
``` shell title="/etc/portage/package.use/systemd-utils"
|
||||
sys-apps/systemd-utils kernel-install boot ukify
|
||||
```
|
||||
|
||||
The installation of the hardware firmware on the system, such as the CPU microcode, is hardware specific:
|
||||
|
||||
=== "AMD CPU"
|
||||
|
||||
The microcode updates for systems with an AMD CPU are all contained in `sys-kernel/linux-firmware`, accept its license:
|
||||
|
||||
``` shell title="/etc/portage/package.license/kernel"
|
||||
sys-kernel/linux-firmware linux-fw-redistributable @BINARY-REDISTRIBUTABLE
|
||||
```
|
||||
|
||||
and emerge them together with the Gentoo kernel, its necessary kernel modules for this system and the bootloader:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a sys-kernel/linux-firmware sys-fs/zfs-kmod sys-kernel/gentoo-kernel sys-apps/systemd-utils
|
||||
```
|
||||
|
||||
=== "Intel CPU"
|
||||
|
||||
The microcode updates for systems with an Intel CPU require alongside `sys-kernel/linux-firmware` also `sys-kernel/intel-microcode`, accept its licenses:
|
||||
|
||||
``` shell title="/etc/portage/package.license/kernel"
|
||||
sys-kernel/linux-firmware linux-fw-redistributable @BINARY-REDISTRIBUTABLE
|
||||
sys-firmware/intel-microcode intel-ucode
|
||||
```
|
||||
|
||||
and emerge them together with the Gentoo kernel, its necessary kernel modules for this system and the bootloader:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a sys-kernel/linux-firmware sys-firmware/intel-microcode sys-fs/zfs-kmod sys-kernel/gentoo-kernel sys-apps/systemd-utils
|
||||
```
|
||||
|
||||
Install the bootloader on the ESP:
|
||||
|
||||
``` shell-session
|
||||
sh# bootctl install
|
||||
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed" to "/efi/EFI/systemd/systemd-bootx64.efi".
|
||||
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed" to "/efi/EFI/BOOT/BOOTX64.EFI".
|
||||
Random seed file /efi/loader/random-seed successfully refreshed (32 bytes).
|
||||
Created EFI boot entry "Linux Boot Manager".
|
||||
```
|
||||
|
||||
and configure the bootloader:
|
||||
|
||||
``` shell title="/efi/loader/loader.conf"
|
||||
timeout 3
|
||||
editor no
|
||||
```
|
||||
|
||||
> One may verify the signed files by running `sbctl verify`.
|
||||
|
||||
Generate the `hostid` and reconfigure the Gentoo Kernel:
|
||||
|
||||
``` shell-session
|
||||
sh# zgenhostid
|
||||
sh# emerge --config gentoo-kernel
|
||||
```
|
||||
|
||||
Finally, add some services for `ZFS`:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add zfs-mount sysinit
|
||||
sh# rc-update add zfs-import sysinit #(1)!
|
||||
sh# rc-update add zfs-load-key sysinit #(2)!
|
||||
```
|
||||
|
||||
1. Omit if a faster boot time is preferred.
|
||||
|
||||
2. Omit if a faster boot time is preferred.
|
||||
|
||||
Now exit the chroot, unmount the datasets and reboot:
|
||||
|
||||
``` shell-session
|
||||
sh# exit
|
||||
sh# umount -lf /mnt
|
||||
sh# reboot
|
||||
```
|
||||
|
||||
## Optional installation
|
||||
|
||||
### Repositories
|
||||
|
||||
[GURU](https://wiki.gentoo.org/wiki/Project:GURU) is an extra repository containing ebuilds that are not available in the Gentoo repository. Although the packages it contains might not be as well tested as in the main repository they are still necessary for some setups, such as some dependencies required for automatic decryption or graphical user interfaces. Add GURU with:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a app-eselect/eselect-repository
|
||||
sh# eselect repository enable guru
|
||||
sh# emaint sync --repo guru
|
||||
```
|
||||
|
||||
[Portage-ample](https://git.ampel.dev/ampel/portage-ample) is an extra extra repository containing ebuilds maintained by us, the [Ampel organisation](https://ampel.dev). Add portage-ample with:
|
||||
|
||||
``` shell-session
|
||||
sh# eselect repository add portage-ample git https://git.ampel.dev/ampel/portage-ample
|
||||
sh# emaint sync --repo portage-ample
|
||||
```
|
||||
|
||||
### Automatic decryption
|
||||
|
||||
Automatic decryption will be managed with `zlevis`, which is able to unlock an encrypted `ZFS` root pool with keys saved in a TPM. Currently it is only available in the [portage-ample](https://git.ampel.dev/ampel/portage-ample) repository and also has some dependencies in the `guru` repository.
|
||||
|
||||
Set the `dracut` flag for `zlevis`:
|
||||
|
||||
``` shell title="/etc/portage/package.use/zlevis"
|
||||
app-crypt/zlevis dracut
|
||||
```
|
||||
|
||||
emerge `zlevis`:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a app-crypt/zlevis
|
||||
```
|
||||
|
||||
and enable the `zlevis` module for `dracut`:
|
||||
|
||||
``` shell title="/etc/dracut.conf.d/zlevis.conf"
|
||||
nofsck="yes"
|
||||
add_dracutmodules+=" zlevis "
|
||||
```
|
||||
|
||||
### Swap
|
||||
|
||||
To add swap to the system emerge `zram-init`:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a sys-block/zram-init
|
||||
```
|
||||
|
||||
Configure `zram-init` to create a swap device of size one fourth of the ram size:
|
||||
|
||||
``` shell title="/etc/conf.d/zram-init"
|
||||
load_on_start="yes"
|
||||
unload_on_stop="yes"
|
||||
num_devices="1"
|
||||
|
||||
type0="swap"
|
||||
size0=`LC_ALL=C free -m | awk '/^Mem:/{print int($2/4)}'`
|
||||
maxs0=1
|
||||
algo0=zstd
|
||||
labl0=zram_swap
|
||||
```
|
||||
|
||||
and add `zram-init` to the default runlevel:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add zram-init default
|
||||
```
|
||||
|
||||
### Compiler cache
|
||||
|
||||
Compiler cache can speed up recompile's, by avoiding recompilation of the same object files by fetching the result from a cache directory. The package `ccache` enables a compiler cache for `C/C++` object files, present in the Gentoo Kernel for example. Emerge it with:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a dev-util/ccache
|
||||
```
|
||||
|
||||
and edit its configuration file:
|
||||
|
||||
``` shell title="/etc/ccache.conf"
|
||||
cache_dir = /var/cache/ccache
|
||||
|
||||
max_size = 20G
|
||||
umask = 002
|
||||
hash_dir = false
|
||||
compiler_check = %compiler% -dumpversion
|
||||
|
||||
compression = true
|
||||
compression_level = 1
|
||||
```
|
||||
|
||||
Configure `portage` to allow `ccache` for specified packages:
|
||||
|
||||
``` shell title="/etc/portage/env/enable-ccache.conf"
|
||||
FEATURES="ccache"
|
||||
CCACHE_DIR="/var/cache/ccache"
|
||||
```
|
||||
|
||||
such that for every `C/C++` package with which you want to use `ccache`:
|
||||
|
||||
``` shell title="/etc/portage/package.env/ccache"
|
||||
sys-kernel/gentoo-kernel enable-ccache.conf
|
||||
...
|
||||
```
|
||||
|
||||
### Users
|
||||
|
||||
To run processes securely, in an environment with fewer privileges, a user is necessary.
|
||||
|
||||
Before creating the user, emerge `doas`, to be able to "do as" root when it is required:
|
||||
|
||||
which requires us to set:
|
||||
|
||||
``` shell title="/etc/portage/package.use/doas"
|
||||
app-admin/doas persist
|
||||
```
|
||||
|
||||
then
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a app-admin/doas
|
||||
```
|
||||
|
||||
and configure `doas` by editing:
|
||||
|
||||
``` shell title="/etc/doas.conf"
|
||||
permit persist :wheel as root
|
||||
```
|
||||
|
||||
Now users who are in the `wheel` group are allowed to use the doas command to gain root privileges.
|
||||
|
||||
We can add a user, set its password and add it to the `wheel` group (if admin) with:
|
||||
|
||||
``` shell-session
|
||||
sh# useradd -m -G wheel -s /bin/bash <username>
|
||||
sh# passwd <username>
|
||||
```
|
||||
|
||||
The `wheel` group should ideally only be assigned to one singular admin account. The users in the group are allowed to use the doas command to gain root privileges. This is necessary for installing packages and changing system files but not for a normal user.
|
||||
|
||||
If you have checked that doas works with the user then you can lock the root account because it imposes security risks if it is kept open. This can be done with:
|
||||
|
||||
``` shell-session
|
||||
sh# passwd -l root
|
||||
```
|
||||
|
||||
and by changing its login shell to:
|
||||
|
||||
``` shell title="/etc/passwd"
|
||||
root:x:0:0:root:/root:/sbin/nologin
|
||||
```
|
||||
|
||||
### Networking
|
||||
|
||||
For desktop use `NetworkManager` is preferred over `dhcpcd` as network daemon, due to its versatility, i.e. Wi-Fi and VPN compatibility, MAC randomisation, et cetera.
|
||||
|
||||
First set the relevant USE flags:
|
||||
|
||||
``` shell title="/etc/portage/package.use/networkmanager"
|
||||
net-misc/networkmanager dhcpcd -wext
|
||||
```
|
||||
|
||||
> Also make sure the `networkmanager` USE flag is enabled in your `make.conf`.
|
||||
|
||||
Now emerge `networkmanager`:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a net-misc/networkmanager
|
||||
```
|
||||
|
||||
and configure `networkmanager` to have MAC randomisation by editing:
|
||||
|
||||
``` shell title="/etc/NetworkManager/NetworkManager.conf"
|
||||
[main]
|
||||
hostname-mode=none
|
||||
plugins=ifupdown,keyfile
|
||||
|
||||
[ifupdown]
|
||||
managed=true
|
||||
|
||||
[device]
|
||||
wifi.scan-rand-mac-address=yes
|
||||
|
||||
[connection-mac-randomization]
|
||||
ethernet.cloned-mac-address=random
|
||||
wifi.cloned-mac-address=random
|
||||
```
|
||||
|
||||
Then stop any other network service, such as `dhcpcd` running in the dynamic runlevel, and enable `NetworkManager`:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add NetworkManager default
|
||||
```
|
||||
|
||||
For users to be able to modify connections on the system they will have to be added to the `plugdev` group.
|
||||
|
||||
## Concluding remarks
|
||||
|
||||
This is the bare minimum for a Gentoo Linux desktop system. Some additional features such as bluetooth, laptop battery management, printer compatiblity, et cetera, have been documented well in the [Gentoo Wiki](https://wiki.gentoo.org/wiki/Main_Page), and can thus be found there. The next steps are the improvement of the security of the system and the configuration of the graphical session.
|
91
docs/posts/06-maintaining-zfs-system.md
Normal file
91
docs/posts/06-maintaining-zfs-system.md
Normal file
|
@ -0,0 +1,91 @@
|
|||
---
|
||||
title: Maintaining a system build on ZFS
|
||||
slug: maintaining-a-system-build-on-zfs
|
||||
date: 2025-08-02
|
||||
draft: false
|
||||
authors:
|
||||
- luc
|
||||
tags:
|
||||
- Alpine Linux
|
||||
- Gentoo Linux
|
||||
categories:
|
||||
- Maintenance
|
||||
---
|
||||
|
||||
ZFS opens up novel methods to safely maintain a system. In this blog entry we will outline these methods in the form of an update protocol. To keep your system healthy this protocol should be executed on a weekly/monthly basis.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## Pre-update
|
||||
|
||||
To be able to rollback the system after a system update, one may create a `snapshot` of the root filesystem:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# zfs snapshot rpool/root/alpine@previous
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# zfs snapshot rpool/root/gentoo@previous
|
||||
```
|
||||
|
||||
Furthermore, `zfs list -t snapshot` can be used to list snapshots and `zfs destroy` can be used to remove snapshots.
|
||||
|
||||
## Update
|
||||
|
||||
We may perform a system update:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# apk upgrade
|
||||
sh# reboot
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -auDU @world
|
||||
sh# reboot
|
||||
```
|
||||
|
||||
If the system does not behave accordingly after reboot, one may `rollback` to the previous snapshot:
|
||||
|
||||
=== "Alpine Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# zfs rollback -r rpool/root/alpine@previous
|
||||
```
|
||||
|
||||
=== "Gentoo Linux"
|
||||
|
||||
``` shell-session
|
||||
sh# zfs rollback -r rpool/root/gentoo@previous
|
||||
```
|
||||
|
||||
## Post-update
|
||||
|
||||
To maintain the performance of the SSDs in the system, perform a `trim` on the ZFS-pool:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool trim --secure --wait rpool #(1)!
|
||||
```
|
||||
|
||||
1. Some devices may not support the option `--secure`.
|
||||
|
||||
A `scrub` on the ZFS-pool checks and repairs the data in the pool and is usually performed after a `trim`:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool scrub rpool
|
||||
```
|
||||
|
||||
A `scrub` may take a while, its progress can be checked with:
|
||||
|
||||
``` shell-session
|
||||
sh# zpool status rpool
|
||||
```
|
||||
|
||||
> A ZFS scrub only repairs if `mirror` or a `zraid` mode is set in the pool.
|
520
docs/posts/07-minimal-gui-river-pipewire-openrc.md
Normal file
520
docs/posts/07-minimal-gui-river-pipewire-openrc.md
Normal file
|
@ -0,0 +1,520 @@
|
|||
---
|
||||
title: A minimal graphical session with River, PipeWire and OpenRC
|
||||
slug: minimal-graphical-session-with-river-pipewire-and-openrc
|
||||
date: 2025-08-08
|
||||
draft: false
|
||||
authors:
|
||||
- luc
|
||||
tags:
|
||||
- Gentoo Linux
|
||||
categories:
|
||||
- Graphical session
|
||||
---
|
||||
|
||||
Graphical sessions are often full of unnecessary features, increasing (unnecessarily) the overall complexity of a desktop system. In this blog entry we intend to go back to the bare minimum, which defines itself as a reasonable step forward in practicality compared the default `tty`. Like being able to run graphical applications in windows and being able to manage these windows efficiently. We will meet but not exceed these requirements with [River](https://isaacfreund.com/software/river/) as our tiling window manager, [PipeWire](https://pipewire.org/) as our multimedia framework, and [OpenRC](https://wiki.gentoo.org/wiki/Project:OpenRC) as our user-service manager.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## User services with OpenRC
|
||||
|
||||
As of `openrc-0.60` user services are supported by `openrc` (1). To enable a `openrc` user session we require a runtime dir (`XDG_RUNTIME_DIR`), which can be supplied by [turnstile](https://github.com/chimera-linux/turnstile) (2). Therefore emerge `turnstile`:
|
||||
{ .annotate }
|
||||
|
||||
1. Presently the versions `>=0.60` of `openrc` are unstable. Enable unstable releases of `openrc` with:
|
||||
|
||||
``` shell title="/etc/portage/package.accept_keyword/openrc"
|
||||
sys-apps/openrc ~amd64
|
||||
```
|
||||
|
||||
2. The `turnstile` ebuild can be found in the [portage-ample](https://git.ampel.dev/ampel/portage-ample) repository.
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a sys-auth/turnstile
|
||||
```
|
||||
|
||||
and enable runtime dir management by `turnstile` by setting `manage_rundir` to yes in `turnstiled.conf`:
|
||||
|
||||
``` shell title="/etc/turnstile/turnstiled.conf"
|
||||
manage_rundir = yes
|
||||
```
|
||||
|
||||
Add the `turnstile` daemon to the default runlevel:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add turnstiled default
|
||||
```
|
||||
|
||||
`pam` enables us to generate this `rundir` and start the `openrc` user session when the user is logged in by enabling the `turnstile` and `openrc` `pam` modules in `pam.d/system-login`:
|
||||
|
||||
``` shell title="/etc/pam.d/system-login"
|
||||
session optional pam_turnstile.so
|
||||
session optional pam_openrc.so
|
||||
```
|
||||
|
||||
This user session contains the usual runlevels (`boot`, `default`, `shutdown`) that are run in their respective regimes (1). One approach is to add the window manager in the `default` runlevel, such that it will be started at login. But there are particular use cases where the graphical session is not required or wanted (only taking up precious resources), it is therefore wise to configure the graphical session in its own runlevel, to have the freedom to enable it any time you want with `openrc -U <runlevel-graphical-session>` (2).
|
||||
{ .annotate }
|
||||
|
||||
1. Though `boot` and `shutdown` should rather be interpreted as `login` and `logout` of the user session in this particular case.
|
||||
2. Or to link it to the `default` runlevel.
|
||||
|
||||
Besides the `<runlevel-graphical-session>`, denoted with `g`, we will also define a `<runlevel-post-graphical-session>`, denoted with `h`, for the services depending on the graphical-session. The creation of these runlevels can be simply performed with `mkdir`:
|
||||
|
||||
``` shell-session
|
||||
sh$ mkdir ~/.config/rc/runlevels/g
|
||||
sh$ mkdir ~/.config/rc/runlevels/h
|
||||
```
|
||||
|
||||
and stack them accordingly:
|
||||
|
||||
``` shell-session
|
||||
sh$ ln -s /home/<user>/.config/rc/runlevels/default /home/<user>/.config/rc/runlevels/g
|
||||
sh$ ln -s /home/<user>/.config/rc/runlevels/g /home/<user>/.config/rc/runlevels/h
|
||||
```
|
||||
|
||||
A set of minimal applications consisting of the terminal emulator (`foot`), application launcher (`fuzzel`(1)), notification daemon (`mako`) and display configurator (`kanshi`(2)) that will be of use in the graphical session may be emerged via:
|
||||
{ .annotate }
|
||||
|
||||
1. Presently `fuzzel` is unstable in `GURU`. Enable unstable releases of `fuzzel` with:
|
||||
|
||||
``` shell title="/etc/portage/package.accept_keywords/fuzzel"
|
||||
gui-apps/fuzzel ~amd64
|
||||
```
|
||||
|
||||
2. Presently `kanshi` and its dependency `libsfcg` is unstable in `GURU`. Enable unstable releases of `kanshi` and `libscfg` with:
|
||||
|
||||
``` shell title="/etc/portage/package.accept_keywords/kanshi"
|
||||
gui-apps/kanshi ~amd64
|
||||
dev-libs/libscfg ~amd64
|
||||
```
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a gui-apps/foot gui-apps/fuzzel gui-apps/mako gui-apps/kanshi
|
||||
```
|
||||
|
||||
These applications require `WAYLAND_DISPLAY` to be set by the window manager. Therefore allow this variable to propagate in the user session:
|
||||
|
||||
``` shell title="~/.config/rc/rc.conf"
|
||||
rc_env_allow="WAYLAND_DISPLAY"
|
||||
```
|
||||
|
||||
The terminal emulator (`foot`) requires the backend `footserver` for its clients. Therefore create a user service that will supervise `footserver`:
|
||||
|
||||
``` shell title="~/.config/rc/init.d/footserver"
|
||||
#!/usr/bin/openrc-run
|
||||
|
||||
depend() {
|
||||
need graphical-session
|
||||
}
|
||||
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/bin/foot"
|
||||
command_args="--server"
|
||||
```
|
||||
|
||||
Also the notification daemon (`mako`) and the display configurator (`kanshi`) require to be supervised, thus create a user service for them as well:
|
||||
|
||||
``` shell title="~/.config/rc/init.d/mako"
|
||||
#!/usr/bin/openrc-run
|
||||
|
||||
depend() {
|
||||
need graphical-session
|
||||
}
|
||||
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/bin/mako"
|
||||
```
|
||||
|
||||
``` shell title="~/.config/rc/init.d/kanshi"
|
||||
#!/usr/bin/openrc-run
|
||||
|
||||
depend() {
|
||||
need graphical-session
|
||||
}
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/bin/kanshi"
|
||||
```
|
||||
|
||||
Adding these user services to `<runlevel-post-graphical-session>` can simply be performed with `rc-update -U`:
|
||||
|
||||
``` shell-session
|
||||
sh$ rc-update -U add footserver h
|
||||
sh$ rc-update -U add mako h
|
||||
sh$ rc-update -U add kanshi h
|
||||
```
|
||||
|
||||
## Window management with River
|
||||
|
||||
We require a seat manager as the mediating layer between the window manager and the hardware, to be able to run the window manager in the user session. We will use `seatd` as the seat management daemon, which requires us to set the following `USE` flags:
|
||||
|
||||
``` shell title="/etc/portage/package.use/seatd"
|
||||
sys-auth/seatd server -builtin
|
||||
```
|
||||
|
||||
Now emerge `seatd`:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a seatd
|
||||
```
|
||||
|
||||
and add the daemon to the default runlevel:
|
||||
|
||||
``` shell-session
|
||||
sh# rc-update add seatd default
|
||||
```
|
||||
|
||||
> We need to be part of the `seat` group to effectively communicate with the seat manager:
|
||||
>
|
||||
> ``` shell-session
|
||||
> sh# usermod --append --groups seat <user>
|
||||
> ```
|
||||
|
||||
Video drivers are necessary for a graphical session to function. In Gentoo it is as simple as specifying what video card the system uses using the `USE` flags:
|
||||
|
||||
``` shell title="/etc/portage/package.use/video"
|
||||
*/* VIDEO_CARDS: -* <amdgpu radeonsi || intel || nvidia>
|
||||
```
|
||||
|
||||
By setting the `VIDEO_CARDS` `USE` flag the dependencies of `river` are compiled for the video card of the system, thus emerge `river` (1):
|
||||
{ .annotate }
|
||||
|
||||
1. Presently `river` is unstable in `GURU`. Enable unstable releases of `river` with:
|
||||
|
||||
``` shell title="/etc/portage/package.accept_keywords/river"
|
||||
gui-wm/river ~amd64
|
||||
```
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a gui-wm/river
|
||||
```
|
||||
|
||||
> Buckle up, this is quite a hefty emerge.
|
||||
|
||||
`river` starts by executing an init (POSIX shell) script present in `~/.conig/river/init`, this scipt should contain or link to all the configuration of `river`. A minimal configuration of the `river/init` script is given below:
|
||||
|
||||
``` shell title="~/.config/river/init"
|
||||
#!/bin/sh
|
||||
|
||||
# Set background and colours
|
||||
swaybg -m fill -c 000000 -i $HOME/.local/share/backgrounds/<background> & #(1)!
|
||||
riverctl border-color-focused 0xFFFFFFCC
|
||||
riverctl border-color-unfocsed 0xFFFFFF66
|
||||
riverctl border-color-urgent 0xFFFFFF
|
||||
|
||||
# Set border-width
|
||||
riverctl border-width 2
|
||||
|
||||
# Set layout
|
||||
riverctl default-layout rivertile
|
||||
rivertile --view-padding 4 --outer-padding 4 &
|
||||
|
||||
# Source the keybinds
|
||||
source ~/.config/river/binds
|
||||
|
||||
# Switch to post-graphical runlevel
|
||||
openrc -U h
|
||||
|
||||
|
||||
## Device input settings ##
|
||||
|
||||
# Keyboard layout
|
||||
riverctl keyboard-layout qwerty
|
||||
|
||||
# Set keyboard repeat rate
|
||||
riverctl set-repeat 50 300
|
||||
|
||||
# Set focus-follow-cursor
|
||||
riverctl focus-follow-cursor normal
|
||||
```
|
||||
|
||||
1. This line is optional, but if you want to spruce up your graphical session, then emerge:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a gui-apps/swaybg
|
||||
```
|
||||
|
||||
with the `river/binds` given by:
|
||||
|
||||
``` shell title="~/.config/river/binds"
|
||||
#!/bin/sh
|
||||
|
||||
# Super+Shift+Q to exit river
|
||||
riverctl map normal Super+Shift Q spawn "openrc -U g && openrc -U default"
|
||||
|
||||
|
||||
## Aplication mapping ##
|
||||
|
||||
# Super+Backspace to spawn waylock
|
||||
riverctl map normal Super BackSpace spawn "waylock -ignore-empty-password" #(1)!
|
||||
|
||||
# Super+Return to spawn foot
|
||||
riverctl map normal Super Return spawn footclient
|
||||
|
||||
# Super+Space to spawn fuzzel
|
||||
riverctl map normal Super Space spawn fuzzel
|
||||
|
||||
# Super+D to dismiss mako notification
|
||||
riverctl map normal Super D spawn "makoctl dismiss"
|
||||
|
||||
# Super+T to spawn datetime notification
|
||||
riverctl map normal Super T spawn "$HOME/.local/bin/datetime" #(2)!
|
||||
|
||||
# Super+B to spawn battery state notification
|
||||
riverctl map normal Super B spawn "$HOME/.local/bin/battery" #(3)!
|
||||
|
||||
# MonBrightnessUp or MonBrightnessDown to spawn brightness notification (4)
|
||||
rivcertl map normal None XF86_MonBrightnessUp spawn "$HOME/.local/bin/brightness"
|
||||
riverctl map normal None XF86_MonBrightnessDown spawn "$HOME/.local/bin/brightness"
|
||||
|
||||
# Print to spawn interactive screenshot
|
||||
riverctl map normal None Print spawn "grim -g "(slurp -d)" - | wl-copy -t image/jpeg" #(5)!
|
||||
|
||||
# Shift+Print to spawn screenshot
|
||||
riverctl map normal Shift Print spawn "grim - | wl-copy -t image/jpeg" #(6)!
|
||||
|
||||
|
||||
## Focused view mapping ##
|
||||
|
||||
# Super+Q to close the focused view
|
||||
riverctl map normal Super Q close
|
||||
|
||||
# Super+F to fullscreen toggle the focused view
|
||||
riverctl map normal Super F toggle-fullscreen
|
||||
|
||||
# Super+BTN_MIDDLE to toggle float of the focused view
|
||||
riverctl map-pointer normal Super BTN_MIDDLE toggle-float
|
||||
|
||||
# Super+BTN_LEFT to move the floated focused view
|
||||
riverctl map-pointer normal Super BTN_LEFT move-view
|
||||
|
||||
# Super+BTN_RIGHT to resize the floated focused view
|
||||
riverctl map-pointer normal Super BTN_RIGHT resize-view
|
||||
|
||||
# Super+{K;J} to focus next/previous view
|
||||
riverctl map normal Super K focus-view next
|
||||
riverctl map normal Super J focus-view previous
|
||||
|
||||
# Super+Shift+{K;J} to swap the focused view with the next/previous view
|
||||
riverctl map normal Super+Shift K swap next
|
||||
riverctl map normal Super+Shift J swap previous
|
||||
|
||||
# Super+{.;,} to focus the next/previous output
|
||||
riverctl map normal Super Period focus-output next
|
||||
riverctl map normal Super Comma focus-output previous
|
||||
|
||||
# Super+Shift+{.;,} to send the focused view to the next/previous output
|
||||
riverctl map normal Super+Shift Period send-to-output next
|
||||
riverctl map normal Super+Shift Comma send-to-output previous
|
||||
|
||||
|
||||
## Rivertile mapping ##
|
||||
|
||||
# Super+{Up;Right;Down;Left} to change layout orientation
|
||||
riverctl map normal Super Up send-layout-cmd rivertile "main-location top"
|
||||
riverctl map normal Super Right send-layout-cmd rivertile "main-location right"
|
||||
riverctl map normal Super Down send-layout-cmd rivertile "main-location down"
|
||||
riverctl map normal Super Left send-layout-cmd rivertile "main-location left"
|
||||
|
||||
# Super+Shift+{Up;Down} to increment/decrement the main count of rivertile
|
||||
riverctl map normal Super+Shift Up send-layout-cmd rivertile "main-count +1"
|
||||
riverctl map normal Super+Shift Down send-layout-cmd rivertile "main-count -1"
|
||||
|
||||
# Super+Shift+{Right;Left} to increase/decrease the main ratio of rivertile
|
||||
riverctl map normal Super+Shift Right send-layout-cmd rivertile "main-ratio +0.05"
|
||||
riverctl map normal Super+Shift Left send-layout-cmd rivertile "main-ratio -0.05"
|
||||
|
||||
|
||||
## Tag mapping ##
|
||||
|
||||
for i in $(seq 1 9)
|
||||
do
|
||||
j=$((1 << ($i - 1)))
|
||||
|
||||
# Super+[1-9] to focus tag [0-8]
|
||||
riverctl map normal Super $i set-focused-tags $j
|
||||
|
||||
# Super+Shift+[1-9] to tag focused view with tag [0-8]
|
||||
riverctl map normal Super+Shift $i set-view-tags $j
|
||||
|
||||
# Super+Control+[1-9] to toggle focus of tag [0-8]
|
||||
riverctl map normal Super+Control $i toggle-focused-tags $j
|
||||
|
||||
# Super+Shift+Control+[1-9] to toggle tag [0-8] of focused view
|
||||
riverctl map normal Super+Shift+Control $i toggle-view-tags $j
|
||||
done
|
||||
|
||||
all_tags=$(((1 << 32) - 1))
|
||||
|
||||
# Super+O to focus all tags
|
||||
riverctl map normal Super O set-focused-tags $all_tags
|
||||
|
||||
# Super+Shift+O to tag focused view with all tags
|
||||
riverctl map normal Super+Shift O set-view-tags $all_tags
|
||||
|
||||
|
||||
## Media mapping ## (7)
|
||||
|
||||
# AudioRaiseVolume to increase volume audio sink by 5%
|
||||
riverctl map normal None XF86AudioRaiseVolume spawn "$HOME/.local/bin/audio sink volup"
|
||||
|
||||
# AudioLowerVolume to decrease volume audio sink by 5%
|
||||
riverctl map normal None XF86AudioLowerVolume spawn "$HOME/.local/bin/audio sink voldown"
|
||||
|
||||
# AudioMute to toggle mute audio sink
|
||||
riverctl map normal None XF86AudioMute spawn "$HOME/.local/bin/audio sink toggle"
|
||||
|
||||
# AudioMicRaiseVolume to increase volume audio source by 5%
|
||||
riverctl map normal None XF86AudioMicRaiseVolume spawn "$HOME/.local/bin/audio source volup"
|
||||
|
||||
# AudioMicLowerVolume to decrease volume audio source by 5%
|
||||
riverctl map normal None XF86AudioMicLowerVolume spawn "$HOME/.local/bin/audio source voldown"
|
||||
|
||||
# AudioMicMute to toggle mute audio source
|
||||
riverctl map normal None XF86AudioMute spawn "$HOME/.local/bin/audio source toggle"
|
||||
```
|
||||
|
||||
1. Optionally, it could be useful to lock your session, therefore emerge `waylock`(1):
|
||||
{ .annotate }
|
||||
|
||||
1. Presently `waylock` is unstable in `GURU`. Enable unstable releases of `waylock` with:
|
||||
|
||||
``` shell title="/etc/portage/package.accept_keywords/fuzzel"
|
||||
gui-apps/waylock ~amd64
|
||||
```
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a gui-apps/waylock
|
||||
```
|
||||
|
||||
2. Optionally, it could be useful to see the datetime as a notification when you long for it, which is part of [ampel/notifiers](https://git.ampel.dev/ampel/notifiers).
|
||||
|
||||
3. Optionally, it could be useful to see the battery state as a notification, especially when you have a laptop. This functionality is part of [ampel/notifiers](https://git.ampel.dev/ampel/notifiers).
|
||||
|
||||
4. Optionally, it could be useful to see the brightness state of your display as a notification, which is part of [ampel/notifiers](https://git.ampel.dev/ampel/notifiers). As an additional remark, the default of configuration of `acpid` in Gentoo sucks, setting brightness capability with the same keys requires some changes in the configuration that can be found [here](https://git.lucbijl.nl/luc/gentoo-desktop/src/branch/main/etc/acpi).
|
||||
|
||||
5. Optional screenshot capability.
|
||||
|
||||
6. Optional screenshot capability.
|
||||
|
||||
7. Optionally, it could be useful to set and get the state of the audio sink and source, which is part of [ampel/notifiers](https://git.ampel.dev/ampel/notifiers).
|
||||
|
||||
|
||||
To be able to run our window manager (`river`) supervised, we require the `river` user service:
|
||||
|
||||
``` shell title="~/.config/rc/init.d/river"
|
||||
#!/usr/bin/openrc-run
|
||||
|
||||
depend() {
|
||||
need dbus
|
||||
provide graphical-session
|
||||
}
|
||||
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/bin/river"
|
||||
```
|
||||
|
||||
and add it to `<runlevel-graphical-session>`:
|
||||
|
||||
```
|
||||
sh$ rc-update -U add river g
|
||||
```
|
||||
|
||||
## Interfacing audio and video with PipeWire
|
||||
|
||||
We will interface audio and video with `pipewire` with `wireplumber` as fronted and `pulseaudio` support, which requires us to set the following `USE` flags:
|
||||
|
||||
``` shell title="/etc/portage/package.use/pipewire"
|
||||
media-video/pipewire sound-server loudness echo-cancel bluetooth
|
||||
media-sound/pulseaudio -daemon
|
||||
```
|
||||
|
||||
Emerge `libpulse` for `pulseaudio` support, `pipewire` and `wireplumber`:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a media-libs/libpulse media-video/pipewire media-video/wireplumber
|
||||
```
|
||||
|
||||
and copy the default configuration of `pipewire` and `wireplumber` to their respective `~/.config` folders:
|
||||
|
||||
``` shell-session
|
||||
sh$ cp /usr/share/pipewire/pipewire.conf ~/.config/pipewire/
|
||||
sh$ cp /usr/share/wirelumber/wireplumber.conf ~/.config/wireplumber/
|
||||
```
|
||||
|
||||
Now create the corresponding user services for the `pipewire-pulse` support layer, `pipewire` itself, and `wireplumber`:
|
||||
|
||||
``` shell title="~/.config/rc/init.d/pipewire-pulse"
|
||||
#!/usr/bin/openrc-run
|
||||
|
||||
depend() {
|
||||
need audio-session
|
||||
}
|
||||
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/bin/pipewire-pulse"
|
||||
```
|
||||
|
||||
``` shell title="~/.config/rc/init.d/pipewire"
|
||||
#!/usr/bin/openrc-run
|
||||
|
||||
depend() {
|
||||
need graphical-session
|
||||
provide audio-session
|
||||
}
|
||||
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/bin/pipewire"
|
||||
```
|
||||
|
||||
``` shell title="~/.config/rc/init.d/wireplumber"
|
||||
#!/usr/bin/openrc-run
|
||||
|
||||
depend() {
|
||||
need audio-session
|
||||
}
|
||||
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/bin/wireplumber"
|
||||
```
|
||||
|
||||
and add them to `<runlevel-post-graphical-session>`:
|
||||
|
||||
``` shell-session
|
||||
sh$ rc-update -U add pipewire h
|
||||
sh$ rc-update -U add pipewire-pulse h
|
||||
sh$ rc-update -U add wireplumber h
|
||||
```
|
||||
|
||||
> We need to be part of the `pipewire` group for `pipewire` and dependencies to work accordingly:
|
||||
>
|
||||
> ``` shell-session
|
||||
> sh# usermod --append --groups pipewire <user>
|
||||
> ```
|
||||
|
||||
For screencast and screenshot support in the graphical session we require the `xdg-desktop-portal` layer for rivers' backend `wlroots`, together with some tools (`grim`, `slurp`, `wl-clipboard`) to perform and save screenshots to the clipboard:
|
||||
|
||||
``` shell-session
|
||||
sh# emerge -a gui-libs/xdg-desktop-portal-wlr gui-apps/grim gui-app/slurp gui-apps/wl-clipboard
|
||||
```
|
||||
|
||||
The `xdg-desktop-portal` should be run supervised, therefore create the `xdg-desktop-portal-wlr` user service:
|
||||
|
||||
``` shell title="~/.config/rc/init.d/xdg-desktop-portal-wlr"
|
||||
|
||||
depend() {
|
||||
need graphical-session
|
||||
need audio-session
|
||||
}
|
||||
|
||||
supervisor=supervise-daemon
|
||||
command="/usr/libexec/xdg-desktop-portal-wlr"
|
||||
```
|
||||
|
||||
and add it to `<runlevel-post-graphical-session>`:
|
||||
|
||||
```
|
||||
sh$ rc-update -U add xdg-desktop-portal-wlr h
|
||||
```
|
||||
|
||||
## Concluding remarks
|
||||
|
||||
Well, I think we are there, a minimal graphical session suitable for everyday use. Regarding the customisation of the installed gui-apps, you may have a look at [my gentoo-desktop repository](https://git.lucbijl.nl/luc/gentoo-desktop), or if you think you have a better taste for aesthetic, then do it yourself you cynical connoisseur!
|
Loading…
Add table
Add a link
Reference in a new issue