#!/usr/bin/env bash set -Eeuo pipefail # Baut das schlanke "Golden Image" fuer das Curtin/FAI-artige Deployment- # Backend (siehe ADR-0024 und den Plan unter # platform-docs/adr/0024-*.md) - eine reine, unbeaufsichtigte # debootstrap-Basis OHNE Desktop-Umgebung. Cinnamon/Mint-Tools/Theming # kommen NICHT hierher, sondern werden nach dem ersten Boot vom # Provisioning Agent per Ansible-Blueprint ausgerollt (ADR-0002) - dieses # Image muss nur ueberall booten und Netzwerk haben. # # Bewusst reines Debian (nicht Mints Ubuntu-Basis) als Zielsystem: die # kundensichtbare Identitaet kommt komplett aus der Ansible-Schicht, die # exakte Basis-Distribution des Images ist zweitrangig - siehe # Plan-Kontext. Kein Root-Passwort, keine SSH-Hostkeys im Ergebnis (siehe # Kommentare unten) - echte Zugangsdaten entstehen erst durch den # Provisioning Agent nach der Installation, ein Golden Image mit # eingebrannten Zugangsdaten waere ein Sicherheitsrisiko fuer JEDES damit # ausgerollte Kundengeraet, nicht nur fuer einen einzelnen Testlauf. # # Laeuft auf jedem Debian-basierten Host mit debootstrap - keine VM # noetig (real auf enterprise/LMDE 6 verifiziert, siehe Kommentar bei # SUITE weiter unten). usage() { echo "Usage: $0 [suite] [arch]" >&2 echo " suite: Debian-Suite fuer debootstrap (Standard: trixie)" >&2 echo " arch: Zielarchitektur (Standard: amd64)" >&2 } OUTPUT_FILE="${1:-}" # trixie (Debian 13) statt bookworm (30.08.2026 umgestellt): bookworm ist # inzwischen oldstable, trixie liefert ausserdem von Haus aus einen # neueren Kernel - direkt relevant fuers Ziel "moeglichst breite # Hardwareunterstuetzung", staerker als der Secure-Boot-Aspekt (die # shim-signed/grub-efi-amd64-signed-Kette funktioniert in beiden Suiten # identisch, kein Unterschied). debootstrap braucht dafuer keine # Uebereinstimmung mit der Suite des Build-Hosts (hier: LMDE 6/bookworm- # basiert) - das Bootstrapping laeuft komplett isoliert im chroot. SUITE="${2:-trixie}" ARCH="${3:-amd64}" MIRROR="http://deb.debian.org/debian" if [[ -z "${OUTPUT_FILE}" ]]; then usage exit 1 fi if [[ "${EUID}" -ne 0 ]]; then echo "Fehler: muss als root laufen (debootstrap/chroot/mount)." >&2 exit 1 fi for cmd in debootstrap chroot tar zstd; do command -v "${cmd}" >/dev/null 2>&1 || { echo "Fehler: benötigtes Werkzeug fehlt: ${cmd}" >&2; exit 1; } done WORK_DIR="$(mktemp -d /var/tmp/tuxflotte-golden-image.XXXXXX)" readonly WORK_DIR MOUNTED=() cleanup() { local mnt # In umgekehrter Reihenfolge aushaengen - /dev/pts vor /dev usw. for ((i = ${#MOUNTED[@]} - 1; i >= 0; i--)); do mnt="${MOUNTED[i]}" umount --recursive "${mnt}" 2>/dev/null || umount --lazy "${mnt}" 2>/dev/null || true done chmod -R u+w "${WORK_DIR}" 2>/dev/null || true rm -rf "${WORK_DIR}" } trap cleanup EXIT bind_mount() { local target="$1" mount --bind "${target}" "${WORK_DIR}${target}" MOUNTED+=("${WORK_DIR}${target}") } echo "==> debootstrap ${SUITE}/${ARCH} nach ${WORK_DIR}" debootstrap --arch="${ARCH}" "${SUITE}" "${WORK_DIR}" "${MIRROR}" # Real beim Testen entdeckt (29.08.2026): mktemp -d legt WORK_DIR mit Modus # 0700 an (sein eigenes Sicherheitsstandardverhalten) - debootstrap aendert # die Rechte des ihm uebergebenen Zielverzeichnisses selbst nicht, und # tar -p uebernimmt diesen Modus beim spaeteren Verpacken/Entpacken 1:1 als # Root-Verzeichnis-Rechte des gesamten ausgerollten Dateisystems. Ergebnis: # JEDER nicht-root-Prozess (z.B. dbus-daemon, das nach dem Start auf den # unprivilegierten "messagebus"-Benutzer wechselt) bekommt dann "Permission # denied" schon beim Traversieren von "/" - reale Boot-Fehlerkaskade # (dbus/logind/NetworkManager/wpa_supplicant schlagen alle fehl) live auf # der QEMU-Testplatte beobachtet. Ein Root-Dateisystem MUSS 0755 sein wie # bei jeder normalen Linux-Installation. chmod 0755 "${WORK_DIR}" echo "==> apt-Quellen fuer non-free-firmware + backports einrichten" # non-free-firmware ist seit Debian 12 eine eigene, standardmaessig NICHT # aktive Komponente (anders als bei Ubuntu/Mint) - ohne sie fehlt auf # vielen echten Geraeten die WLAN-/GPU-Firmware. Backports fuer einen # neueren Kernel (bessere Hardwareunterstuetzung auf aktueller Hardware # als der Suite-Standardkernel). cat > "${WORK_DIR}/etc/apt/sources.list" < /dev, /proc, /sys, /dev/pts einbinden fuer chroot-Paketinstallation" bind_mount /dev mount -t proc proc "${WORK_DIR}/proc" MOUNTED+=("${WORK_DIR}/proc") mount -t sysfs sysfs "${WORK_DIR}/sys" MOUNTED+=("${WORK_DIR}/sys") mount -t devpts devpts "${WORK_DIR}/dev/pts" MOUNTED+=("${WORK_DIR}/dev/pts") # DNS-Aufloesung im chroot braucht eine eigene resolv.conf - der # Netzwerk-Namespace wird zwar geteilt, aber /etc/resolv.conf ist eine # normale Datei innerhalb des debootstrap-Baums, nicht automatisch mit # der des Hosts identisch. cp /etc/resolv.conf "${WORK_DIR}/etc/resolv.conf" echo "==> Pakete im chroot installieren (Kernel, Firmware, Netzwerk, SSH)" # Paketliste ist ein erster, plausibler Startpunkt - wird nach dem ersten # echten Boot-Test (Phase 1 des Plans) voraussichtlich noch angepasst, # je nachdem was auf der Testhardware/-VM tatsaechlich fehlt. # # WLAN-Abdeckung bewusst breit (30.08.2026 ergaenzt) - die gaengigen # Chip-Hersteller bei Consumer-PCs/Notebooks sind ueberschaubar (Intel, # Realtek, MediaTek, Broadcom, Qualcomm Atheros): # - firmware-iwlwifi/firmware-realtek/firmware-atheros: je Hersteller # - firmware-brcm80211: Broadcom (BCM43xx) - haeufig in aelteren/gewissen # OEM-Notebooks, wurde vorher NICHT explizit abgedeckt # - firmware-misc-nonfree: Sammelpaket, deckt u.a. MediaTek-Restfaelle ab # - wireless-regdb: die Regulatory-Datenbank fuers Funkband - ohne sie # schraenkt der Kernel Kanaele/Sendeleistung ein bzw. verweigert # bestimmte Betriebsmodi, leicht zu uebersehen, da WLAN grundsaetzlich # noch "geht" (nur eingeschraenkter/non-compliant) # - wpasupplicant: WPA/WPA2/WPA3-Handshake - kam bisher nur transitiv als # NetworkManager-Abhaengigkeit mit, jetzt explizit fuer Klarheit # - iw, rfkill: Diagnose/Kontrollwerkzeuge (Funk-Kill-Switch, manuelle # Fehlersuche) - kein Laufzeit-Erfordernis, aber Standardausstattung chroot "${WORK_DIR}" /bin/bash -c ' set -e export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y \ linux-image-amd64 \ firmware-linux \ firmware-realtek \ firmware-iwlwifi \ firmware-atheros \ firmware-brcm80211 \ firmware-misc-nonfree \ wireless-regdb \ wpasupplicant \ iw \ rfkill \ network-manager \ openssh-server \ sudo \ locales \ curl \ ca-certificates \ parted apt-get clean ' echo "==> Golden Image von eingebrannten Zugangsdaten befreien" # Debootstrap setzt kein Root-Passwort (Konto bleibt gesperrt) - das # bleibt bewusst so. openssh-server generiert beim Postinst aber eigene # Hostkeys, die JEDES aus diesem Image ausgerollte Geraet sonst identisch # mit-ausliefern wuerde (Kollisionsrisiko + Sicherheitsproblem) - werden # hier entfernt, die Deployment-Mechanik (Plan Phase 1) erzeugt sie beim # eigentlichen Ausrollen pro Geraet frisch. rm -f "${WORK_DIR}"/etc/ssh/ssh_host_* : > "${WORK_DIR}/etc/machine-id" echo "==> Aushaengen" for ((i = ${#MOUNTED[@]} - 1; i >= 0; i--)); do umount --recursive "${MOUNTED[i]}" done MOUNTED=() echo "==> Packe als ${OUTPUT_FILE}" tar --numeric-owner --xattrs -C "${WORK_DIR}" -cf - . | zstd -f -T0 -19 -o "${OUTPUT_FILE}" echo "==> Fertig: ${OUTPUT_FILE} ($(du -h "${OUTPUT_FILE}" | cut -f1))"