sshpass -p repo-weit weg + vier TP15-Cutover-Skripte archiviert
sshpass -p schreibt das Passwort in argv, wo es in ps und jedem argv-Trace steht -- die Klasse, an der am 2026-08-22 das Core-Passwort verbrannt ist. Nach gc-transaktion.py (voriger Commit) jetzt auch die fuenf unter routeros/: SSHPASS-Env + sshpass -e. Nachweis, gemessen statt gelesen: - alle 13 subprocess.run mit sshpass tragen env= (per AST geprueft; ohne env waere SSHPASS leer und alles still kaputt) - Signaturen passen zu den Aufrufen (AST-Arity; py_compile findet das NICHT) - echter Lauf des gepatchten Codes gegen den lebenden Core: backup-core.py importiert, IP zur Laufzeit auf 10.40.99.1 -> "name: crs326-core", rc=0 - Passwort in argv? nein. In ps waehrend der Verbindung? nein, 40 Stichproben - Negativkontrolle: derselbe Test mit einem Dummy-Wert ueber die alte -p-Form -> 36 Treffer. Die Probe ist also nicht blind, das "nein" hat Aussagekraft Mitgefixt, weil es im umgebauten Code lag: apply-core-cutover-l3.py griff in run_silent auf ein pw zu, das dort gar nicht existierte -- jeder Fehlerfall endete in einem NameError statt in der gedachten Meldung. Fuer TP30 aendert das NICHTS: der Weg ist zu, der Wert bleibt verbrannt. Posten #1 (MikroTik-admin) bleibt faellig. Im TP so hingeschrieben, damit es niemand als erledigt liest. --- Archivierung (Christoph-Entscheid) --- Beim Fixen fiel auf, dass alle vier Cutover-Skripte auf tote Adressen zeigten: apply-core-cutover-l3.py 10.40.41.211 tot (VLAN41 seit 2026-08-27 weg) backup-core.py 10.40.41.211 tot restore-core.py 192.168.88.1 tot (Werks-Temp-Pfad) backup-ap1.py 10.40.10.1 ANTWORTET -- ist aber der CORE backup-ap1.py war der gefaehrliche: er waere nicht gescheitert, er haette den Core gesichert und die Datei ap1-pre-core-cutover-*.backup genannt. Wer das spaeter zurueckspielt, spielt Core-Config auf einen AP. Entscheid war archivieren, nicht reparieren -- der Cutover ist am 2026-08-08 durch, die Skripte haben ihre Aufgabe erfuellt. Sie liegen jetzt unter routeros/archiv-tp15-cutover/ mit README nach dem Muster von switch/. Die Pfad-Nebenwirkungen des Verschiebens sind ABSICHTLICH nicht repariert (backup-core.py schriebe jetzt ins Archivverzeichnis, apply-core-cutover-l3.py faende seine .rsc nicht mehr) -- sie zu reparieren hiesse, die Skripte wieder lauffaehig zu machen, und das ist gerade nicht die Absicht. Der sshpass-Fix wurde trotzdem VOR dem Verschieben gemacht: Archiv-Code wird abgeschrieben, also soll dort das richtige Muster stehen. import-core-rsc.py bleibt, wo es ist -- als einziges generisch nuetzlich. Sein Default-Host ist ebenfalls tot (--host mitgeben), und in --host steckt ein zweiter latenter Bug: sys.argv[1] wird als RSC-Pfad gelesen, bevor auf --host geprueft wird. NICHT gefixt, eigene Runde. Zeiger gesetzt in: CLAUDE.md (sshpass-Gotcha), teilprojekte.md (TP30), teilprojekte-archiv.md (TP15), Handoff 2026-08-31. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
100
routeros/archiv-tp15-cutover/backup-core.py
Normal file
100
routeros/archiv-tp15-cutover/backup-core.py
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Erstelle ein Binary-Backup + einen Text-Export von crs326-core vor dem TP15-Cutover.
|
||||
|
||||
Creds aus ~/.claude.json (mikromcp-env, CRS326_CORE_*). Backup-Dateien landen unter routeros/.
|
||||
|
||||
🔴 PASSWORT NIE UEBER argv. `sshpass -p <PW>` 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 10.40.41.211 ist TOT (VLAN41 abgeschafft 2026-08-27); der Core ist heute
|
||||
10.40.99.1. Bewusst nicht stillschweigend geaendert, s. Handoff 2026-08-31.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
CORE_IP = "10.40.41.211" # VLAN1-Temp; alternativ 10.40.99.11 sobald 99T auf Uplink
|
||||
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, remote_file, local_file):
|
||||
return [
|
||||
"sshpass", "-e",
|
||||
"scp",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
"-o", "PubkeyAuthentication=no",
|
||||
f"{user}@{CORE_IP}:{remote_file}",
|
||||
str(local_file),
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
user, pw = load_core_creds()
|
||||
ts = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
|
||||
backup_name = f"crs326-core-pre-cutover-{ts}"
|
||||
|
||||
print(f"crs326-core Backup via SSH {user}@{CORE_IP}")
|
||||
|
||||
create_cmd = ssh_cmd(
|
||||
user,
|
||||
f'/system backup save name="{backup_name}" dont-encrypt=yes; '
|
||||
f'/export file="{backup_name}"'
|
||||
)
|
||||
print(f" Erzeuge Backup/Export auf crs326-core ...")
|
||||
subprocess.run(create_cmd, check=True, env=ssh_env(pw))
|
||||
|
||||
local_backup = OUT_DIR / f"{backup_name}.backup"
|
||||
local_export = OUT_DIR / f"{backup_name}.rsc"
|
||||
|
||||
print(f" Lade {backup_name}.backup herunter ...")
|
||||
subprocess.run(scp_cmd(user, f"{backup_name}.backup", local_backup), check=True, env=ssh_env(pw))
|
||||
|
||||
print(f" Lade {backup_name}.rsc herunter ...")
|
||||
subprocess.run(scp_cmd(user, f"{backup_name}.rsc", local_export), check=True, env=ssh_env(pw))
|
||||
|
||||
backup_size = local_backup.stat().st_size
|
||||
export_size = local_export.stat().st_size
|
||||
print(f" OK: {local_backup.name} ({backup_size} Bytes)")
|
||||
print(f" OK: {local_export.name} ({export_size} Bytes)")
|
||||
|
||||
if backup_size < 1000:
|
||||
print(" WARN: Backup-Datei auffällig klein!")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user