ci / test (push) Successful in 25s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
66 lines
3.1 KiB
Bash
66 lines
3.1 KiB
Bash
#!/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" \
|
||
| python -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'."
|