From 02bed956cffc6543322f85d915b779c3418c6889 Mon Sep 17 00:00:00 2001 From: Sebastian Serfling Date: Wed, 29 Apr 2026 21:51:46 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20SSH-Key-Auth=20als=20prim=C3=A4re=20Met?= =?UTF-8?q?hode,=20Bitwarden=20als=20Fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neuer Step I (ssh_key_versuch.py): liest SSH-Keys aus DB, testet Verbindung per paramiko; erfolgreiche Server in server_creds, fehlgeschlagene in needs_bitwarden - Step G (Bitwarden) ist jetzt No-Op wenn alle Server per Key OK - paramiko.DSSKey in allen Dateien entfernt (nicht in paramiko 4.0) - failure_module (flow_fehler_handler.py): sendet bei jedem Flow-Fehler eine Nextcloud-Talk-Nachricht und bereinigt DB/Session - Bitwarden-Step überspringt fehlgeschlagene Server statt abzubrechen Co-Authored-By: Claude Sonnet 4.6 --- .../ersten_restore_pro_server_starten.py | 2 +- ...tastores_auf_allen_servern_registrieren.py | 2 +- ..._fuer_alle_restore-server_aus_bitwarden.py | 25 ++++++++++++++++--- .../ssh_key_versuch.py | 23 ++++++++++------- ...en_restore_auf_demselben_server_starten.py | 2 +- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/f/Backup/backup_restore_orchestrator__flow/ersten_restore_pro_server_starten.py b/f/Backup/backup_restore_orchestrator__flow/ersten_restore_pro_server_starten.py index 6ad32de..e8687a5 100644 --- a/f/Backup/backup_restore_orchestrator__flow/ersten_restore_pro_server_starten.py +++ b/f/Backup/backup_restore_orchestrator__flow/ersten_restore_pro_server_starten.py @@ -2,7 +2,7 @@ import wmill, json, paramiko, io, mysql.connector def _load_pkey(key_str: str): - for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey, paramiko.DSSKey]: + for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey]: try: return cls.from_private_key(io.StringIO(key_str)) except Exception: diff --git a/f/Backup/backup_restore_orchestrator__flow/script_deployen_&_pbs-datastores_auf_allen_servern_registrieren.py b/f/Backup/backup_restore_orchestrator__flow/script_deployen_&_pbs-datastores_auf_allen_servern_registrieren.py index 5a3442b..59b5151 100644 --- a/f/Backup/backup_restore_orchestrator__flow/script_deployen_&_pbs-datastores_auf_allen_servern_registrieren.py +++ b/f/Backup/backup_restore_orchestrator__flow/script_deployen_&_pbs-datastores_auf_allen_servern_registrieren.py @@ -1,7 +1,7 @@ import wmill, json, paramiko, io, mysql.connector, re def _load_pkey(key_str: str): - for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey, paramiko.DSSKey]: + for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey]: try: return cls.from_private_key(io.StringIO(key_str)) except Exception: diff --git a/f/Backup/backup_restore_orchestrator__flow/ssh-credentials_fuer_alle_restore-server_aus_bitwarden.py b/f/Backup/backup_restore_orchestrator__flow/ssh-credentials_fuer_alle_restore-server_aus_bitwarden.py index c864f49..a718f7b 100644 --- a/f/Backup/backup_restore_orchestrator__flow/ssh-credentials_fuer_alle_restore-server_aus_bitwarden.py +++ b/f/Backup/backup_restore_orchestrator__flow/ssh-credentials_fuer_alle_restore-server_aus_bitwarden.py @@ -75,14 +75,31 @@ def main( raise Exception("Vault konnte nicht entsperrt werden") env["BW_SESSION"] = bw_session - server_creds = prev.get("server_creds", {}) + server_creds = prev.get("server_creds", {}) + failed_servers = [] + for server in servers: hostname = server["hostname"] print(f"Hole Creds fuer: {hostname}") - creds = bw_lookup(hostname, env, run) - server_creds[hostname] = creds - print(f" -> OK: {creds['username']}@{hostname}") + try: + creds = bw_lookup(hostname, env, run) + server_creds[hostname] = creds + print(f" -> OK: {creds['username']}@{hostname}") + except Exception as e: + print(f" -> WARNUNG: {e} – Server wird übersprungen") + failed_servers.append(hostname) run(["bw", "logout"], check=False) + if failed_servers: + print(f"\nWARNUNG: Keine Credentials für: {failed_servers}") + # Server aus target_servers entfernen damit C/D sie nicht anfassen + remaining = [s for s in prev.get("target_servers", []) + if s["hostname"] not in failed_servers] + if not remaining: + raise Exception( + f"Keine Restore-Server verfügbar – Bitwarden-Lookup fehlgeschlagen für: {failed_servers}" + ) + return {**prev, "server_creds": server_creds, "target_servers": remaining} + return {**prev, "server_creds": server_creds} diff --git a/f/Backup/backup_restore_orchestrator__flow/ssh_key_versuch.py b/f/Backup/backup_restore_orchestrator__flow/ssh_key_versuch.py index 88bfff4..b5c01b3 100644 --- a/f/Backup/backup_restore_orchestrator__flow/ssh_key_versuch.py +++ b/f/Backup/backup_restore_orchestrator__flow/ssh_key_versuch.py @@ -1,7 +1,7 @@ import wmill, mysql.connector, json, paramiko, io def _load_pkey(key_str: str): - for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey, paramiko.DSSKey]: + for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey]: try: return cls.from_private_key(io.StringIO(key_str)) except Exception: @@ -19,14 +19,19 @@ def main(prev: dict): hostnames = [s["hostname"] for s in servers] placeholders = ",".join(["%s"] * len(hostnames)) - cur.execute(f""" - SELECT hostname, ip, ssh_private_key, ssh_key_user - FROM Kunden.`bronze.restore.server` - WHERE hostname IN ({placeholders}) - AND ssh_private_key IS NOT NULL - AND ssh_private_key != '' - """, hostnames) - key_rows = {row["hostname"]: row for row in cur.fetchall()} + try: + cur.execute(f""" + SELECT hostname, ip, ssh_private_key, ssh_key_user + FROM Kunden.`bronze.restore.server` + WHERE hostname IN ({placeholders}) + AND ssh_private_key IS NOT NULL + AND ssh_private_key != '' + """, hostnames) + key_rows = {row["hostname"]: row for row in cur.fetchall()} + except Exception as e: + print(f"WARNUNG: SSH-Key-Spalten nicht in DB vorhanden ({e})") + print("Alle Server werden über Bitwarden authentifiziert.") + key_rows = {} cur.close(); conn.close() server_creds = {} diff --git a/f/Backup/backup_restore_orchestrator__flow/webhook_verarbeiten_&_naechsten_restore_auf_demselben_server_starten.py b/f/Backup/backup_restore_orchestrator__flow/webhook_verarbeiten_&_naechsten_restore_auf_demselben_server_starten.py index fdafbca..2d5b341 100644 --- a/f/Backup/backup_restore_orchestrator__flow/webhook_verarbeiten_&_naechsten_restore_auf_demselben_server_starten.py +++ b/f/Backup/backup_restore_orchestrator__flow/webhook_verarbeiten_&_naechsten_restore_auf_demselben_server_starten.py @@ -1,7 +1,7 @@ import wmill, json, mysql.connector, paramiko, io, re, base64 def _load_pkey(key_str: str): - for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey, paramiko.DSSKey]: + for cls in [paramiko.RSAKey, paramiko.Ed25519Key, paramiko.ECDSAKey]: try: return cls.from_private_key(io.StringIO(key_str)) except Exception: