diff --git a/deploy/bootstrap/clupilot-bootstrap.sh b/deploy/bootstrap/clupilot-bootstrap.sh index a59bee6..f54ee65 100755 --- a/deploy/bootstrap/clupilot-bootstrap.sh +++ b/deploy/bootstrap/clupilot-bootstrap.sh @@ -547,6 +547,83 @@ section_rebooted() { die 'Neustart wurde angestoßen, aber die Maschine läuft nach zwei Minuten noch. Über die Anbieterkonsole nachsehen.' } +# --------------------------------------------------------------------------- +# Abschnitt 4: proxmox_installed — abnehmen, nicht installieren +# --------------------------------------------------------------------------- +# +# PVE kam mit dem Abbild. Dieser Abschnitt stellt fest, dass es wirklich läuft, +# dass Abbild und Unterbau zusammenpassen, und bringt die Paketquellen in +# Ordnung — ohne die kommt kein einziger der folgenden Abschnitte an seine +# Pakete. +section_proxmox_installed() { + if ! command -v pveversion >/dev/null 2>&1; then + CLUPILOT_SECTION_NOTE='pveversion gibt es nicht — auf dieser Maschine läuft kein Proxmox' + return 1 + fi + + # Der Kernel ist die Aussage, nicht das Paket. Ein installiertes + # proxmox-ve bei laufendem Debian-Kernel ist genau der halbe Zustand, aus + # dem RebootIntoPveKernel entstanden ist. + _kernel="$(uname -r)" + case "$_kernel" in + *-pve*) ;; + *) + CLUPILOT_SECTION_NOTE="läuft auf ${_kernel}, das ist kein PVE-Kernel" + return 1 + ;; + esac + + _id="$(os_release_field id)" + _codename="$(os_release_field codename)" + if [ "$_id" != 'debian' ] || ! _release="$(proxmox_release_for "$_codename")"; then + CLUPILOT_SECTION_NOTE="unbekannter Unterbau: ID=${_id:-(leer)}, VERSION_CODENAME=${_codename:-(leer)} ($(os_release_field pretty)). Dieses Skript kennt keine dazu passende Proxmox-Suite und rät nicht." + return 1 + fi + + # Abbild und Unterbau müssen zusammengehören. Passen sie nicht, wurde das + # falsche Abbild installiert — und das heilt keine Paketquelle, das ist ein + # PVE gegen eine andere libc. + _pve_version="$(pveversion 2>/dev/null | head -1)" + _pve_major="$(printf '%s' "$_pve_version" | sed -n 's#^pve-manager/\([0-9]*\)\..*#\1#p')" + _expected_major="$(pve_major_for_codename "$_codename")" + + _pairing='ungeprüft' + if [ -z "$_pve_major" ]; then + # Nicht stillschweigend durchwinken. Eine Prüfung, die nicht gelaufen + # ist, muss das sagen — sonst hält sie den Nächsten vom Nachsehen ab, + # und genau das hält R19 als eigenen Fund fest. + _pairing="Paarung UNGEPRÜFT, pveversion nicht lesbar: ${_pve_version:-(keine Ausgabe)}" + log "$_pairing" + elif [ "$_pve_major" != "$_expected_major" ]; then + CLUPILOT_SECTION_NOTE="${_pve_version} auf Debian ${_codename}: erwartet wäre PVE ${_expected_major}. Das Abbild passt nicht zum Unterbau; die Maschine wird mit dem richtigen Abbild neu aufgesetzt." + return 1 + else + _pairing="PVE ${_pve_major} zu Debian ${_codename}" + fi + + split_proxmox_release "$_release" + + if ! _keyring_path="$(install_proxmox_key "$CLUPILOT_PVE_KEY_URL" "$CLUPILOT_PVE_KEYRING")"; then + CLUPILOT_SECTION_NOTE="Proxmox-Release-Schlüssel nicht ladbar: ${CLUPILOT_PVE_KEY_URL}" + return 1 + fi + + remove_subscription_sources + write_proxmox_sources "$CLUPILOT_PVE_SUITE" "$_keyring_path" '/etc/apt/sources.list.d/pve-install-repo.sources' + + # Der eigentliche Beweis. Solange irgendeine Abonnement-Quelle stehen bleibt, + # endet das hier mit Fehler — und dann ist sie überschrieben worden statt + # entfernt. + export DEBIAN_FRONTEND=noninteractive + if ! apt-get update >> "${CLUPILOT_WORK_DIR}/apt.log" 2>&1; then + CLUPILOT_SECTION_NOTE="apt-get update schlägt fehl; siehe ${CLUPILOT_WORK_DIR}/apt.log" + return 1 + fi + + CLUPILOT_SECTION_NOTE="${_pve_version} auf ${_kernel}; ${_pairing}; Quellen auf ${CLUPILOT_PVE_SUITE}/pve-no-subscription" + return 0 +} + main() { parse_arguments "$@" @@ -582,14 +659,17 @@ main() { die 'Abbruch beim Neustart.' fi - # run_section proxmox_installed section_proxmox_installed # Task 4 + if ! run_section proxmox_installed section_proxmox_installed; then + die 'Abbruch: Proxmox ist nicht in dem Zustand, den die folgenden Abschnitte voraussetzen.' + fi + # run_section network_bridged section_network_bridged # Task 5 # run_section wireguard_joined section_wireguard_joined # Task 6 # run_section traefik_running section_traefik_running # Task 7 # run_section template_built section_template_built # Task 8 # run_section registered section_registered # Task 9 - log 'Proxmox läuft. Die Abschnitte ab Task 4 sind noch nicht eingehängt.' + log 'Proxmox abgenommen, Quellen in Ordnung. Die Abschnitte ab Task 5 sind noch nicht eingehängt.' } main "$@" diff --git a/deploy/bootstrap/lib/proxmox.sh b/deploy/bootstrap/lib/proxmox.sh index 6bdef7a..05ab203 100644 --- a/deploy/bootstrap/lib/proxmox.sh +++ b/deploy/bootstrap/lib/proxmox.sh @@ -89,6 +89,31 @@ Signed-By: ${_keyring_path} EOF } +# Die Abonnement-Quellen, die ein Proxmox-Abbild mitbringt. +# +# Sie scheitern ohne Abonnement bei JEDEM `apt-get update` — und danach schlägt +# jede Paketinstallation der folgenden Abschnitte fehl. Das ist keine +# Schönheitskorrektur, sondern die Voraussetzung dafür, dass Traefik, WireGuard +# und die Vorlage überhaupt installiert werden können. +# +# Beide Schreibweisen, weil PVE 8 die einzeiligen `.list` benutzte und PVE 9 auf +# deb822 umgestellt hat. Und `ceph`, weil dieselbe Falle dort ein zweites Mal +# steht. +remove_subscription_sources() { + for _f in \ + /etc/apt/sources.list.d/pve-enterprise.list \ + /etc/apt/sources.list.d/pve-enterprise.sources \ + /etc/apt/sources.list.d/ceph.list \ + /etc/apt/sources.list.d/ceph.sources \ + /etc/apt/sources.list.d/pve-install-repo.list + do + if [ -f "$_f" ]; then + rm -f "$_f" + log "entfernt: ${_f}" + fi + done +} + # --------------------------------------------------------------------------- # Was diese Maschine gerade ist: Bootmodus, Netz, Platten # --------------------------------------------------------------------------- @@ -180,6 +205,47 @@ zfs_raid_for() { # des Ziels: das Werkzeug läuft hier, nicht dort. Unbekannter Codename bricht ab # — nicht „trixie ist das neueste, also trixie". Das nächste Debian ist auch # unbekannt, und dann sieht niemand hin. +# Welche PVE-Hauptversion zu welchem Debian gehört. +# +# Die andere Hälfte der Tabelle: `proxmox_release_for` sagt, welche Suite zu +# einem Debian passt; das hier sagt, welche PVE-Version dabei herauskommen muss. +# Gebraucht wird es, um ein ABBILD abzunehmen — bei dem niemand die Suite +# gewählt hat, sondern Proxmox das Paar schon zusammengestellt hatte. Passt es +# nicht, ist das Abbild falsch, und kein Nachbessern der Quellen heilt das. +pve_major_for_codename() { + case "$1" in + bookworm) printf '8' ;; + trixie) printf '9' ;; + *) return 1 ;; + esac +} + +# Legt den Proxmox-Release-Schlüssel ab und gibt seinen Pfad aus. +# +# Nach /etc/apt/keyrings/ und über `Signed-By` benannt, NICHT nach +# /etc/apt/trusted.gpg.d/ — dort bürgte er für jede Quelle der Maschine. +install_proxmox_key() { + _key_url="$1" + _keyring="$2" + _keyring_path="/etc/apt/keyrings/${_keyring}" + + mkdir -p /etc/apt/keyrings + if ! http_download "$_key_url" "$_keyring_path"; then + return 1 + fi + chmod 644 "$_keyring_path" + + printf '%s' "$_keyring_path" +} + +# Zerlegt "suite|key_url|keyring" in CLUPILOT_PVE_SUITE, _KEY_URL, _KEYRING. +split_proxmox_release() { + CLUPILOT_PVE_SUITE="${1%%|*}" + _rest="${1#*|}" + CLUPILOT_PVE_KEY_URL="${_rest%%|*}" + CLUPILOT_PVE_KEYRING="${_rest##*|}" +} + ensure_autoinstall_assistant() { if command -v proxmox-auto-install-assistant >/dev/null 2>&1; then log 'proxmox-auto-install-assistant ist vorhanden' @@ -194,20 +260,14 @@ ensure_autoinstall_assistant() { return 1 fi - _suite="${_release%%|*}" - _rest="${_release#*|}" - _key_url="${_rest%%|*}" - _keyring="${_rest##*|}" - _keyring_path="/etc/apt/keyrings/${_keyring}" + split_proxmox_release "$_release" - mkdir -p /etc/apt/keyrings - if ! http_download "$_key_url" "$_keyring_path"; then - log "Proxmox-Release-Schlüssel für ${_codename} nicht ladbar: ${_key_url}" + if ! _keyring_path="$(install_proxmox_key "$CLUPILOT_PVE_KEY_URL" "$CLUPILOT_PVE_KEYRING")"; then + log "Proxmox-Release-Schlüssel für ${_codename} nicht ladbar: ${CLUPILOT_PVE_KEY_URL}" return 1 fi - chmod 644 "$_keyring_path" - write_proxmox_sources "$_suite" "$_keyring_path" '/etc/apt/sources.list.d/pve-install-repo.sources' + write_proxmox_sources "$CLUPILOT_PVE_SUITE" "$_keyring_path" '/etc/apt/sources.list.d/pve-install-repo.sources' export DEBIAN_FRONTEND=noninteractive apt-get update >/dev/null 2>&1 || return 1 diff --git a/docs/superpowers/plans/2026-07-30-host-uebernahme-bootstrap-skript.md b/docs/superpowers/plans/2026-07-30-host-uebernahme-bootstrap-skript.md index 6136080..c1d7f98 100644 --- a/docs/superpowers/plans/2026-07-30-host-uebernahme-bootstrap-skript.md +++ b/docs/superpowers/plans/2026-07-30-host-uebernahme-bootstrap-skript.md @@ -253,7 +253,7 @@ bewiesen, bevor neu gestartet wird, nicht danach gehofft. ### Task 4: `proxmox_installed` -- [ ] **Step 1: Schreiben** +- [x] **Step 1: Schreiben** > **Korrigiert mit Task 3.** PVE kommt jetzt schon mit dem Abbild. Dieser > Abschnitt **installiert** es also nicht mehr, er **nimmt es ab und bringt @@ -285,7 +285,7 @@ werden: läuft (`uname -r` enthält `-pve`), und **`apt-get update` läuft ohne Fehler durch** — das ist der Beweis, dass die Enterprise-Quelle wirklich weg ist und nicht nur überschrieben wurde. -- [ ] **Step 3: Committen.** `Point Proxmox at the sources this Debian actually has` +- [x] **Step 3: Committen.** `Point Proxmox at the sources this Debian actually has` (war `Install Proxmox …`, bevor das Abbild PVE schon mitbrachte) ---