pve-cluster: Phase 4 als Rolle – Admin-Gruppe/-Konten, Healer-Token, Benachrichtigungen, Firewall mit IPSET management (Tasks 4.1–4.4)
ci / test (push) Failing after 21s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Robert Rapp
2026-10-13 17:05:30 +02:00
co-authored by Claude Fable 5.1
parent 5c790cc8d4
commit 81602373fc
6 changed files with 309 additions and 0 deletions
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Persönliches Admin-Konto fertigstellen: Passwort, TOTP (Authenticator-App) und Recovery-Keys.
# Läuft auf dem Admin-Laptop (Git-Bash). Das Konto selbst legt playbooks/cluster.yml an.
#
# platform/proxmox/scripts/pve-admin-user.sh r.rapp [pve44]
#
# Ergebnis (nie ins Repo):
# <ENV> PVE_ADMIN_<NAME>_PASS wird ergänzt, falls noch nicht vorhanden
# (Standard: C:/Users/r.rapp/it/proxmox/.env)
# <ENVDIR>/tfa-<name>.txt otpauth-URI zum Eintippen/Scannen und die Recovery-Keys (0600)
# Idempotent: Passwort nur beim ersten Lauf, TOTP/Recovery nur, wenn noch keine 2FA existiert.
set -euo pipefail
user=${1:?Benutzername ohne @pve}
node=${2:-pve44}
env_file=${PVE_ENV:-C:/Users/r.rapp/it/proxmox/.env}
out_dir=$(dirname "$env_file")
userid="${user}@pve"
var="PVE_ADMIN_$(echo "$user" | tr 'a-z.-' 'A-Z__')_PASS"
tfa_file="$out_dir/tfa-${user}.txt"
run() { ssh -T -o BatchMode=yes -o ConnectTimeout=10 "$node" "$@"; }
run "pveum user list --output-format json" | grep -q "\"userid\":\"$userid\"" \
|| { echo "FEHLER: $userid gibt es nicht – erst playbooks/cluster.yml laufen lassen" >&2; exit 1; }
# 1. Passwort (einmalig erzeugen, in .env ablegen, nie ausgeben)
if ! grep -qE "^${var}=" "$env_file"; then
pw=$(openssl rand -base64 24 | tr -d '/+=' | cut -c1-24)
printf '%s=%s\n' "$var" "$pw" >> "$env_file"
printf '%s' "$pw" | run "p=\$(cat); pvesh set /access/password --userid $userid --password \"\$p\""
echo "Passwort gesetzt und als $var in $env_file abgelegt"
else
echo "Passwort: $var ist schon in $env_file, unverändert"
fi
# 2. TOTP und Recovery-Keys, nur wenn für das Konto noch keine 2FA existiert
if run "pvesh get /access/tfa/$userid --output-format json" | grep -q '"type"'; then
echo "2FA: für $userid existiert schon ein Eintrag, nichts geändert (siehe $tfa_file)"
exit 0
fi
secret=$(openssl rand 20 | base32 | tr -d '=')
uri="otpauth://totp/knaster:${userid}?secret=${secret}&issuer=knaster&digits=6&period=30&algorithm=SHA1"
# aktuellen Code auf dem Knoten berechnen (Zeit des Knotens, kein Zusatzpaket)
code=$(run "python3 -c \"
import base64,hmac,struct,time,hashlib
s='$secret'
k=base64.b32decode(s+'='*(-len(s)%8))
h=hmac.new(k,struct.pack('>Q',int(time.time())//30),hashlib.sha1).digest()
o=h[-1]&15
print('%06d'%((struct.unpack('>I',h[o:o+4])[0]&0x7fffffff)%1000000))\"")
run "pvesh create /access/tfa/$userid --type totp --description 'Authenticator-App' --totp '$uri' --value $code" >/dev/null
keys=$(run "pvesh create /access/tfa/$userid --type recovery --description 'Recovery-Keys (offline)' --output-format json" \
| python3 -c 'import json,sys; print("\n".join(json.load(sys.stdin)["recovery"]))')
umask 077
{
echo "Proxmox 2FA für $userid (angelegt $(date +%F))"
echo
echo "TOTP in die Authenticator-App eintragen (QR aus der URI erzeugen oder Secret manuell eingeben):"
echo "$uri"
echo "Secret: $secret"
echo
echo "Recovery-Keys (je einmal gültig, offline aufbewahren):"
echo "$keys"
} > "$tfa_file"
echo "TOTP und Recovery-Keys angelegt: $tfa_file (0600). Anmeldung: Weboberfläche, Realm 'Proxmox VE authentication server'."