Idempotent, läuft vom Admin-Laptop in Git-Bash. Hostschlüssel wird nur mit --reinstall verworfen, das Update läuft per systemd-run abgekoppelt von der SSH-Sitzung. .gitattributes hält Shell-Skripte auf LF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
4.5 KiB
Bash
101 lines
4.5 KiB
Bash
#!/usr/bin/env bash
|
|
# Bootstrap eines Proxmox-Knotens vom Admin-Laptop (Git-Bash). Idempotent.
|
|
#
|
|
# bootstrap-node.sh [--reinstall] <knoten>
|
|
#
|
|
# <knoten> SSH-Alias aus ~/.ssh/config (z. B. pve44)
|
|
# --reinstall nur nach einer Neuinstallation: alten Hostschlüssel verwerfen.
|
|
# Ohne den Schalter bleibt der bekannte Hostschlüssel Pflicht.
|
|
#
|
|
# Umgebung (optional):
|
|
# PVE_ENV .env mit <KNOTEN>_PASS (Standard: C:/Users/r.rapp/it/proxmox/.env)
|
|
# PVE_PUBKEY öffentlicher Schlüssel (Standard: C:/Users/r.rapp/it/proxmox/keys/id_ed25519_pve.pub)
|
|
#
|
|
# Ablauf: Schlüssel eintragen (nur falls nötig) → Paketquellen auf pve-no-subscription
|
|
# → apt full-upgrade + ethtool/lm-sensors (abgekoppelt per systemd-run) → Prüfungen.
|
|
# Ausgabe endet mit "BOOTSTRAP OK <hostname>" oder Exit ≠ 0. Neu gestartet wird nicht.
|
|
set -euo pipefail
|
|
|
|
reinstall=0
|
|
if [ "${1:-}" = "--reinstall" ]; then reinstall=1; shift; fi
|
|
node=${1:?Aufruf: bootstrap-node.sh [--reinstall] <knoten>}
|
|
env_file=${PVE_ENV:-C:/Users/r.rapp/it/proxmox/.env}
|
|
pubkey=${PVE_PUBKEY:-C:/Users/r.rapp/it/proxmox/keys/id_ed25519_pve.pub}
|
|
ip=$(ssh -G "$node" | awk '$1 == "hostname" {print $2}')
|
|
|
|
ssh_key() { ssh -T -o BatchMode=yes -o ConnectTimeout=10 "$node" "$@"; }
|
|
|
|
# 1. Hostschlüssel nur nach einer Neuinstallation verwerfen
|
|
if [ "$reinstall" = 1 ]; then
|
|
ssh-keygen -R "$ip" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
# 2. Laptop-Schlüssel per Passwort aus .env eintragen, falls der Schlüssel-Login noch nicht geht
|
|
if ! ssh_key true 2>/dev/null; then
|
|
var="$(printf '%s' "$node" | tr '[:lower:]' '[:upper:]')_PASS"
|
|
pw=$(grep -E "^${var}=" "$env_file" | head -n1 | cut -d= -f2- | tr -d '\r')
|
|
[ -n "$pw" ] || { echo "FEHLER: ${var} fehlt in ${env_file}" >&2; exit 1; }
|
|
askpass=$(mktemp)
|
|
trap 'rm -f "$askpass"' EXIT
|
|
printf '#!/bin/sh\nprintf "%%s\\n" "$PVE_ASKPASS_PW"\n' > "$askpass"
|
|
chmod +x "$askpass"
|
|
PVE_ASKPASS_PW=$pw SSH_ASKPASS=$askpass SSH_ASKPASS_REQUIRE=force DISPLAY=:0 \
|
|
ssh -o StrictHostKeyChecking=accept-new -o PubkeyAuthentication=no \
|
|
-o NumberOfPasswordPrompts=1 -o ConnectTimeout=10 "root@$ip" \
|
|
'umask 077; mkdir -p ~/.ssh; read -r k; grep -qxF "$k" ~/.ssh/authorized_keys 2>/dev/null || echo "$k" >> ~/.ssh/authorized_keys' \
|
|
< "$pubkey"
|
|
unset pw
|
|
ssh_key true || { echo "FEHLER: Schlüssel-Login auf $node geht nicht" >&2; exit 1; }
|
|
fi
|
|
|
|
# 3. Paketquellen umstellen und das Update abgekoppelt starten (übersteht Neustarts von sshd/pveproxy)
|
|
ssh_key 'bash -s' <<'REMOTE'
|
|
set -euo pipefail
|
|
d=/etc/apt/sources.list.d
|
|
if ! ls -d /root/apt-backup-* >/dev/null 2>&1; then
|
|
b=/root/apt-backup-$(date +%F); mkdir -p "$b"; cp -a "$d/." "$b/"
|
|
fi
|
|
for f in pve-enterprise.sources ceph.sources; do
|
|
if [ -f "$d/$f" ] && ! grep -q '^Enabled:' "$d/$f"; then sed -i '/^Types:/a Enabled: no' "$d/$f"; fi
|
|
done
|
|
test -f /usr/share/keyrings/proxmox-archive-keyring.gpg
|
|
[ -f "$d/proxmox.sources" ] || cat > "$d/proxmox.sources" <<'SRC'
|
|
Types: deb
|
|
URIs: http://download.proxmox.com/debian/pve
|
|
Suites: trixie
|
|
Components: pve-no-subscription
|
|
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
|
SRC
|
|
rm -f /root/bootstrap-upgrade.rc
|
|
systemd-run --unit="bootstrap-upgrade-$(date +%s)" --collect --quiet \
|
|
--setenv=DEBIAN_FRONTEND=noninteractive \
|
|
bash -c 'o="-o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold"
|
|
{ apt-get update && apt-get -y $o full-upgrade && apt-get -y $o install ethtool lm-sensors; } \
|
|
> /root/bootstrap-upgrade.log 2>&1
|
|
echo $? > /root/bootstrap-upgrade.rc'
|
|
REMOTE
|
|
|
|
echo "Update läuft auf $node (Protokoll dort: /root/bootstrap-upgrade.log) ..."
|
|
rc=""
|
|
for _ in $(seq 1 240); do # höchstens 60 Minuten
|
|
rc=$(ssh_key 'cat /root/bootstrap-upgrade.rc 2>/dev/null' 2>/dev/null || true)
|
|
[ -n "$rc" ] && break
|
|
sleep 15
|
|
done
|
|
if [ "$rc" != 0 ]; then
|
|
echo "FEHLER: Update auf $node (rc=${rc:-Zeitüberschreitung})" >&2
|
|
ssh_key 'tail -n 20 /root/bootstrap-upgrade.log' >&2 || true
|
|
exit 1
|
|
fi
|
|
|
|
# 4. Prüfungen
|
|
ssh_key 'bash -s' <<'REMOTE'
|
|
set -euo pipefail
|
|
[ "$(timedatectl show -p NTPSynchronized --value)" = yes ] || { echo "FEHLER: Zeit nicht synchronisiert" >&2; exit 1; }
|
|
command -v ethtool >/dev/null && command -v sensors >/dev/null
|
|
newest=$(ls /lib/modules | sort -V | tail -n1)
|
|
if [ "$newest" = "$(uname -r)" ]; then restart=nein; else restart="ja ($(uname -r) -> $newest)"; fi
|
|
echo "$(pveversion | cut -d/ -f2) | offene Updates: $(apt-get -s full-upgrade | grep -c '^Inst' || true) | Neustart nötig: $restart"
|
|
echo "BOOTSTRAP OK $(hostname)"
|
|
REMOTE
|