From 72d0b11647f3d7026dbcd9cc370c5bd07bd498f7 Mon Sep 17 00:00:00 2001 From: Herb Date: Mon, 13 Jul 2026 13:21:32 +0200 Subject: [PATCH] Jahres-Reimage: Boot-Check, GRUB-Eintrag, Clonezilla-Batch-Skript reimage-check erkennt die Platte DEZNTRSICHERUNG mit neuerem Image und bootet in den GRUB-Eintrag Schul-Neuinstallation (Clonezilla toram). schul-reimage.sh: Mini-Menue (AUFSPIELEN auto nach 20 s / IMAGE ERSTELLEN / Abbrechen), ocs-sr im Batch-Modus ohne Nachpruefung (E-004), Hostname bleibt erhalten, Admin-Key nur auf laptop03 (E-005). local.yml haelt alles per Pull aktuell. Co-Authored-By: Claude Fable 5 --- files/clonezilla/grub-custom.cfg | 9 +++ files/clonezilla/schul-reimage.sh | 118 ++++++++++++++++++++++++++++++ files/reimage-check.service | 11 +++ files/reimage-check.sh | 37 ++++++++++ local.yml | 69 +++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 files/clonezilla/grub-custom.cfg create mode 100644 files/clonezilla/schul-reimage.sh create mode 100644 files/reimage-check.service create mode 100644 files/reimage-check.sh diff --git a/files/clonezilla/grub-custom.cfg b/files/clonezilla/grub-custom.cfg new file mode 100644 index 0000000..8c4c1c1 --- /dev/null +++ b/files/clonezilla/grub-custom.cfg @@ -0,0 +1,9 @@ +# Wird von GRUB (41_custom) zur Boot-Zeit gelesen — kein update-grub noetig. +# Ziel von "grub-reboot Schul-Neuinstallation" (reimage-check.service). +# Bootet Clonezilla-Live aus /boot/clonezilla komplett ins RAM (toram), +# damit die eigene Platte ueberschrieben werden kann. +menuentry "Schul-Neuinstallation" { + search --no-floppy --file --set=root /boot/clonezilla/vmlinuz + linux /boot/clonezilla/vmlinuz boot=live union=overlay username=user config components noswap edd=on noprompt nosplash live-media-path=/boot/clonezilla toram=filesystem.squashfs locales=de_DE.UTF-8 keyboard-layouts=de ocs_live_batch="yes" ocs_live_run="bash /run/live/medium/boot/clonezilla/schul-reimage.sh" + initrd /boot/clonezilla/initrd.img +} diff --git a/files/clonezilla/schul-reimage.sh b/files/clonezilla/schul-reimage.sh new file mode 100644 index 0000000..0568ad8 --- /dev/null +++ b/files/clonezilla/schul-reimage.sh @@ -0,0 +1,118 @@ +#!/bin/bash +# +# Laeuft in der Clonezilla-Live-Umgebung (via ocs_live_run, toram). +# Eigenes Mini-Menue statt Clonezilla-TUI, alle Sicherheitsschranken aus (E-004): +# +# AUFSPIELEN - neuestes Image von der Platte, Hostname bleibt erhalten +# (vorausgewaehlt, startet nach 20 s automatisch) +# IMAGE ERSTELLEN - sichert diesen Rechner als schule-laptop- +# ABBRECHEN - Neustart ins normale System +# +set -u + +DISK="sda" +ROOT_PART="/dev/sda2" # 12x identische Hardware: UEFI, sda1=EFI, sda2=System +LABEL="DEZNTRSICHERUNG" +IMG_MNT="/home/partimag" # Clonezilla-Standardpfad fuer Images +VERSION_FILE="/etc/schul-image-version" + +rot() { echo -e "\033[0;31m$*\033[0m"; } +gruen(){ echo -e "\033[0;32m$*\033[0m"; } +gelb() { echo -e "\033[1;33m$*\033[0m"; } + +# ---------- Image-Platte einbinden ---------- +mkdir -p "$IMG_MNT" +if ! mountpoint -q "$IMG_MNT"; then + DEV=$(blkid -L "$LABEL" 2>/dev/null) + if [ -z "$DEV" ] || ! mount "$DEV" "$IMG_MNT"; then + rot "Platte '$LABEL' nicht gefunden — Neustart in 15 s." + sleep 15; reboot; exit 1 + fi +fi + +NEWEST=$(find "$IMG_MNT" -maxdepth 1 -name 'schule-laptop-*' -type d -printf '%f\n' | sort | tail -1) + +# ---------- Hostname aus dem alten System retten ---------- +OLD_HOSTNAME="" +mkdir -p /mnt/altes-system +if mount -o ro "$ROOT_PART" /mnt/altes-system 2>/dev/null; then + OLD_HOSTNAME=$(cat /mnt/altes-system/etc/hostname 2>/dev/null | tr -d '[:space:]') + umount /mnt/altes-system +fi + +# ---------- Mini-Menue ---------- +clear +gruen "===============================================" +gruen " SCHUL-LAPTOPS — IMAGE-STATION" +gruen "===============================================" +echo "Rechner: ${OLD_HOSTNAME:-unbekannt}" +echo "Neuestes Image: ${NEWEST:-KEINES GEFUNDEN}" +echo +echo " [a] AUFSPIELEN (Standard, startet automatisch)" +echo " [i] IMAGE ERSTELLEN (diesen Rechner sichern)" +echo " [x] Abbrechen (Neustart ins normale System)" +echo +gelb "AUFSPIELEN startet in 20 Sekunden ..." +WAHL="a" +read -t 20 -n 1 -r WAHL_INPUT && WAHL="${WAHL_INPUT:-a}" +echo + +case "$WAHL" in + i|I) + NEU="schule-laptop-$(date +%Y-%m-%d)" + gelb "Erstelle Image '$NEU' von /dev/$DISK ..." + # Batch, zstd-Kompression, KEINE Nachpruefung des Images (-scs, E-004) + /usr/sbin/ocs-sr -q2 -j2 -z5p -i 4096 -sfsck -senc -scs -batch -p true savedisk "$NEU" "$DISK" + # Eigene Versionsdatei nachziehen: der gesicherte Rechner ist damit "aktuell" + # und bespielt sich nicht beim naechsten Boot selbst mit dem eigenen Image. + if mount "$ROOT_PART" /mnt/altes-system 2>/dev/null; then + echo "$NEU" > /mnt/altes-system$VERSION_FILE + umount /mnt/altes-system + fi + gruen "Image '$NEU' erstellt — Rechner faehrt herunter." + sleep 5; poweroff + ;; + x|X) + gelb "Abgebrochen — Neustart." + sleep 3; reboot + ;; + *) + if [ -z "$NEWEST" ]; then + rot "Kein Image auf der Platte — Neustart in 15 s." + sleep 15; reboot; exit 1 + fi + gelb "Spiele '$NEWEST' auf /dev/$DISK auf ..." + # Batch, KEINE Bestaetigungen, KEINE Nachpruefung (-scr), Groessencheck aus (E-004) + /usr/sbin/ocs-sr -g auto -e1 auto -e2 -r -j2 -icds -k1 -scr -batch -p true restoredisk "$NEWEST" "$DISK" + + # ---------- Frisches System personalisieren ---------- + gelb "Personalisiere frisches System (${OLD_HOSTNAME:-laptopXX}) ..." + mkdir -p /mnt/neues-system + if mount "$ROOT_PART" /mnt/neues-system; then + if [ -n "$OLD_HOSTNAME" ]; then + echo "$OLD_HOSTNAME" > /mnt/neues-system/etc/hostname + sed -i "s/127.0.1.1.*/127.0.1.1\t$OLD_HOSTNAME/" /mnt/neues-system/etc/hosts + fi + # Neue SSH-Host-Keys + Machine-ID (jeder Klon muss eindeutig sein) + rm -f /mnt/neues-system/etc/ssh/ssh_host_* + chroot /mnt/neues-system ssh-keygen -A + rm -f /mnt/neues-system/etc/machine-id /mnt/neues-system/var/lib/dbus/machine-id + chroot /mnt/neues-system dbus-uuidgen --ensure=/etc/machine-id + cp /mnt/neues-system/etc/machine-id /mnt/neues-system/var/lib/dbus/machine-id + # Versionsdatei: dieses Image ist jetzt installiert + echo "$NEWEST" > /mnt/neues-system$VERSION_FILE + # Privater Gitea-Admin-Key gehoert nur auf laptop03 (E-005) + if [ "$OLD_HOSTNAME" != "laptop03" ]; then + rm -f /mnt/neues-system/home/puettmann/.ssh/id_ed25519 \ + /mnt/neues-system/home/puettmann/.ssh/id_ed25519.pub + fi + umount /mnt/neues-system + gruen "Fertig: ${OLD_HOSTNAME:-unbenannt} mit Image '$NEWEST'." + else + rot "Konnte frisches System nicht mounten — Personalisierung uebersprungen!" + rot "Bitte von Hand: first-boot-setup.sh ausfuehren." + sleep 15 + fi + sleep 5; poweroff + ;; +esac diff --git a/files/reimage-check.service b/files/reimage-check.service new file mode 100644 index 0000000..694be8e --- /dev/null +++ b/files/reimage-check.service @@ -0,0 +1,11 @@ +[Unit] +Description=Schul-Laptops: Pruefen ob ein neues Image aufgespielt werden soll + +[Service] +Type=oneshot +# USB-Platte braucht einen Moment, bis sie erkannt ist +ExecStartPre=/bin/sleep 5 +ExecStart=/usr/local/bin/reimage-check.sh + +[Install] +WantedBy=multi-user.target diff --git a/files/reimage-check.sh b/files/reimage-check.sh new file mode 100644 index 0000000..e83053b --- /dev/null +++ b/files/reimage-check.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Prueft beim Boot, ob die Image-Platte (DEZNTRSICHERUNG) angeschlossen ist +# und ein neueres Image traegt als das installierte. Wenn ja: einmaliger +# Reboot in den GRUB-Eintrag "Schul-Neuinstallation" (Clonezilla, toram). +# +# Sicherungen gegen Fehlausloesung: +# - laptop03 (Master) bespielt sich nie selbst (E-004) +# - Versionsvergleich verhindert Reimage-Schleifen bei steckender Platte +# +set -u + +LABEL="DEZNTRSICHERUNG" +VERSION_FILE="/etc/schul-image-version" +GRUB_ENTRY="Schul-Neuinstallation" + +# Der Master ist tabu — doppelte Absicherung zusaetzlich zum deaktivierten Service +[ "$(hostname)" = "laptop03" ] && exit 0 + +DEV=$(blkid -L "$LABEL" 2>/dev/null) || exit 0 +[ -n "$DEV" ] || exit 0 + +MNT=$(mktemp -d) +mount -o ro "$DEV" "$MNT" 2>/dev/null || { rmdir "$MNT"; exit 0; } +NEWEST=$(find "$MNT" -maxdepth 1 -name 'schule-laptop-*' -type d -printf '%f\n' 2>/dev/null | sort | tail -1) +umount "$MNT" +rmdir "$MNT" + +[ -n "$NEWEST" ] || exit 0 +INSTALLED=$(cat "$VERSION_FILE" 2>/dev/null || echo "0") + +if [ "$NEWEST" \> "$INSTALLED" ]; then + echo "Neues Image '$NEWEST' gefunden (installiert: '$INSTALLED') — Neuinstallation beim naechsten Boot." + grub-reboot "$GRUB_ENTRY" && systemctl reboot +fi + +exit 0 diff --git a/local.yml b/local.yml index c68a5d8..bf1a9f5 100644 --- a/local.yml +++ b/local.yml @@ -50,6 +50,75 @@ enabled: yes state: started + # --- Jahres-Reimage: Boot-Check + GRUB-Eintrag aktuell halten --- + + - name: reimage-check-Script installieren + copy: + src: files/reimage-check.sh + dest: /usr/local/bin/reimage-check.sh + mode: "0755" + + - name: reimage-check.service installieren + copy: + src: files/reimage-check.service + dest: /etc/systemd/system/reimage-check.service + mode: "0644" + register: reimage_unit + + - name: systemd neu laden, falls reimage-Unit geaendert + systemd: + daemon_reload: yes + when: reimage_unit.changed + + - name: reimage-check aktivieren (alle ausser Master laptop03) + systemd: + name: reimage-check.service + enabled: "{{ 'no' if ansible_hostname == 'laptop03' else 'yes' }}" + + - name: GRUB auf gespeicherten Default umstellen (fuer grub-reboot) + lineinfile: + path: /etc/default/grub + regexp: '^GRUB_DEFAULT=' + line: 'GRUB_DEFAULT=saved' + register: grub_default + + - name: update-grub ausfuehren, falls GRUB-Konfig geaendert + command: update-grub + when: grub_default.changed + + - name: Normalen Boot als gespeicherten Default setzen + command: grub-set-default 0 + args: + creates: /boot/grub/grubenv + + - name: GRUB-Eintrag Schul-Neuinstallation installieren + copy: + src: files/clonezilla/grub-custom.cfg + dest: /boot/grub/custom.cfg + mode: "0644" + + - name: Verzeichnis /boot/clonezilla anlegen + file: + path: /boot/clonezilla + state: directory + mode: "0755" + + - name: Reimage-Skript nach /boot/clonezilla verteilen + copy: + src: files/clonezilla/schul-reimage.sh + dest: /boot/clonezilla/schul-reimage.sh + mode: "0755" + + - name: Pruefen ob Clonezilla-Live-Dateien vorhanden sind + stat: + path: /boot/clonezilla/filesystem.squashfs + register: clonezilla_live + + - name: Hinweis falls Clonezilla-Live-Dateien fehlen + debug: + msg: "HINWEIS: /boot/clonezilla ohne Live-Dateien (vmlinuz, initrd.img, filesystem.squashfs) — Reimage ohne Stick nicht moeglich." + when: not clonezilla_live.stat.exists + # --- Status melden --- - name: Pruefen ob ein Neustart noetig ist