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>
This commit is contained in:
parent
108444ecf7
commit
683cf70af5
38
initrd-hooks/casper-bottom/99casperboot
Executable file
38
initrd-hooks/casper-bottom/99casperboot
Executable file
@ -0,0 +1,38 @@
|
||||
#! /bin/sh
|
||||
|
||||
PREREQ=""
|
||||
|
||||
prereqs()
|
||||
{
|
||||
echo "$PREREQ"
|
||||
}
|
||||
|
||||
case $1 in
|
||||
# get pre-requisites
|
||||
prereqs)
|
||||
prereqs
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Ueberschreibt das originale 99casperboot (macht nur `touch
|
||||
# /run/.casper-boot`) statt einen zusaetzlichen, neuen Hook-Skriptnamen
|
||||
# einzufuehren: initramfs-tools generiert beim ISO-Bau eine ORDER-Datei
|
||||
# (scripts/casper-bottom/ORDER) mit der fest verdrahteten Aufruf-Reihenfolge
|
||||
# aller casper-bottom-Skripte - real durch Extraktion des echten initrd.lz
|
||||
# mit unmkinitramfs bestaetigt. Ein per Initrd-Cpio-Layer NEU hinzugefuegtes
|
||||
# Skript taucht in dieser ORDER-Datei nicht auf und wird nie aufgerufen,
|
||||
# selbst wenn die Datei im initramfs physisch vorhanden ist (erste Version
|
||||
# dieses Fixes ist daran gescheitert). Das Ueberschreiben eines bereits in
|
||||
# ORDER referenzierten Pfads funktioniert dagegen zuverlaessig (spaetere
|
||||
# cpio-Schichten ueberschreiben Dateiinhalte am selben Pfad, siehe
|
||||
# scripts/lib/initrd.sh - derselbe Mechanismus, der schon fuer das
|
||||
# Kexec-Preseed in Phase 1 verifiziert wurde). 99casperboot ist laut ORDER
|
||||
# das letzte casper-bottom-Skript ueberhaupt - garantiert nach 05mountpoints,
|
||||
# das /cdrom nach /root/cdrom umhaengt und damit die Voraussetzung fuer den
|
||||
# folgenden Kopiervorgang schafft.
|
||||
if [ -d /root/cdrom/updates ]; then
|
||||
cp -a /root/cdrom/updates/. /root/
|
||||
fi
|
||||
|
||||
touch /run/.casper-boot
|
||||
@ -30,4 +30,13 @@ dconf write /org/nemo/desktop/show-desktop-icons false
|
||||
dconf write /org/cinnamon/enabled-applets "['panel1:left:0:window-list@cinnamon.org:1', 'panel1:right:0:power@cinnamon.org:2']"
|
||||
|
||||
mkdir -p /home/liveuser/.local/share/tuxflotte-kiosk
|
||||
|
||||
# Auto-Modus (personalisierte Kunden-ISO, siehe scripts/build_customer_iso.sh)
|
||||
# startet den Installer sofort unbeaufsichtigt statt der Kiosk-Startseite -
|
||||
# config/installer.conf wird nur bei personalisierten Abbildern mitgeliefert,
|
||||
# das Standard-/Testabbild bleibt unverändert interaktiv.
|
||||
if grep -qE '^\s*(export\s+)?TUXFLOTTE_AUTO_MODE=true\s*$' /opt/tuxflotte/config/installer.conf 2>/dev/null; then
|
||||
exec sudo /opt/tuxflotte/scripts/installer.sh
|
||||
fi
|
||||
|
||||
exec epiphany --profile=/home/liveuser/.local/share/tuxflotte-kiosk file:///opt/tuxflotte/kiosk/index.html
|
||||
|
||||
@ -10,6 +10,9 @@ 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
|
||||
@ -50,7 +53,7 @@ check_dependencies() {
|
||||
done
|
||||
|
||||
if [[ "$BACKEND" == "mint" ]]; then
|
||||
for cmd in jq envsubst base64; do
|
||||
for cmd in jq envsubst base64 cpio; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
echo "Error: missing dependency: $cmd"
|
||||
exit 1
|
||||
@ -61,6 +64,9 @@ check_dependencies() {
|
||||
|
||||
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"
|
||||
}
|
||||
@ -68,26 +74,34 @@ prepare_dirs() {
|
||||
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 "$REPO_DIR/grub/EFI-BOOT-grub.cfg" \
|
||||
cp -f "$REPO_DIR/grub/EFI-BOOT-grub.cfg" \
|
||||
"$WORK_DIR/EFI/BOOT/grub.cfg"
|
||||
|
||||
cp "$REPO_DIR/grub/boot-grub2-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 "$REPO_DIR/grub/mint-boot-grub.cfg" \
|
||||
cp -f "$REPO_DIR/grub/mint-boot-grub.cfg" \
|
||||
"$WORK_DIR/boot/grub/grub.cfg"
|
||||
|
||||
cp "$REPO_DIR/grub/mint-isolinux-live.cfg" \
|
||||
cp -f "$REPO_DIR/grub/mint-isolinux-live.cfg" \
|
||||
"$WORK_DIR/isolinux/live.cfg"
|
||||
fi
|
||||
}
|
||||
@ -171,6 +185,16 @@ prepare_updates() {
|
||||
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..."
|
||||
|
||||
@ -189,6 +213,7 @@ create_iso() {
|
||||
-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
|
||||
|
||||
@ -217,6 +242,7 @@ main() {
|
||||
verify_workdir
|
||||
bake_test_preseed
|
||||
prepare_updates
|
||||
patch_initrd
|
||||
create_iso
|
||||
|
||||
echo
|
||||
|
||||
109
scripts/build_customer_iso.sh
Executable file
109
scripts/build_customer_iso.sh
Executable file
@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Baut eine personalisierte Kunden-ISO fuer den Self-Service-Flow (Phase 4).
|
||||
# Anders als build.sh (das fuer Testzwecke einen statischen Preseed mit
|
||||
# Fake-Geraete-ID einbrennt, siehe bake_test_preseed()) wird hier nur
|
||||
# config/installer.conf mit den echten, organisationsspezifischen Werten
|
||||
# befuellt - Hostname/Geraete-ID/Partitionierung werden weiterhin erst live
|
||||
# auf dem Zielgeraet erzeugt (siehe backend_generate_config() in
|
||||
# backends/mint/backend.sh). Hier geht es nur um WLAN-Zugangsdaten +
|
||||
# Enrollment-Session-Code als Boot-Konfiguration, die start-kiosk.sh dazu
|
||||
# bringt, den Installer beim Hochfahren automatisch zu starten statt der
|
||||
# interaktiven Kiosk-Startseite.
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <source.iso> <output.iso> <activation_code> [wifi_ssid] [wifi_psk]" >&2
|
||||
}
|
||||
|
||||
SOURCE_ISO="${1:-}"
|
||||
OUTPUT_ISO="${2:-}"
|
||||
ACTIVATION_CODE="${3:-}"
|
||||
WIFI_SSID="${4:-}"
|
||||
WIFI_PSK="${5:-}"
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
# shellcheck source=lib/initrd.sh
|
||||
source "$REPO_DIR/scripts/lib/initrd.sh"
|
||||
|
||||
cleanup() {
|
||||
# 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}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -z "${SOURCE_ISO}" || -z "${OUTPUT_ISO}" || -z "${ACTIVATION_CODE}" ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ -f "${SOURCE_ISO}" ]] ||
|
||||
{ echo "Error: Quell-ISO nicht gefunden: ${SOURCE_ISO}" >&2; exit 1; }
|
||||
|
||||
for cmd in xorriso cpio; do
|
||||
command -v "${cmd}" >/dev/null 2>&1 ||
|
||||
{ echo "Error: fehlendes Werkzeug: ${cmd}" >&2; exit 1; }
|
||||
done
|
||||
|
||||
echo "Extrahiere Quell-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 noch der Cleanup-Trap Dateien darin ersetzen/entfernen.
|
||||
chmod -R u+w "${WORK_DIR}"
|
||||
|
||||
echo "Installiere Tuxflotte-GRUB-Konfiguration..."
|
||||
# -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.
|
||||
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"
|
||||
|
||||
grep -q "Tuxflotte" "${WORK_DIR}/boot/grub/grub.cfg"
|
||||
grep -q "Tuxflotte" "${WORK_DIR}/isolinux/live.cfg"
|
||||
|
||||
echo "Baue personalisierte Boot-Konfiguration..."
|
||||
updates_dir="${WORK_DIR}/updates"
|
||||
rm -rf "${updates_dir}"
|
||||
mkdir -p "${updates_dir}"
|
||||
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/"
|
||||
mkdir -p "${updates_dir}/opt/tuxflotte/config"
|
||||
|
||||
{
|
||||
echo "export TUXFLOTTE_AUTO_MODE=true"
|
||||
printf 'export TUXFLOTTE_ACTIVATION_CODE=%q\n' "${ACTIVATION_CODE}"
|
||||
[[ -n "${WIFI_SSID}" ]] && printf 'export TUXFLOTTE_WIFI_SSID=%q\n' "${WIFI_SSID}"
|
||||
[[ -n "${WIFI_PSK}" ]] && printf 'export TUXFLOTTE_WIFI_PSK=%q\n' "${WIFI_PSK}"
|
||||
} >"${updates_dir}/opt/tuxflotte/config/installer.conf"
|
||||
|
||||
chmod 0440 "${updates_dir}/etc/sudoers.d/90-tuxflotte"
|
||||
|
||||
echo "Injiziere casper-bottom Updates-Hook in die Initrd..."
|
||||
custom_initrd="${WORK_DIR}/casper/initrd-tuxflotte.lz"
|
||||
inject_casper_bottom_hooks "${REPO_DIR}" \
|
||||
"${WORK_DIR}/casper/initrd.lz" \
|
||||
"${custom_initrd}"
|
||||
|
||||
echo "Erzeuge Kunden-ISO..."
|
||||
rm -f "${OUTPUT_ISO}"
|
||||
|
||||
xorriso \
|
||||
-indev "${SOURCE_ISO}" \
|
||||
-outdev "${OUTPUT_ISO}" \
|
||||
-compliance no_emul_toc \
|
||||
-volid TUXFLOTTE \
|
||||
-map "${REPO_DIR}/grub/mint-boot-grub.cfg" /boot/grub/grub.cfg \
|
||||
-map "${REPO_DIR}/grub/mint-isolinux-live.cfg" /isolinux/live.cfg \
|
||||
-map "${custom_initrd}" /casper/initrd.lz \
|
||||
-map "${updates_dir}" /updates \
|
||||
-chown_r 0 /updates -- \
|
||||
-chgrp_r 0 /updates -- \
|
||||
-boot_image any replay
|
||||
|
||||
echo "Kunden-ISO erstellt: ${OUTPUT_ISO}"
|
||||
42
scripts/lib/initrd.sh
Normal file
42
scripts/lib/initrd.sh
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
# Wird von build.sh und build_customer_iso.sh eingebunden (source), nicht
|
||||
# eigenstaendig ausgefuehrt.
|
||||
|
||||
# Haengt initrd-hooks/casper-bottom/* als zusaetzliche cpio-Schicht an ein
|
||||
# bestehendes Casper-Initrd an - derselbe Mechanismus, den
|
||||
# backend_launch() (backends/mint/backend.sh) bereits fuer das
|
||||
# Kexec-Preseed nutzt und der per QEMU verifiziert ist: der Kernel haengt
|
||||
# spaeter aneinandergereihte cpio-Archive additiv an ein bestehendes
|
||||
# initramfs an. Noetig, weil Casper keinen automatischen /updates-Merge
|
||||
# kennt (real per QEMU-Boot widerlegt) - der Hook selbst kopiert
|
||||
# /cdrom/updates beim Boot auf das Root-Dateisystem, siehe
|
||||
# initrd-hooks/casper-bottom/21tuxflotte_updates.
|
||||
inject_casper_bottom_hooks() {
|
||||
local repo_dir="$1"
|
||||
local src_initrd="$2"
|
||||
local dest_initrd="$3"
|
||||
|
||||
local hook_dir="${repo_dir}/initrd-hooks"
|
||||
local layer_dir
|
||||
layer_dir="$(mktemp -d)"
|
||||
# Ausserhalb von layer_dir, damit die Archiv-Datei sich beim Bauen nicht
|
||||
# selbst als leeren Eintrag einfaengt (find laeuft im selben Moment, in
|
||||
# dem die Shell-Redirection sie bereits anlegt).
|
||||
local cpio_out
|
||||
cpio_out="$(mktemp)"
|
||||
|
||||
cp -a "${hook_dir}/casper-bottom" "${layer_dir}/scripts-casper-bottom-tmp"
|
||||
mkdir -p "${layer_dir}/scripts"
|
||||
mv "${layer_dir}/scripts-casper-bottom-tmp" "${layer_dir}/scripts/casper-bottom"
|
||||
chmod 0755 "${layer_dir}"/scripts/casper-bottom/*
|
||||
|
||||
(cd "${layer_dir}" && find . -mindepth 1 | cpio -o -H newc 2>/dev/null | gzip) \
|
||||
>"${cpio_out}" ||
|
||||
{ echo "Error: Casper-Bottom-Hook-Schicht konnte nicht gebaut werden." >&2; rm -rf "${layer_dir}"; rm -f "${cpio_out}"; return 1; }
|
||||
|
||||
cat "${src_initrd}" "${cpio_out}" >"${dest_initrd}" ||
|
||||
{ echo "Error: Initrd konnte nicht mit Hook-Schicht zusammengesetzt werden." >&2; rm -rf "${layer_dir}"; rm -f "${cpio_out}"; return 1; }
|
||||
|
||||
rm -rf "${layer_dir}"
|
||||
rm -f "${cpio_out}"
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user