Asset-Register als einzige Quelle

Schema: Aus einer YAML-Datei entstehen Doku-Tabellen, Intranet-Kacheln, Zertifikatsziele, IP-Tabellen und ein SSH-Vorschlag; ein täglicher Abgleich vergleicht das Register mit Proxmox, DHCP, DNS und Monitoring

Verfasst von

in

Die Adresse meines Monitoring-Servers stand an 24 Stellen: so viele Dateien allein in der Doku nannten sie, als ich nachgezählt habe. Dazu kamen acht handgepflegte Listen, die Dienste und Hosts jeweils neu aufzählten: Intranet-Kacheln, Zertifikatsziele, Erfassungsstand, Sicherungsplan, Abhängigkeiten und weitere. Jede Änderung musste an mehreren Orten nachgezogen werden, und irgendeiner wurde immer vergessen. Heute pflege ich diese Angaben in genau einer YAML-Datei, dem Asset-Register. Daraus entstehen Doku-Tabellen, Intranet-Kacheln, Zertifikatsziele und IP-Tabellen.

Schema: Aus einer YAML-Datei entstehen Doku-Tabellen, Intranet-Kacheln, Zertifikatsziele, IP-Tabellen und ein SSH-Vorschlag; ein täglicher Abgleich vergleicht das Register mit Proxmox, DHCP, DNS und Monitoring

Die Ausgangslage

Bevor ich etwas gebaut habe, habe ich gezählt, wo welche Angabe gepflegt wird:

SchichtBeispielePflege
Live-SystemeProxmox, DHCP, DNS, Monitoring, Backup-Serversind die Wahrheit
automatisch erzeugtInventar aus Proxmox, DHCP und Monitoring, daraus 31 Seitenkeine
von Hand, strukturiert8 Listen, die Dienste oder Hosts neu aufzählendoppelt
von Hand, Fließtext48 von 71 handgeschriebenen Seiten mit festen Adressendoppelt, veraltet schnell

Die erste Zeile war nie das Problem. Was Proxmox oder der DHCP-Server weiß, lese ich ohnehin täglich per Skript aus. Das Problem waren die beiden unteren Zeilen. Sie enthalten das, was sich nicht ablesen lässt, und das stand an vielen Orten gleichzeitig.

Absicht im Register, Fakten aus dem Live-System

Die wichtigste Entscheidung war die Abgrenzung. Ins Register gehört nur, was ich will: Rolle, Soll-Adresse, Soll-Name, Dienste und Ports, Kritikalität, Backup-Pflicht, Kachel im Intranet, Zertifikatsziel, Doku-Link. Was ein System selbst weiß, lese ich dort aus und schreibe es nicht ab: ob eine VM existiert, auf welchem Knoten sie läuft, welche Leases vergeben sind, ob ein Host im Monitoring grün ist.

Zwei Dinge stehen bewusst nicht im Register. Die MAC-Adressen bleiben in den DHCP-Reservierungen, denn dort ist ihre Wahrheit. Und Geheimnisse stehen nirgends, nur Orte und Namen.

Der Aufbau der Datei

Ich habe mich für eine einzige Datei entschieden, nicht für einen Ordner mit vielen kleinen. Der Prüfer liest sie als Ganzes, und ein Diff zeigt jede Änderung an einer Stelle. Nach dem ersten Import hatte sie 116 Assets und rund 1.200 Zeilen. Vereinfacht und mit Beispieldaten sieht sie so aus:

gruppen:
  - {id: infra, name: Infrastruktur}
  - {id: haus, name: Haus}

assets:
  monitor:
    typ: vm                      # host | vm | ct | geraet
    vmid: 120                    # Verbindung zum Proxmox-Inventar
    name: monitor.example.lan
    rolle: "Monitoring und Doku"
    ip: 192.0.2.10               # Soll, wird gegen DHCP, DNS, Proxmox geprüft
    dns: [monitor, docs]
    ssh: monitor
    kritikalitaet: hoch
    backup: {pbs: true, konfig: true}
    zertifikate:
      - {id: Z01, fqdn: monitor.example.lan, handler: apache}
  steckdose-kueche: {typ: geraet, name: steckdose-kueche, ip: 192.0.2.85, art: shelly, web: true}

dienste:
  monitoring:
    asset: monitor
    name: Monitoring
    gruppe: infra
    url: https://monitor.example.lan/
    kachel: true                 # erscheint im Intranet

adressplan_bloecke:
  - {von: 80, bis: 109, zweck: Schaltaktoren}

Server und Gäste haben ausführliche Einträge. Die rund 90 IoT-Geräte stehen einzeln, aber kompakt in einer Zeile mit Name, Adresse, Art und gegebenenfalls Raum. Ich hätte sie auch als Gruppen führen können. Ich wollte sie aber einzeln, damit jedes Gerät mit Weboberfläche eine eigene Kachel im Intranet bekommt.

Den Anfang hat ein Importskript gemacht. Es hat das Register einmalig aus allen bestehenden Listen zusammengesetzt und Widersprüche aufgelistet, die ich von Hand geklärt habe. Seitdem pflege ich nur noch das Register.

Was daraus entsteht

Ein Generator läuft als erster Schritt jedes Doku-Baus. Er schreibt die Kacheln des Intranets mit Gruppen und Downloads, die Zielliste für den Zertifikatsautomaten mit Namen, Ports und Handler je Ziel, die IP-Tabelle im Netzplan und die Tabelle der Adressblöcke mit belegten und freien Adressen, den Erfassungsstand der Doku mit der Abhängigkeitsliste und einen Vorschlag für die SSH-Konfiguration. Der Vorschlag steht in einer eigenen Datei, die echte Konfiguration fasst er nicht an.

In Doku-Seiten stehen Adressen als Platzhalter wie {{ip:monitor}}, die ein Hook beim Bau aus dem Register füllt.

Vor dem Schreiben prüft der Generator das Register: Ist jeder Typ bekannt? Ist eine Adresse doppelt vergeben? Verweist jeder Dienst auf ein vorhandenes Asset und eine vorhandene Gruppe? Schlägt eine Prüfung fehl, schreibt er gar nichts. Eine halb erzeugte Zertifikatsliste ist schlimmer als eine veraltete.

Datenfluss: Das Register in Git speist den Generator, der nach einer Prüfung Intranet-Kacheln, Zertifikatsziele, Tabellen und SSH-Vorschlag erzeugt; ein täglicher Abgleich vergleicht das Register mit den Live-Systemen.

Konflikterkennung: keine Handänderung geht verloren

Jede erzeugte Datei trägt einen Kopf „ERZEUGT … nicht von Hand ändern“. Das hält niemanden ab, schon gar nicht eine zweite KI-Sitzung, die schnell etwas korrigieren will. Deshalb merkt sich der Generator nach jedem Schreiben eine Prüfsumme der Datei. Beim nächsten Lauf vergleicht er:

neu = sha256(erzeugter_text)
if datei_existiert:
    alt = sha256(datei_inhalt)
    if alt == neu:
        return "unverändert"
    if alt != gemerkt.get(pfad):        # seit dem letzten Schreiben geändert
        return "KONFLIKT"               # Datei bleibt, wie sie ist
schreibe(erzeugter_text)
gemerkt[pfad] = neu

Bei einem Konflikt überschreibt er die Datei nicht, sondern meldet ihn und lässt sie stehen. So verschwindet keine Änderung stillschweigend, und die Meldung sagt gleich, wo die Änderung hingehört: ins Register. Bei Seiten, in denen nur ein Block erzeugt ist, etwa die Tabelle im Netzplan, gilt die Prüfsumme nur für den Block zwischen zwei Markierungen. Anfangs galt sie für die ganze Seite. Damit hätte jede Korrektur im Fließtext einen Konflikt ausgelöst.

Der tägliche Abgleich mit der Wirklichkeit

Ein zweites Skript prüft täglich und nur lesend, ob Register und Live-Systeme zusammenpassen. Es unterscheidet Abweichungen (etwas ist falsch) und Hinweise (jemand sollte es ansehen). Einige Beispiele:

  • Ein Gast in Proxmox hat keinen Eintrag im Register, oder umgekehrt.
  • Die Soll-Adresse weicht von der DHCP-Reservierung, dem DNS-Eintrag oder der ARP-Tabelle ab.
  • Ein Gast soll gesichert werden, steht aber nicht im Backup-Job.
  • Ein Gerät antwortet auf Webports, ist aber nicht im Register.
  • Eine erzeugte Datei hat ihren Kopf verloren, wurde also vermutlich von Hand ersetzt.

Das Ergebnis landet auf einer Doku-Seite und als Dienst im Monitoring: OK bei null Abweichungen, Warnung ab einer, kritisch ab sechs oder wenn der letzte Abgleich älter als 54 Stunden ist.

Was das Register gefunden hat

Der erste Abgleich meldete drei Abweichungen und fünf Hinweise. Drei DNS-Namen aus der Zertifikatsplanung existierten gar nicht. Es gab mögliche Dubletten, etwa zwei Einträge für dasselbe Gerät vor und nach einem Umzug. Und ein Server fehlte im Monitoring.

Lehrreicher waren die Vergleichsproben. Bevor ich eine Liste auf den Generator umgestellt habe, musste die erzeugte Datei der handgepflegten entsprechen. Dabei kamen alte Fehler ans Licht:

  • In der Abhängigkeitsliste war ein Name an einem Komma abgeschnitten.
  • Bei der Zertifikatsliste zeigte die Probe einen Einrückungsfehler im DNS-Abschnitt. Die Probe hatte nur die berechneten Daten verglichen, nicht den geschriebenen Text. Seitdem liest sie die geschriebene Datei zurück.
  • Als die Tabelle der Adressblöcke zum ersten Mal erzeugt wurde, fehlte ein Zählerleser im Register. Er trägt seine Adresse fest im Gerät und hat keine DHCP-Reservierung, aus der der Import ihn hätte übernehmen können.
  • Zwölf Gastadressen kamen nur aus einem handgeschriebenen Netzplan, weil Proxmox sie ohne Gast-Agent nicht liefert. Heute liest der Abgleich die MAC aus der Gast-Konfiguration und sucht sie in der ARP-Tabelle. Alle 16 laufenden Gäste sind so bestätigt.

Neues Gerät per Merge-Request

Das Register liegt in Git, und Änderungen gehen per Merge-Request. Meldet jemand im Intranet ein neues Gerät, legt ein Skript daraus einen Entwurf an. Es sucht eine freie Adresse im Block der Geräteart, ergänzt den Eintrag im Register und öffnet einen Merge-Request. Die Befehle für die DHCP-Reservierung auf beiden Servern stehen in der Beschreibung. Mergen und reservieren mache ich selbst. Das Skript mergt nie.

Nach dem Merge prüft eine Pipeline drei Dinge: gültiges YAML und gültige Skripte, ob die erzeugten Listen zum Register passen, und ob versehentlich ein Schlüssel oder Kennwort im Repository gelandet ist. Den Doku-Bau selbst macht weiter der tägliche Lauf, weil auf dem Runner kein MkDocs installiert ist.

Technisch besteht das Ganze aus einer YAML-Datei, zwei Python-Skripten und einer Pipeline. Ein neuer Dienst braucht nur noch einen Eintrag, Kachel, Zertifikatsziel, Tabellenzeile und Abgleich folgen daraus. Offen ist Fleißarbeit: Viele IoT-Geräte tragen kryptische DHCP-Namen, und ein paar mögliche Dubletten muss ich noch klären.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht, deine IP-Adresse wird nicht gespeichert. Kommentare erscheinen erst nach Freigabe. Was mit deinen Angaben passiert, steht in der Datenschutzerklärung. Pflichtfelder sind mit * markiert.