Thomas Stallinger 683cf70af5 fix: /updates auf dem Live-System deployen + Kunden-ISO-Build (Phase 4)
Casper merged /updates nie automatisch auf das gebootete Live-System -
eine seit Projektbeginn unverifizierte Annahme, die erst beim echten
QEMU-Boot (UEFI, kompletter GRUB->Live-Desktop-Pfad) einer personalisierten
Kunden-ISO aufgefallen ist: /opt/tuxflotte fehlte trotz korrekt auf dem
Medium liegendem /cdrom/updates vollständig.

Fix per casper-bottom-Hook, eingebettet via Initrd-Cpio-Konkatenation
(scripts/lib/initrd.sh, derselbe in Phase 1 verifizierte Mechanismus wie
beim Kexec-Preseed). Wichtig dabei: ein komplett neuer Hook-Skriptname wird
nie ausgeführt, weil mkinitramfs eine statische ORDER-Datei mit der
Aufrufliste ins Initrd backt - stattdessen wird der Inhalt des bereits
gelisteten, garantiert letzten Skripts (99casperboot) überschrieben.

start-kiosk.sh erkennt jetzt TUXFLOTTE_AUTO_MODE und startet den Installer
automatisch statt der Kiosk-Startseite. build_customer_iso.sh baut daraus
personalisierte Kunden-ISOs (WLAN-Zugangsdaten + Enrollment-Session-Code).

Nebenbei zwei vorbestehende Bugs behoben: xorriso -osirrox übernimmt
Original-ISO-Dateirechte (oft 444/555, kein Write-Bit), was cp/rm -rf in
build.sh/build_customer_iso.sh bisher unbemerkt kaputt gemacht hat.

Real per QEMU verifiziert (echter GRUB-Boot einer gebauten Kunden-ISO).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 22:18:04 +02:00

254 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SOURCE_ISO="${1:-}"
BACKEND="${2:-}"
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BASE_DIR="$(cd "$REPO_DIR/.." && pwd)"
BUILD_DIR="$BASE_DIR/build"
WORK_DIR="$BUILD_DIR/work"
OUTPUT_DIR="$BUILD_DIR/output"
# shellcheck source=lib/initrd.sh
source "$REPO_DIR/scripts/lib/initrd.sh"
# Test-Device fuer den in bake_test_preseed() eingebrannten, nicht
# personalisierten Test-Preseed (Mint) - dasselbe "Default Lab"-Testgeraet,
# das schon fuer die Fedora-Verifikation genutzt wurde. Echte Personalisierung
# pro Kunde ist nicht Teil dieses Build-Skripts (siehe ADR-0011-Kontext).
TEST_DEVICE_ID="a0238a0b-d2b5-4516-a6ce-da7170041d11"
TEST_HOSTNAME="tuxflotte-mint-test"
usage() {
echo "Usage: $0 /path/to/source.iso <fedora|mint>"
}
check_input() {
if [[ -z "$SOURCE_ISO" || -z "$BACKEND" ]]; then
usage
exit 1
fi
if [[ ! -f "$SOURCE_ISO" ]]; then
echo "Error: ISO not found: $SOURCE_ISO"
exit 1
fi
case "$BACKEND" in
fedora|mint) ;;
*)
echo "Error: unbekanntes Backend '$BACKEND' (erwartet: fedora oder mint)"
exit 1
;;
esac
}
check_dependencies() {
for cmd in xorriso; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: missing dependency: $cmd"
exit 1
fi
done
if [[ "$BACKEND" == "mint" ]]; then
for cmd in jq envsubst base64 cpio; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: missing dependency: $cmd"
exit 1
fi
done
fi
}
prepare_dirs() {
mkdir -p "$BUILD_DIR" "$OUTPUT_DIR"
# xorriso extrahiert Verzeichnisse teils mit den Original-ISO-Rechten
# (z.B. 555, kein Write-Bit) - ohne chmod schlaegt rm -rf darauf fehl.
chmod -R u+w "$WORK_DIR" 2>/dev/null || true
rm -rf "$WORK_DIR"
mkdir -p "$WORK_DIR"
}
extract_iso() {
echo "Extracting ISO..."
xorriso -indev "$SOURCE_ISO" -osirrox on -extract / "$WORK_DIR" >/dev/null
# xorriso uebernimmt die Original-ISO-Rechte (haeufig 555 auf
# Verzeichnissen, 444 auf Dateien) - ohne Write-Bit koennen weder
# spaetere Schritte (patch_grub) noch spaetere Aufraeumlaeufe
# (prepare_dirs) Dateien darin ersetzen/entfernen.
chmod -R u+w "$WORK_DIR"
}
patch_grub() {
echo "Installing Tuxflotte GRUB configuration..."
# -f: xorriso extrahiert ISO-Dateien mit deren Original-Rechten, die auf
# Live-Medien haeufig read-only (444) sind - ohne -f schlaegt cp beim
# Ueberschreiben mit "Keine Berechtigung" fehl.
if [[ "$BACKEND" == "fedora" ]]; then
cp -f "$REPO_DIR/grub/EFI-BOOT-grub.cfg" \
"$WORK_DIR/EFI/BOOT/grub.cfg"
cp -f "$REPO_DIR/grub/boot-grub2-grub.cfg" \
"$WORK_DIR/boot/grub2/grub.cfg"
else
# Mint hat, anders als Fedora, keine zweite ESP/FAT-Kopie des GRUB-Menüs,
# die separat gepatcht werden müsste (real gegen die vorhandene
# tuxflotte-mint-test.iso verifiziert - x86_64-efi/grub.cfg ist nur ein
# Loader-Stub, der per `source /boot/grub/grub.cfg` zurückverweist).
cp -f "$REPO_DIR/grub/mint-boot-grub.cfg" \
"$WORK_DIR/boot/grub/grub.cfg"
cp -f "$REPO_DIR/grub/mint-isolinux-live.cfg" \
"$WORK_DIR/isolinux/live.cfg"
fi
}
verify_workdir() {
echo "Verifying workdir..."
if [[ "$BACKEND" == "fedora" ]]; then
grep -q "Tuxflotte" "$WORK_DIR/EFI/BOOT/grub.cfg"
grep -q "Tuxflotte" "$WORK_DIR/boot/grub2/grub.cfg"
else
grep -q "Tuxflotte" "$WORK_DIR/boot/grub/grub.cfg"
grep -q "Tuxflotte" "$WORK_DIR/isolinux/live.cfg"
fi
echo "GRUB verification passed."
}
bake_test_preseed() {
[[ "$BACKEND" == "mint" ]] || return 0
echo "Rendering test preseed (nicht personalisiert, siehe TEST_DEVICE_ID)..."
local preseed_dir="$WORK_DIR/preseed"
local runtime_blueprint_file="$WORK_DIR/runtime_blueprint.json"
local server_response_file="$WORK_DIR/response.json"
mkdir -p "$preseed_dir"
cat > "$runtime_blueprint_file" <<EOF
{
"runtime_blueprint": {
"backend_id": "mint",
"blueprints": [],
"installation_directives": {
"disk_encryption": false,
"partitioning": {"scheme": "single", "root_filesystem": "ext4"},
"secure_boot_required": false
}
}
}
EOF
cat > "$server_response_file" <<EOF
{
"device": {"hostname": "${TEST_HOSTNAME}", "id": "${TEST_DEVICE_ID}"}
}
EOF
# backend.sh direkt wiederverwenden statt die Templating-Logik hier zu
# duplizieren - Variablen sind bewusst nicht readonly (siehe backend.sh),
# backend_init() wird übersprungen (braucht root für --owner/--group,
# hier nicht nötig, mkdir reicht für den Build-Kontext).
# shellcheck disable=SC1091
source "$REPO_DIR/backends/mint/backend.sh"
RUNTIME_BLUEPRINT_FILE="$runtime_blueprint_file"
SERVER_RESPONSE_FILE="$server_response_file"
RUNTIME_DIR="$preseed_dir"
CONFIG_FILE="$preseed_dir/tuxflotte.seed"
backend_validate
backend_generate_config
echo "Test-Preseed erzeugt: $CONFIG_FILE"
}
prepare_updates() {
echo "Assembling live-updates payload..."
local updates_dir="$WORK_DIR/updates"
rm -rf "$updates_dir"
mkdir -p "$updates_dir/opt/tuxflotte"
cp -a "$REPO_DIR/live-updates/." "$updates_dir/"
cp -a "$REPO_DIR/scripts" "$updates_dir/opt/tuxflotte/"
cp -a "$REPO_DIR/backends" "$updates_dir/opt/tuxflotte/"
cp -a "$REPO_DIR/config" "$updates_dir/opt/tuxflotte/"
chmod 0440 "$updates_dir/etc/sudoers.d/90-tuxflotte"
}
patch_initrd() {
[[ "$BACKEND" == "mint" ]] || return 0
echo "Injecting casper-bottom updates-hook into initrd..."
inject_casper_bottom_hooks "$REPO_DIR" \
"$WORK_DIR/casper/initrd.lz" \
"$WORK_DIR/casper/initrd-tuxflotte.lz"
}
create_iso() {
echo "Creating Tuxflotte ISO..."
local output_iso
local -a grub_map_args
if [[ "$BACKEND" == "fedora" ]]; then
output_iso="$OUTPUT_DIR/tuxflotte-provisioning-0.2.iso"
grub_map_args=(
-map "$REPO_DIR/grub/EFI-BOOT-grub.cfg" /EFI/BOOT/grub.cfg
-map "$REPO_DIR/grub/boot-grub2-grub.cfg" /boot/grub2/grub.cfg
)
else
output_iso="$OUTPUT_DIR/tuxflotte-mint-provisioning-0.1.iso"
grub_map_args=(
-map "$REPO_DIR/grub/mint-boot-grub.cfg" /boot/grub/grub.cfg
-map "$REPO_DIR/grub/mint-isolinux-live.cfg" /isolinux/live.cfg
-map "$WORK_DIR/preseed/tuxflotte.seed" /preseed/tuxflotte.seed
-map "$WORK_DIR/casper/initrd-tuxflotte.lz" /casper/initrd.lz
)
fi
rm -f "$output_iso"
xorriso \
-indev "$SOURCE_ISO" \
-outdev "$output_iso" \
-compliance no_emul_toc \
-volid TUXFLOTTE \
"${grub_map_args[@]}" \
-map "$WORK_DIR/updates" /updates \
-chown_r 0 /updates -- \
-chgrp_r 0 /updates -- \
-boot_image any replay
echo "ISO created: $output_iso"
}
main() {
check_input
check_dependencies
prepare_dirs
extract_iso
patch_grub
verify_workdir
bake_test_preseed
prepare_updates
patch_initrd
create_iso
echo
echo "Build complete."
echo "Work dir: $WORK_DIR"
}
main "$@"