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.
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.
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/grubGRUB_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 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.
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-…) …
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.