#!/usr/bin/env python3 r"""Push nautobot/mikrotik_routeros.j2 to the bf-golden-config Gitea repo. Reads the Gitea token from ~/.secrets/credentials.env (GITEA_ACCESS_TOKEN). python3 routeros/push-gc-template.py --dry-run # Diff gegen Remote zeigen python3 routeros/push-gc-template.py -m "TP22: AP-Zweig" # pushen SECRET-GUARD (TP22, 2026-08-13) ------------------------------- Vor jedem PUT wird der Template-Inhalt gegen dieselben Feld-Regexe geprueft, die die Nautobot-ConfigReplace-Regeln fuers Backup-Scrubbing verwenden. Findet sich ein Wert, der NICHT die Maske `"<>"` traegt, bricht das Skript ab. Grund: genau dieser Weg hat am 2026-07-22 den WG-Hub-private-key und 6 WLAN-PSKs ins Gitea-Repo getragen (via Backup-Job, aber dieselbe Zielablage). Das Repo musste am 2026-08-13 neu angelegt werden. Der Guard kostet nichts und verhindert die Wiederholung. """ import argparse import base64 import difflib import json import pathlib import re import subprocess import sys import urllib.error import urllib.request REPO = "https://gitea.emsenhuber.net/api/v1/repos/admin/bf-golden-config" FILE = "jinja_templates/mikrotik_routeros.j2" TOKEN_PATH = pathlib.Path.home() / ".secrets/credentials.env" SRC = pathlib.Path(__file__).resolve().parent.parent / "nautobot" / "mikrotik_routeros.j2" MASK = '"<>"' # Muss mit nautobot/ingest-gc-scrubbing.py:RULES synchron bleiben. SECRET_FIELDS = ["passphrase", "private-key", "pre-shared-key", "wpa-pre-shared-key", "password", "secret"] VAL = r'("(?:[^"\\]|\\.)*"|\S+)' def get_token(): m = re.search(r"^GITEA_ACCESS_TOKEN=(.+)$", TOKEN_PATH.read_text(), re.MULTILINE) if not m: raise SystemExit("GITEA_ACCESS_TOKEN not found") return m.group(1).strip() def scan_for_secrets(text): """Gibt eine Liste von (zeilennr, feld) zurueck, deren Wert nicht maskiert ist.""" bad = [] for lineno, line in enumerate(text.splitlines(), 1): # Kommentarzeilen sind Doku (nennen die Feldnamen absichtlich), nicht Config. if line.lstrip().startswith("#"): continue for field in SECRET_FIELDS: for m in re.finditer(rf"(?