#!/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): # PVE_ADMIN__PASS wird ergänzt, falls noch nicht vorhanden # (Standard: C:/Users/r.rapp/it/proxmox/.env) # /tfa-.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'."