#!/usr/bin/env python3 """Stelle das crs326-core Backup vom Cutover-Stand wieder her. Creds aus ~/.claude.json (mikromcp-env, CRS326_CORE_*). Backup-Datei wird per SCP hochgeladen und /system backup load ausgefuehrt. 🔴 PASSWORT NIE UEBER argv. `sshpass -p ` schreibt es in die Kommandozeile, wo es in `ps` und jedem argv-Trace steht -- genau so ist am 2026-08-22 das Core-Passwort in ein Sitzungstranskript geraten und musste rotiert werden. `sshpass -e` liest es aus der Umgebung des Kindprozesses. Umgestellt 2026-08-31. ⚠️ CORE_IP 192.168.88.1 ist der Werks-Temp-Pfad und antwortet nicht; der Core ist heute 10.40.99.1. Der hartkodierte BACKUP-Dateiname stammt vom 2026-08-06. Bewusst nicht stillschweigend geaendert, s. Handoff 2026-08-31. """ import json import os import pathlib import subprocess import sys CORE_IP = "192.168.88.1" BACKUP = "crs326-core-pre-cutover-20260806-073712.backup" CLAUDE_JSON = pathlib.Path.home() / ".claude.json" OUT_DIR = pathlib.Path(__file__).resolve().parent def load_core_creds(): cfg = json.loads(CLAUDE_JSON.read_text()) env = cfg.get("mcpServers", {}).get("mikromcp", {}).get("env", {}) user = env.get("CRS326_CORE_USER") or env.get("ROUTER_USER") pw = env.get("CRS326_CORE_PASS") or env.get("ROUTER_PASS") if not user or not pw: raise SystemExit("CRS326_CORE_USER/PASS nicht in ~/.claude.json mikromcp.env gefunden") return user, pw # Das Passwort geht ausschliesslich ueber die Umgebung an sshpass -- nie als Argument. def ssh_env(pw): return dict(os.environ, SSHPASS=pw) def ssh_cmd(user, remote_command): return [ "sshpass", "-e", "ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "PubkeyAuthentication=no", f"{user}@{CORE_IP}", remote_command, ] def scp_cmd(user, local_file, remote_file): return [ "sshpass", "-e", "scp", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "PubkeyAuthentication=no", str(local_file), f"{user}@{CORE_IP}:{remote_file}", ] def run_silent(cmd, pw): r = subprocess.run(cmd, capture_output=True, text=True, env=ssh_env(pw)) if r.returncode != 0: out = (r.stdout + r.stderr).replace(pw, "***") raise SystemExit(f"Befehl fehlgeschlagen (rc={r.returncode}):\n{out}") return r def main(): local_backup = OUT_DIR / BACKUP if not local_backup.exists(): raise SystemExit(f"{local_backup} fehlt") user, pw = load_core_creds() print(f"crs326-core Restore via SSH {user}@{CORE_IP}") print(f" Lade {BACKUP} hoch ...") run_silent(scp_cmd(user, local_backup, BACKUP), pw) print(f" Spiele Backup zurueck ...") run_silent(ssh_cmd(user, f'/system backup load name="{BACKUP}" password=""'), pw) print(" Backup geladen, Router rebootet.") print(" Warte auf Wiederverbindung an 10.40.41.211 ...") if __name__ == "__main__": main()