System starten

Der Rechner führt beim Einschalten zuerst die auf einem EEPROM gespeicherte Firmware aus. Diese startet einen Bootloader, der den Kernel des Betriebssystems lädt, welcher die initiale Ramdisk entpackt und schließlich die Kontrolle an den Init-Prozess (PID 1) übergibt.

Schritte beim Start des Rechners

Auf moderner Hardware ist die Firmware das UEFI und der Bootloader ein EFI-Programm, das auf der Systempartition (ESP) liegt. Wenn die Startliste keine Einträge hat, wird /EFI/BOOT/BOOTX64.EFI ausgeführt. Theoretisch lassen sich alle in diesem Kapitel beschriebenen Varianten parallel betreiben.

GRUB

Standardmäßig installiert Debian den komplexen GRUB Bootloader. Da er sehr viele Dateisysteme unterstützt, kann er den Kernel sogar von einem verschlüsselten Wurzeldateisystem laden.

/etc/default/grub
GRUB_DEFAULT=0 GRUB_TIMEOUT=5 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX=""

Nach Änderungen an der Konfiguration müssen diverse Skripte und Konfigurationsdateien unter /boot/grub neu erzeugt werden.

update-grub
tree /boot/grub
/boot/grub/
├── fonts
│   └── unicode.pf2
├── grub.cfg
├── grubenv
├── locale
│   ├── ast.mo
│   └── …
├── unicode.pf2
└── x86_64-efi
    ├── acpi.mod
    └── …

Der Bootloader wird auf der Systempartition installiert mit:

grub-install /dev/sda
tree /boot/efi
/boot/efi
└── EFI
    └── debian
        ├── shimx64.efi   # 1. Zwischenstufe mit Signatur von Microsoft
        ├── mmx64.efi     # 2. MOKManager
        ├── grubx64.efi   # 3. Bootloader
        ├── fbx64.efi     # 4. Fallback
        └── grub.cfg

Systemd-boot

Systemd enthält einen einfachen Bootloader für EFI-Systeme. Das Paket systemd-boot installiert das EFI-Programm in der Systempartition und legt Menüeinträge gemäß der Boot Loader Specification an.

apt install systemd-boot
apt purge grub-common
bootctl install
tree /boot/efi
/boot/efi
├── EFI
│   └── systemd
│       └── systemd-bootx64.efi
├── MachineId
│   └── Version-Architecture
│       ├── initrd.img-Version-Architecture
│       └── linux
└── loader
    ├── entries
    │   └── MachineId-Version-Architecture.conf
    ├── loader.conf
    ├── entries.srel
    └── random-seed

Mit dem Programm bootctl lassen sich die Einträge ansehen und verwalten.

EFIstub

Die Firmware kann den Kernel auch – ohne Umweg über einen Bootloader – direkt ausführen, da dieser standardmäßig einen EFI-Header enthält.

apt install efibootmgr efitools

Da die Paketverwaltung keine Pakete auf VFAT-Partitionen entpacken kann, muss folgendes Skript den Kernel nach jeder Aktualisierung dorthin kopieren und ihn in der Startliste verankern.

/etc/kernel/postinst.d/zz-update-efistub
#!/bin/sh -e # read configuration vers=${1:-$(uname -r)} root=$(sed 's/#.*//g' /etc/fstab | awk '($2 == "/") {print $1}') flag=$(sed 's/#.*//g' /etc/fstab | awk '($2 == "/") {print $4}') . /etc/os-release # install image install -D /boot/vmlinuz-$vers /boot/efi/linux install -D /boot/initrd.img-$vers /boot/efi/initrd efibootmgr -c -g \ -L "$PRETTY_NAME" \ -l '\linux' \ -u "initrd=\\initrd add_efi_memmap root=$root rootflags=$flag ro quiet splash"
efibootmgr -v
BootCurrent: 0000
Timeout: 2 seconds
BootOrder: 0000,…
Boot0000* Linux Boot Manager    HD(1,GPT,de4fb3a7-…)
…

SecureBoot

Um zu verhindern, dass ein Bösewicht heimlich den Kernel austauscht, kann man diesen kryptografisch signieren und mittels SecureBoot verifizieren. Da flexible Bootoptionen das Konzept eines sicheren Starts unterlaufen, werden die Kernel-Parameter fest in ein sogenanntes Unified Kernel Image eingebaut. Es enthält einen EFI-Header, den Kernel, die Ramdisk und Metadaten.

apt install sbsigntool

Das folgende Skript erzeugt beim ersten Aufruf alle benötigten Schlüssel und Zertifikate.

/etc/kernel/postinst.d/zz-update-secureboot
#!/bin/sh -e # build kernel command line from files system table vers=${1:-$(uname -r)} # 4.19.0-6-amd64 root=$(awk '($2 == "/") {print $1}' /etc/fstab) # /dev/mapper/root flag=$(awk '($2 == "/") {print $4}' /etc/fstab) # noatime,subvol=@root # generate keys once if [ ! -f /etc/efi/DB.key ] then # setup certification authority mkdir -p -m 0700 /etc/efi cd /etc/efi read uuid < /etc/machine-id > noPK.esl # generate keys and cerfificates for key in PK KEK DB do openssl req -new -x509 -newkey rsa:2048 -days 3650 -nodes -sha256 \ -subj "/CN=$HOSTNAME $key/" -keyout $key.key -out $key.crt openssl x509 -in $key.crt -out $key.cer -outform DER cert-to-efi-sig-list -g $uuid $key.crt $key.esl chmod 0600 $key.key done # create efi signature lists soon=$(date --date='1 second' +'%Y-%m-%d %H:%M:%S') sign-efi-sig-list -t "$soon" -k PK.key -c PK.crt PK PK.esl PK.auth sign-efi-sig-list -t "$soon" -k PK.key -c PK.crt PK noPK.esl noPK.auth sign-efi-sig-list -t "$soon" -k PK.key -c PK.crt KEK KEK.esl KEK.auth sign-efi-sig-list -t "$soon" -k KEK.key -c KEK.crt db DB.esl DB.auth cd - fi # build unified kernel image echo "root=$root rootflags=$flag ro quiet splash" > /tmp/cmdline gunzip -c /boot/initrd.img-$vers > /tmp/initrd objcopy \ --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \ --add-section .cmdline="/tmp/cmdline" --change-section-vma .cmdline=0x30000 \ --add-section .linux="/boot/vmlinuz-$vers" --change-section-vma .linux=0x40000 \ --add-section .initrd="/tmp/initrd" --change-section-vma .initrd=0x3000000 \ /usr/lib/systemd/boot/efi/linuxx64.efi.stub \ /tmp/linux sbsign \ --key /etc/efi/DB.key \ --cert /etc/efi/DB.crt \ --output /boot/efi/linux \ /tmp/linux efibootmgr --create --label "Debian GNU/Linux" --loader "\\linux" rm -f /tmp/{cmdline,initrd,linux}

Die Gültigkeit der Signatur lässt sich prüfen mit:

sbverify --cert /etc/efi/DB.crt /boot/efi/linux
Signature verification OK

Nachdem im Setup-Programm des Rechners die Schlüssel hinterlegt und SecureBoot aktiviert wurde, führt der Rechner nur noch korrekt signierte EFI-Programme aus.

Literatur

  1. Debian Wiki: EFIStub
  2. Debian Wiki: SecureBoot
  3. Systemd: The Boot Loader Specification
  4. Pid Eins: Brave New Trusted Boot World, 2022
  5. Roderick W. Smith: The rEFInd Boot Manager
  6. Roderick W. Smith: Controlling Secure Boot
  7. Matthew Bentley: Secure Boot on Arch Linux