feat: Golden-Image-Deployment als Ersatz fuer Ubiquity-Automatisierung (Phase 0+1)
Neue Richtung nach ADR-0024-Diskussion: statt Distributions-Installer (Ubiquity/Anaconda) zu automatisieren - strukturell fragil, siehe ADR-0023-Nachtrag zum partman-rebuild-cache-Loop - wird das curtin/FAI-Muster genutzt: Zieldatentraeger direkt partitionieren, ein fertiges Root-Filesystem-Image entpacken, per chroot nacharbeiten. - build_golden_image.sh: baut eine schlanke Debian-bookworm-Basis per debootstrap (Kernel, breites linux-firmware, NetworkManager, openssh-server) - KEINE Desktop-Umgebung, die kommt wie jedes andere Merkmal per Ansible-Blueprint nach dem ersten Boot (ADR-0002). Keine SSH-Hostkeys/kein Root-Passwort im Ergebnis - reale Zugangsdaten entstehen erst durch den Provisioning Agent pro Geraet. - scripts/lib/image_deploy.sh: Kernmechanik als wiederverwendbare Funktionsbibliothek (partitionieren, formatieren, mounten, Image entpacken, fstab aus echten UUIDs, chroot-Fixup fuer machine-id/SSH- Hostkeys/initramfs, Bootloader-Installation UEFI+BIOS). - scripts/image_deploy_test.sh: isolierter Testtreiber fuer Phase 1, ruft dieselben Funktionen auf, die spaeter backends/mint-image/ backend.sh (Phase 2) nutzen wird. Golden Image real gebaut und verifiziert (enterprise/LMDE 6, debootstrap bookworm): 302 MB, Kernel vorhanden, keine SSH-Hostkeys, machine-id leer. Deployment-Mechanik noch nicht live getestet - naechster Schritt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
dfb8c2fc72
commit
5845e0108a
153
scripts/build_golden_image.sh
Executable file
153
scripts/build_golden_image.sh
Executable file
@ -0,0 +1,153 @@
|
||||
#!/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 <output.tar.zst> [suite] [arch]" >&2
|
||||
echo " suite: Debian-Suite fuer debootstrap (Standard: bookworm)" >&2
|
||||
echo " arch: Zielarchitektur (Standard: amd64)" >&2
|
||||
}
|
||||
|
||||
OUTPUT_FILE="${1:-}"
|
||||
# bookworm = Debian 12, dieselbe Basis wie der Build-Host selbst (LMDE 6
|
||||
# "faye"), dadurch keine Cross-Suite-Ueberraschungen bei apt/debootstrap.
|
||||
SUITE="${2:-bookworm}"
|
||||
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}"
|
||||
|
||||
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" <<EOF
|
||||
deb ${MIRROR} ${SUITE} main contrib non-free-firmware
|
||||
deb ${MIRROR} ${SUITE}-updates main contrib non-free-firmware
|
||||
deb ${MIRROR}-security ${SUITE}-security main contrib non-free-firmware
|
||||
deb ${MIRROR} ${SUITE}-backports main contrib non-free-firmware
|
||||
EOF
|
||||
|
||||
echo "==> /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.
|
||||
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-misc-nonfree \
|
||||
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 -T0 -19 -o "${OUTPUT_FILE}"
|
||||
|
||||
echo "==> Fertig: ${OUTPUT_FILE} ($(du -h "${OUTPUT_FILE}" | cut -f1))"
|
||||
83
scripts/image_deploy_test.sh
Executable file
83
scripts/image_deploy_test.sh
Executable file
@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Isolierter Testtreiber fuer scripts/lib/image_deploy.sh (Phase 1 des
|
||||
# Golden-Image-Deployment-Plans, siehe ADR-0024) - direkt in einer
|
||||
# frischen Live-Mint-Session ausgefuehrt, OHNE Umweg ueber Kiosk/
|
||||
# Aktivierung/Kexec, um den Iterationszyklus kurz zu halten. Wird erst in
|
||||
# Phase 2 durch backends/mint-image/backend.sh ersetzt/ergaenzt, das
|
||||
# dieselben Bibliotheksfunktionen im echten Self-Service-Ablauf aufruft.
|
||||
#
|
||||
# WARNUNG: ueberschreibt den angegebenen Datentraeger vollstaendig. Nur
|
||||
# auf einer Test-VM mit einer garantiert leeren/wegwerfbaren Platte
|
||||
# ausfuehren.
|
||||
|
||||
usage() {
|
||||
echo "Usage: sudo $0 <disk> <golden-image.tar.zst> [root_fs] [hostname]" >&2
|
||||
echo " disk: z.B. /dev/vda oder /dev/sda - WIRD KOMPLETT ÜBERSCHRIEBEN" >&2
|
||||
}
|
||||
|
||||
DISK="${1:-}"
|
||||
IMAGE_PATH="${2:-}"
|
||||
ROOT_FS="${3:-ext4}"
|
||||
HOSTNAME_VALUE="${4:-tuxflotte-image-test}"
|
||||
|
||||
if [[ -z "${DISK}" || -z "${IMAGE_PATH}" ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "Fehler: muss als root laufen." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ -b "${DISK}" ]] || { echo "Fehler: ${DISK} ist kein Blockgerät." >&2; exit 1; }
|
||||
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
# shellcheck source=lib/image_deploy.sh
|
||||
source "${REPO_DIR}/scripts/lib/image_deploy.sh"
|
||||
|
||||
TARGET="/target"
|
||||
declare -a MOUNT_STACK=()
|
||||
|
||||
cleanup() {
|
||||
image_deploy_unbind_mounts MOUNT_STACK
|
||||
umount -R "${TARGET}" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ -d /sys/firmware/efi ]]; then
|
||||
IS_EFI="true"
|
||||
else
|
||||
IS_EFI="false"
|
||||
fi
|
||||
image_deploy_log "Firmware-Modus: $([ "${IS_EFI}" = true ] && echo UEFI || echo BIOS)"
|
||||
|
||||
image_deploy_log "Schritt 1/8: Partitionieren von ${DISK}"
|
||||
read -r BOOT_PART ROOT_PART <<<"$(image_deploy_partition "${DISK}" "${IS_EFI}")"
|
||||
image_deploy_log " -> BOOT_PART='${BOOT_PART}' ROOT_PART='${ROOT_PART}'"
|
||||
|
||||
image_deploy_log "Schritt 2/8: Formatieren"
|
||||
image_deploy_format "${BOOT_PART}" "${ROOT_PART}" "${ROOT_FS}"
|
||||
|
||||
image_deploy_log "Schritt 3/8: Mounten unter ${TARGET}"
|
||||
image_deploy_mount "${TARGET}" "${BOOT_PART}" "${ROOT_PART}"
|
||||
|
||||
image_deploy_log "Schritt 4/8: Golden Image entpacken (${IMAGE_PATH})"
|
||||
image_deploy_extract_image "${IMAGE_PATH}" "${TARGET}"
|
||||
|
||||
image_deploy_log "Schritt 5/8: fstab schreiben"
|
||||
image_deploy_write_fstab "${TARGET}" "${BOOT_PART}" "${ROOT_PART}" "${ROOT_FS}"
|
||||
|
||||
image_deploy_log "Schritt 6/8: bind mounts + chroot-Fixup (machine-id/SSH-Hostkeys/initramfs)"
|
||||
image_deploy_bind_mounts "${TARGET}" MOUNT_STACK
|
||||
image_deploy_chroot_fixup "${TARGET}"
|
||||
|
||||
image_deploy_log "Schritt 7/8: Bootloader installieren"
|
||||
image_deploy_install_bootloader "${TARGET}" "${DISK}" "${IS_EFI}"
|
||||
|
||||
image_deploy_log "Schritt 8/8: Hostname setzen (${HOSTNAME_VALUE})"
|
||||
image_deploy_set_hostname "${TARGET}" "${HOSTNAME_VALUE}"
|
||||
|
||||
image_deploy_log "Fertig. Zum Test: VM neu starten und von ${DISK} booten."
|
||||
243
scripts/lib/image_deploy.sh
Normal file
243
scripts/lib/image_deploy.sh
Normal file
@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env bash
|
||||
# Kernmechanik des Golden-Image-Deployments (siehe ADR-0024 und
|
||||
# platform-docs-Plan "Golden-Image-Deployment statt Ubiquity-
|
||||
# Automatisierung"). Wird von source eingebunden - nicht eigenstaendig
|
||||
# ausfuehrbar. Ersetzt den bisherigen Ansatz "nativen Distributions-
|
||||
# Installer automatisieren" (Ubiquity/Anaconda) durch das curtin/FAI-
|
||||
# Muster: Zieldatentraeger direkt partitionieren, ein fertiges
|
||||
# Root-Filesystem-Image entpacken, per chroot nacharbeiten
|
||||
# (fstab/initramfs/machine-id/SSH-Hostkeys/Bootloader).
|
||||
#
|
||||
# Bewusst als eigenstaendige Funktionsbibliothek (wie lib/initrd.sh,
|
||||
# lib/checks.sh) statt einer einzigen "mach alles"-Funktion - sowohl der
|
||||
# isolierte Phase-1-Testtreiber als auch das spaetere
|
||||
# backends/mint-image/backend.sh rufen dieselben Bausteine in derselben
|
||||
# Reihenfolge auf.
|
||||
|
||||
image_deploy_log() {
|
||||
printf '[image_deploy] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
image_deploy_fatal() {
|
||||
printf '[image_deploy] FEHLER: %s\n' "$*" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# Nvme/mmcblk-Geraete brauchen ein "p" vor der Partitionsnummer
|
||||
# (/dev/nvme0n1p1), sd/vd-Geraete nicht (/dev/sda1) - Heuristik: Geraete-
|
||||
# name endet auf eine Ziffer -> braucht "p".
|
||||
_image_deploy_part_suffix() {
|
||||
local disk="$1"
|
||||
if [[ "${disk}" =~ [0-9]$ ]]; then
|
||||
echo "p"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
image_deploy_partition_path() {
|
||||
local disk="$1"
|
||||
local num="$2"
|
||||
echo "${disk}$(_image_deploy_part_suffix "${disk}")${num}"
|
||||
}
|
||||
|
||||
# Partitioniert den Zieldatentraeger komplett neu (GPT, wie schon in
|
||||
# _tuxflotte_render_partman_recipe() begruendet - auch ohne EFI legt GPT
|
||||
# an, siehe dortiger Kommentar zu bios_grub). Gibt "BOOT_PART ROOT_PART"
|
||||
# auf stdout aus - BOOT_PART ist bei BIOS leer (die biosgrub-Partition
|
||||
# braucht kein Dateisystem/keinen Mountpunkt).
|
||||
image_deploy_partition() {
|
||||
local disk="$1"
|
||||
local is_efi="$2"
|
||||
|
||||
parted --script "${disk}" mklabel gpt ||
|
||||
{ image_deploy_fatal "mklabel gpt fehlgeschlagen auf ${disk}"; return 1; }
|
||||
|
||||
if [[ "${is_efi}" == "true" ]]; then
|
||||
parted --script "${disk}" mkpart ESP fat32 1MiB 513MiB &&
|
||||
parted --script "${disk}" set 1 esp on &&
|
||||
parted --script "${disk}" mkpart root ext4 513MiB 100% ||
|
||||
{ image_deploy_fatal "Partitionierung (UEFI) fehlgeschlagen auf ${disk}"; return 1; }
|
||||
else
|
||||
parted --script "${disk}" mkpart biosgrub 1MiB 2MiB &&
|
||||
parted --script "${disk}" set 1 bios_grub on &&
|
||||
parted --script "${disk}" mkpart root ext4 2MiB 100% ||
|
||||
{ image_deploy_fatal "Partitionierung (BIOS) fehlgeschlagen auf ${disk}"; return 1; }
|
||||
fi
|
||||
|
||||
partprobe "${disk}" 2>/dev/null || true
|
||||
udevadm settle
|
||||
|
||||
local part1 part2
|
||||
part1="$(image_deploy_partition_path "${disk}" 1)"
|
||||
part2="$(image_deploy_partition_path "${disk}" 2)"
|
||||
|
||||
[[ -b "${part1}" && -b "${part2}" ]] ||
|
||||
{ image_deploy_fatal "Partitionen nach partprobe nicht gefunden (${part1}, ${part2})"; return 1; }
|
||||
|
||||
if [[ "${is_efi}" == "true" ]]; then
|
||||
echo "${part1} ${part2}"
|
||||
else
|
||||
echo " ${part2}"
|
||||
fi
|
||||
}
|
||||
|
||||
image_deploy_format() {
|
||||
local boot_part="$1"
|
||||
local root_part="$2"
|
||||
local root_fs="$3"
|
||||
|
||||
if [[ -n "${boot_part}" ]]; then
|
||||
mkfs.vfat -F32 -n ESP "${boot_part}" ||
|
||||
{ image_deploy_fatal "mkfs.vfat auf ${boot_part} fehlgeschlagen"; return 1; }
|
||||
fi
|
||||
|
||||
case "${root_fs}" in
|
||||
ext4) mkfs.ext4 -F -L root "${root_part}" ;;
|
||||
btrfs) mkfs.btrfs -f -L root "${root_part}" ;;
|
||||
*) image_deploy_fatal "Nicht unterstuetztes Root-Dateisystem: ${root_fs}"; return 1 ;;
|
||||
esac || { image_deploy_fatal "mkfs auf ${root_part} fehlgeschlagen"; return 1; }
|
||||
}
|
||||
|
||||
image_deploy_mount() {
|
||||
local target="$1"
|
||||
local boot_part="$2"
|
||||
local root_part="$3"
|
||||
|
||||
install -d "${target}"
|
||||
mount "${root_part}" "${target}" ||
|
||||
{ image_deploy_fatal "Root-Partition ${root_part} konnte nicht auf ${target} gemountet werden"; return 1; }
|
||||
|
||||
if [[ -n "${boot_part}" ]]; then
|
||||
install -d "${target}/boot/efi"
|
||||
mount "${boot_part}" "${target}/boot/efi" ||
|
||||
{ image_deploy_fatal "ESP ${boot_part} konnte nicht gemountet werden"; return 1; }
|
||||
fi
|
||||
}
|
||||
|
||||
# Entpackt das per build_golden_image.sh gebaute Golden Image direkt in
|
||||
# den gemounteten Zielbaum. -p erhaelt Rechte/Eigentuemer, --zstd deckt
|
||||
# sowohl .tar.zst als auch (falls spaeter gewechselt) andere
|
||||
# zstd-komprimierte Varianten ab.
|
||||
image_deploy_extract_image() {
|
||||
local image_path="$1"
|
||||
local target="$2"
|
||||
|
||||
[[ -r "${image_path}" ]] ||
|
||||
{ image_deploy_fatal "Golden Image nicht lesbar: ${image_path}"; return 1; }
|
||||
|
||||
tar --zstd -xpf "${image_path}" -C "${target}" ||
|
||||
{ image_deploy_fatal "Golden Image konnte nicht nach ${target} entpackt werden"; return 1; }
|
||||
}
|
||||
|
||||
# /etc/fstab aus den tatsaechlichen Partitions-UUIDs neu erzeugen - kein
|
||||
# genfstab auf Debian/Mint verfuegbar (das ist ein Arch-Linux-Werkzeug),
|
||||
# blkid reicht fuer unseren einfachen Fall (Root + optional ESP) locker.
|
||||
image_deploy_write_fstab() {
|
||||
local target="$1"
|
||||
local boot_part="$2"
|
||||
local root_part="$3"
|
||||
local root_fs="$4"
|
||||
|
||||
local root_uuid
|
||||
root_uuid="$(blkid -s UUID -o value "${root_part}")"
|
||||
[[ -n "${root_uuid}" ]] ||
|
||||
{ image_deploy_fatal "Keine UUID fuer ${root_part} gefunden"; return 1; }
|
||||
|
||||
{
|
||||
echo "# Von image_deploy.sh generiert - siehe ADR-0024."
|
||||
echo "UUID=${root_uuid} / ${root_fs} defaults 0 1"
|
||||
if [[ -n "${boot_part}" ]]; then
|
||||
local boot_uuid
|
||||
boot_uuid="$(blkid -s UUID -o value "${boot_part}")"
|
||||
[[ -n "${boot_uuid}" ]] ||
|
||||
{ image_deploy_fatal "Keine UUID fuer ${boot_part} gefunden"; return 1; }
|
||||
echo "UUID=${boot_uuid} /boot/efi vfat umask=0077 0 1"
|
||||
fi
|
||||
} > "${target}/etc/fstab"
|
||||
}
|
||||
|
||||
# /dev, /proc, /sys, /dev/pts in den Zielbaum einbinden (fuer chroot-
|
||||
# Operationen, die Geraeteknoten/Kernel-Interfaces brauchen - initramfs,
|
||||
# grub-install) sowie eine funktionierende DNS-Aufloesung fuers
|
||||
# apt-get-basierte Bootloader-Nachinstallieren. MOUNTED_STACK wird vom
|
||||
# aufrufenden Skript vorgehalten (siehe image_deploy_unbind_mounts()).
|
||||
image_deploy_bind_mounts() {
|
||||
local target="$1"
|
||||
local -n stack_ref="$2"
|
||||
|
||||
mount --bind /dev "${target}/dev" && stack_ref+=("${target}/dev")
|
||||
mount -t proc proc "${target}/proc" && stack_ref+=("${target}/proc")
|
||||
mount -t sysfs sysfs "${target}/sys" && stack_ref+=("${target}/sys")
|
||||
mount -t devpts devpts "${target}/dev/pts" && stack_ref+=("${target}/dev/pts")
|
||||
cp /etc/resolv.conf "${target}/etc/resolv.conf"
|
||||
}
|
||||
|
||||
image_deploy_unbind_mounts() {
|
||||
local -n stack_ref="$1"
|
||||
local i mnt
|
||||
|
||||
for ((i = ${#stack_ref[@]} - 1; i >= 0; i--)); do
|
||||
mnt="${stack_ref[i]}"
|
||||
umount --recursive "${mnt}" 2>/dev/null || umount --lazy "${mnt}" 2>/dev/null || true
|
||||
done
|
||||
stack_ref=()
|
||||
}
|
||||
|
||||
# machine-id + SSH-Hostkeys werden im Golden Image bewusst NICHT
|
||||
# mitgeliefert (siehe build_golden_image.sh) - hier, pro tatsaechlich
|
||||
# ausgerolltem Geraet, frisch erzeugt. update-initramfs regeneriert das
|
||||
# initrd fuer die tatsaechliche Zielhardware (Golden Image wurde auf
|
||||
# einem anderen System gebaut).
|
||||
image_deploy_chroot_fixup() {
|
||||
local target="$1"
|
||||
|
||||
chroot "${target}" systemd-machine-id-setup ||
|
||||
{ image_deploy_fatal "machine-id konnte nicht erzeugt werden"; return 1; }
|
||||
chroot "${target}" ssh-keygen -A ||
|
||||
{ image_deploy_fatal "SSH-Hostkeys konnten nicht erzeugt werden"; return 1; }
|
||||
chroot "${target}" update-initramfs -u -k all ||
|
||||
{ image_deploy_fatal "initramfs-Regenerierung fehlgeschlagen"; return 1; }
|
||||
}
|
||||
|
||||
# Grub wird bewusst NICHT ins Golden Image eingebaut (spart Platz, das
|
||||
# Image kennt beim Bauen das Zielfirmware-Schema noch nicht) - stattdessen
|
||||
# hier zur Deployment-Zeit nachinstalliert, wo bereits Netzwerk verfuegbar
|
||||
# ist (Stufe-1-Umgebung hat sich schon fuer die Aktivierung verbunden).
|
||||
image_deploy_install_bootloader() {
|
||||
local target="$1"
|
||||
local disk="$2"
|
||||
local is_efi="$3"
|
||||
|
||||
if [[ "${is_efi}" == "true" ]]; then
|
||||
chroot "${target}" /bin/bash -c '
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y grub-efi-amd64
|
||||
' || { image_deploy_fatal "grub-efi-amd64-Installation fehlgeschlagen"; return 1; }
|
||||
chroot "${target}" grub-install --target=x86_64-efi \
|
||||
--efi-directory=/boot/efi --bootloader-id=tuxflotte --recheck ||
|
||||
{ image_deploy_fatal "grub-install (UEFI) fehlgeschlagen"; return 1; }
|
||||
else
|
||||
chroot "${target}" /bin/bash -c '
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y grub-pc
|
||||
' || { image_deploy_fatal "grub-pc-Installation fehlgeschlagen"; return 1; }
|
||||
chroot "${target}" grub-install --target=i386-pc --recheck "${disk}" ||
|
||||
{ image_deploy_fatal "grub-install (BIOS) fehlgeschlagen"; return 1; }
|
||||
fi
|
||||
|
||||
chroot "${target}" update-grub ||
|
||||
{ image_deploy_fatal "update-grub fehlgeschlagen"; return 1; }
|
||||
}
|
||||
|
||||
image_deploy_set_hostname() {
|
||||
local target="$1"
|
||||
local hostname="$2"
|
||||
|
||||
echo "${hostname}" > "${target}/etc/hostname"
|
||||
printf '127.0.1.1\t%s\n' "${hostname}" >> "${target}/etc/hosts"
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user