Files
DDI/docs/OPERATIONS.md
2026-10-17 06:05:29 +02:00

251 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Betrieb
Dieses Dokument beschreibt den laufenden Betrieb der DDI-Suite: Dienste, Logs, Überwachung, Sicherung, Wiederherstellung, Upgrade, Schlüssel- und Zertifikatspflege, Cluster und Fehlersuche. Installation: [INSTALL.md](INSTALL.md). Voraussetzungen, Ports und Größen: [SYSTEM-REQUIREMENTS.md](SYSTEM-REQUIREMENTS.md). Sicherheitsmodell: [SECURITY.md](SECURITY.md).
Namen und Pfade sind die Standardwerte der zentralen Benennung ([ADR 0018](adr/0018-zentrale-benennung.md)); wer umbenennt, ersetzt `ddi-suite`, `ddi-server`, `ddi-agent-dns` und `ddi-agent-dhcp` entsprechend.
## 1. Dienste und Dateien
| Rolle | systemd-Unit | Benutzer | Konfiguration | Zustand |
| --- | --- | --- | --- | --- |
| Management-Server | `ddi-server.service` | `ddi-suite` | `/etc/ddi-suite/server.yaml`, `/etc/ddi-suite/master.key` | PostgreSQL-Datenbank `ddi-suite`, `/var/lib/ddi-suite/server` |
| DNS-Agent | `ddi-agent-dns.service` | BIND-Benutzer (`bind` bzw. `named`) | `/etc/ddi-suite/agent-dns.yaml` | `/var/lib/ddi-suite/agent-dns` (Identität, Revisionen, Zustand) |
| DHCP-Agent | `ddi-agent-dhcp.service` | Kea-Benutzer (`_kea` bzw. `kea`) | `/etc/ddi-suite/agent-dhcp.yaml` | `/var/lib/ddi-suite/agent-dhcp` |
| BIND | `named.service` bzw. `bind9.service` | Distribution | `named.conf` mit Includes aus `/etc/bind/ddi-suite` bzw. `/etc/named/ddi-suite` | Zonen in `/var/lib/bind/ddi-suite` (inkl. `secondary/`) |
| Kea DHCPv4 | `isc-kea-dhcp4-server.service` (ISC-Pakete auf Debian/Ubuntu) bzw. `kea-dhcp4.service` (ISC-Pakete auf EL) | Kea-Paket | `/etc/kea/kea-dhcp4.conf` (vom Agent geschrieben; das Original liegt als `kea-dhcp4.conf.ddi-suite-orig` daneben) | Leases in `/var/lib/kea/kea-leases4.csv` |
Die Agents schreiben ausschließlich die Dateien, die der Server erzeugt (Include-Dateien, Zonendateien, `kea-dhcp4.conf`, Schlüssel- und HA-Dateien). `named.conf` selbst gehört dem Administrator; der Installer fügt dort nur markierte Blöcke ein (`// BEGIN ddi-suite …` bis `// END ddi-suite …`: `ddi-suite-keys.conf` vor `ddi-suite.conf` auf oberster Ebene, `ddi-suite-options.conf` innerhalb von `options`). Optionen der Distribution, die der Generator selbst setzt (`recursion`, `allow-recursion`, `forwarders`, `allow-transfer`, auf localhost beschränkte `listen-on`/`allow-query`), kommentiert er mit dem Präfix `//ddi-suite-disabled: ` aus; `--uninstall` stellt sie wieder her. Innerhalb der Blöcke nichts von Hand ändern.
Weitere Dateien des Installers:
- Benutzer der Agents per Drop-in `/etc/systemd/system/ddi-agent-<rolle>.service.d/10-user.conf`; Kea erhält das Drop-in `…/<kea-unit>.service.d/10-ddi-suite.conf` mit `KEA_CONTROL_SOCKET_DIR=/run/kea` (die ISC-Pakete sind mit `/var/run/kea` gebaut und vergleichen Pfade wörtlich; die Variable hält den Standardpfad der Generator-Einstellungen gültig). Der DHCP-Agent bekommt dieselbe Variable.
- Installer-Log `/var/log/ddi-suite/install.log`, Zertifikat der Weboberfläche `/etc/ddi-suite/tls/server.{crt,key}` (selbstsigniert, falls keines angegeben wurde; eigenes ablegen und `systemctl restart ddi-server`).
- EL9: Kea aus dem ISC-Repository braucht `log4cplus` aus **EPEL**; EPEL für Kea-Updates aktiviert lassen.
Übliche Befehle:
```sh
systemctl status ddi-server ddi-agent-dns ddi-agent-dhcp
systemctl restart ddi-server # führt beim Start offene Migrationen aus
sudo -u bind ddi-agent-dns status --config /etc/ddi-suite/agent-dns.yaml # Identität, Revisionen, letztes Ergebnis
ddi-server version
```
`ddi-agent-<rolle> status` zeigt Agent-ID, Server, Ablauf des Zertifikats (und ab wann es erneuert wird), CA-Fingerprint, angewendete und letzte funktionierende Revision, die lokal gehaltenen Revisionen (die letzten 10) und eine zuletzt abgelehnte Revision.
## 2. Logs
- Alle Dienste schreiben nach **journald**: `journalctl -u ddi-server -f`, `journalctl -u ddi-agent-dns --since today`.
- Server und Agents loggen standardmäßig **JSON** (eine Zeile je Ereignis), Stufe `info`. Umstellen in der YAML-Datei (`log: {level: debug, format: text}`) oder beim Server per `DDI_LOG_LEVEL`/`DDI_LOG_FORMAT`.
- **Korrelations-ID:** Jede HTTP-Anfrage erhält eine `request_id` (aus einem gültigen `X-Request-ID`-Header übernommen oder neu erzeugt), die in jeder Logzeile dieser Anfrage, im Antwort-Header `X-Request-ID` und im Audit-Eintrag (`request_id`) steht. Fehlermeldungen der Oberfläche nennen sie; damit findet man die Logzeilen: `journalctl -u ddi-server -o cat | grep '"request_id":"<id>"'`.
- Deployments erscheinen im Server-Log und im Agent-Log mit Revisionsnummer, Agent-ID und Deployment-ID.
- Geheimnisse (Passwörter, Tokens, Schlüssel, Lizenzschlüssel) werden nie geloggt, auch nicht im Installer-Log `/var/log/ddi-suite/install.log`.
- BIND und Kea loggen wie von der Distribution eingerichtet (journald bzw. Syslog).
## 3. Überwachung
| Endpunkt | Zweck |
| --- | --- |
| `GET /healthz` | Lebt der Prozess? Immer `{"status":"ok"}`, ohne Abhängigkeiten. Für Neustart-Logik. |
| `GET /readyz` | Kann der Server arbeiten? Prüft die Datenbank (`{"status":"ok","checks":{"database":"ok"}}`, sonst HTTP 503). Für Load-Balancer und Monitoring. |
| `GET /metrics` | Prometheus-Metriken, u. a. `ddi_http_requests_total`, `ddi_http_request_duration_seconds`, `ddi_agents_connected{role}`, `ddi_license_writable`, `ddi_build_info`, dazu Go- und Prozessmetriken. Ohne Anmeldung – per Firewall oder Reverse-Proxy auf das Monitoring beschränken. |
Empfohlene Alarme:
- `/readyz` nicht 200 über mehrere Minuten.
- `ddi_agents_connected` kleiner als die Zahl der Agents (ein Agent ohne Stream arbeitet im Polling weiter, meldet aber ggf. ein Problem).
- `ddi_license_writable == 0` (Lesemodus, siehe §10.6).
- In der Oberfläche bzw. `GET /api/v1/agents`: Health-Zustand ungleich `ok`, angewendete Revision ≠ Soll-Revision über längere Zeit, Deployments mit Ergebnis `rejected`, `rolled_back` oder `failed`.
- Auf den Agent-Hosts zusätzlich BIND und Kea selbst überwachen (DNS-Abfrage, DHCP-Dienststatus); die Datenebene läuft auch ohne Server und Agent weiter.
- Zeitsynchronisation auf allen Hosts (Zertifikate, TSIG und Lizenzprüfung brauchen korrekte Uhren).
## 4. Sicherung
Was gesichert werden muss:
| Was | Wo | Wie oft | Hinweis |
| --- | --- | --- | --- |
| **Datenbank** | PostgreSQL `ddi-suite` | täglich, vor jedem Upgrade | Enthält alle Fachdaten, Revisionen, Audit-Log und – versiegelt – CA, Bundle-Schlüssel, TSIG-Schlüssel, HA-Passwörter, Lizenzzustand. |
| **Master-Key** | `/etc/ddi-suite/master.key` | einmalig und nach jeder Änderung | **Ohne ihn ist die Datenbank nur teilweise brauchbar** (§5). Getrennt von der Datenbanksicherung aufbewahren (z. B. Passwortsafe, Tresor). |
| **Server-Konfiguration** | `/etc/ddi-suite/` (`server.yaml`, TLS-Zertifikat und -Schlüssel) | nach Änderungen | |
| **Agent-Identität** (optional) | `/var/lib/ddi-suite/agent-<rolle>/identity/` | nach dem Enrollment | Erspart nach einer Neuinstallation des Agent-Hosts das erneute Registrieren. Ohne Sicherung: neu registrieren (§8.2). |
| BIND-/Kea-Grunddateien | `named.conf`, `rndc.key`, Kea-Leases | nach Bedarf | Generierte Dateien muss man nicht sichern, der Server liefert sie neu. Leases (`kea-leases4.csv`) sichern, wenn laufende Vergaben eine Neuinstallation überstehen sollen. |
Datenbank sichern (als root auf dem Server, Peer-Authentifizierung):
```sh
sudo -u postgres pg_dump --format=custom --file=/var/backups/ddi-suite-$(date +%F).dump ddi-suite
```
Der Dump enthält keine Klartext-Geheimnisse, aber IPAM-Daten und Passwort-Hashes: verschlüsselt ablegen. Ein Dump im laufenden Betrieb ist konsistent (eine Transaktion); der Server muss dafür nicht angehalten werden.
## 5. Wiederherstellung
1. Server installieren wie in [INSTALL.md](INSTALL.md) beschrieben, **ohne** ihn zu starten (bzw. danach `systemctl stop ddi-server`).
2. Den gesicherten **Master-Key** nach `/etc/ddi-suite/master.key` legen (Eigentümer `ddi-suite`, Modus 0400) und die gesicherte `server.yaml` zurückspielen.
3. Datenbank leeren und zurückspielen:
```sh
sudo -u postgres dropdb ddi-suite && sudo -u postgres createdb -O ddi-suite ddi-suite
sudo -u postgres pg_restore --role=ddi-suite --no-owner --dbname=ddi-suite /var/backups/ddi-suite-<datum>.dump
```
4. `systemctl start ddi-server`. Neuere Server-Versionen migrieren das Schema beim Start.
5. Agents verbinden sich von selbst wieder: CA und Bundle-Schlüssel stammen aus der Sicherung, ihre Zertifikate bleiben gültig. Agents, die seit der Sicherung ihr Zertifikat erneuert haben, kennt der Server nur mit der alten Seriennummer: Sie werden abgewiesen und müssen neu registriert werden (§8.2). Ebenso Agents, die erst nach der Sicherung registriert wurden.
6. Liegt die Sicherung hinter der höchsten Revision, die Agents schon angewendet haben, lehnen sie ältere Nummern ab (Replay-Schutz). Abhilfe: so lange neue Revisionen erzeugen bzw. einen Rollback auf den gewünschten Stand ausführen, bis die Nummer über der höchsten angewendeten liegt; die Oberfläche zeigt die angewendete Revision je Agent.
**Master-Key verloren = Geheimnisse verloren.** Ohne den passenden Master-Key startet der Server nicht. Ein neuer Master-Key macht die versiegelten Werte nicht wieder lesbar. Der einzige Weg ist ein Neuaufbau der Geheimnisse: neuer Master-Key, Einträge in `pki_keys` löschen (der Server erzeugt CA und Bundle-Schlüssel beim Start neu), alle Agents neu registrieren, alle TSIG-Schlüssel neu anlegen bzw. erneuern, DHCP-Cluster neu anlegen (HA-Passwort), OIDC-Secret neu eintragen, TOTP für alle Benutzer neu einrichten, Lizenz neu eintragen. Die IPAM-Daten bleiben erhalten. Bis dahin laufen DNS und DHCP mit ihrer letzten Konfiguration weiter.
## 6. Upgrade
- **Server:** `sudo ./install.sh --upgrade --role server [--version <tag>]` ersetzt Binary und Unit und startet den Dienst neu (Details in [INSTALL.md](INSTALL.md)). Datenbankmigrationen laufen beim Start automatisch (`database.auto_migrate`, Standard an); manuell mit `ddi-server migrate`. Vorher die Datenbank sichern (§4) – Migrationen werden nicht automatisch zurückgenommen; ein Downgrade heißt: alte Version installieren und die Sicherung zurückspielen.
- **Upgrade auf DHCP pro Adresse ([ADR 0025](adr/0025-dhcp-pro-adresse-pruefungen-ui.md), Migration 00006):** Jede freie Adresse eines bisherigen Pools wird ein `dynamic`-Eintrag mit Zufallsnamen `dyn-…` (ohne Netz-, Broadcast- und Gateway-Adresse); bisher namenlose `dynamic`-Einträge erhalten einen Zufallsnamen, kollidierende Lease-Namen wandern nach `lease_hostname`. Große Pools werden zu vielen Zeilen (ein /16-Pool: rund 65.000) und zählen gegen das Adresslimit der Lizenz. Die nächste Revision enthält für alle diese Einträge A- und PTR-Records; die Kea-Pools bleiben gleich. Ältere gespeicherte Revisionen werden beim Rollback unverändert erzeugt (ohne Records für `dynamic`).
- **Agents:** `sudo ./install.sh --upgrade --role agent-dns` bzw. `agent-dhcp` auf jedem Agent-Host. Identität und Revisionen bleiben erhalten. Während des Neustarts läuft BIND bzw. Kea unverändert weiter.
- **Reihenfolge:** erst der Server, dann die Agents. Das gRPC-Protokoll wird nur rückwärtskompatibel erweitert (neue Felder, keine geänderten Bedeutungen); ein Server bedient Agents der Vorversion. Agents, die neuer als der Server sind, werden nicht unterstützt. Die Versionen aller Agents zeigt die Agent-Liste.
- **BIND/Kea:** Pakete der Distribution bzw. des ISC-Repositorys wie gewohnt aktualisieren. Nach einem Wechsel der Kea-Hauptversion (z. B. 2.6 → 3.x) mit einer neuen Revision prüfen, dass `kea-dhcp4 -t` die Konfiguration akzeptiert (der Agent tut das bei jedem Ausrollen).
- **Releases prüfen:** Der Installer prüft `SHA256SUMS` und die Signatur ([RELEASE.md](RELEASE.md)). Von Hand: `sha256sum -c SHA256SUMS` und `scripts/sign-release.sh verify SHA256SUMS SHA256SUMS.sig release-signing.pub.pem`.
- **Pakete:** Wer statt der Tarballs die `.deb`-/`.rpm`-Pakete des Releases nutzt (`apt install ./ddi-server_<version>-1_amd64.deb` bzw. `dnf install ./ddi-server-<version>-1.x86_64.rpm`), aktualisiert damit nur Binary (`/usr/bin`) und Unit; die Pakete legen keine Benutzer oder Konfiguration an und starten nichts. Eingerichtet wird weiterhin mit dem Installer, danach `systemctl restart ddi-server`.
## 7. Aufbewahrung (Retention)
Audit-Log und Revisionen wachsen mit jeder Änderung (Messwerte in [SYSTEM-REQUIREMENTS.md §7](SYSTEM-REQUIREMENTS.md#7-plattenplatz-planung)). Standardmäßig wird nichts gelöscht. In `server.yaml`:
```yaml
retention:
audit_days: 365 # Audit-Einträge älter als 365 Tage löschen (0 = unbegrenzt, Standard)
revisions_keep: 200 # nur die neuesten 200 Revisionen behalten (0 = alle, Standard)
```
Der Server räumt auf, sobald einer der beiden Werte gesetzt ist (auch per Umgebung: `DDI_RETENTION_AUDIT_DAYS`, `DDI_RETENTION_REVISIONS_KEEP`): fünf Minuten nach dem Start und danach alle 24 Stunden, in einer Transaktion, auch im Lizenz-Lesemodus. Gelöscht werden Audit-Einträge, die älter als `audit_days` Tage sind (höchstens 36.500), und Revisionen jenseits der neuesten `revisions_keep` samt ihren Dateien. **Nie gelöscht** werden die neueste Revision, jede Revision, die ein Agent anwendet, als letzte funktionierende kennt oder als Soll-Revision hat, und Revisionen mit offenen (`pending`) Deployments. Deployments selbst bleiben erhalten (sie nennen nur die Nummer). Hat ein Lauf etwas gelöscht, schreibt er den Audit-Eintrag `retention.cleanup` mit den Anzahlen und den gelöschten Revisionsnummern.
Folgen: Ein Rollback auf eine gelöschte Revision und ein Diff gegen eine gelöschte Vorgängerin sind nicht mehr möglich (`not_found`). Wer das Audit-Log länger braucht als die Aufbewahrung, exportiert es vorher (`GET /api/v1/audit/export`, Seite **Audit**). Nach großen Löschläufen gibt PostgreSQL den Platz erst durch `VACUUM` an die Tabelle zurück (Autovacuum erledigt das), an das Dateisystem erst durch `VACUUM FULL`.
## 8. Schlüssel und Zertifikate
### 8.1 Agent-Zertifikate
Agent-Zertifikate laufen 14 Tage und werden automatisch ab der Hälfte der Laufzeit über die bestehende Verbindung erneuert (neuer Schlüssel). Ein Agent, der länger offline war, darf sein abgelaufenes Zertifikat bis 30 Tage nach Ablauf noch für die Erneuerung benutzen. Danach ist ein neues Enrollment nötig.
Das Serverzertifikat des Agent-Ports stellt der Server bei jedem Start neu aus; es muss nicht gepflegt werden. Das Zertifikat der Weboberfläche (`http.tls_cert_file`) erneuert der Administrator selbst (eigenes oder ACME-Zertifikat ablegen, `systemctl restart ddi-server`).
### 8.2 Erneuerung mit verlorener Antwort, Neuregistrierung
Der Server speichert je Agent nur die Seriennummer des **aktuellen** Zertifikats. Geht die Antwort auf eine Erneuerung verloren (Verbindungsabbruch genau in diesem Moment, Absturz vor dem Speichern), gilt beim Server schon das neue Zertifikat, der Agent hat aber nur das alte. Der Agent wird dann abgewiesen (Log: Zertifikat unbekannt bzw. ersetzt) und muss **neu registriert** werden. Ebenso nach mehr als 30 Tagen offline, nach einer Wiederherstellung des Servers aus einer älteren Sicherung (§5) oder nach Verlust des Zustandsverzeichnisses.
Neu registrieren:
1. In der Oberfläche den alten Agent **löschen** (er verlässt dabei seinen Cluster) und ein neues Enrollment-Token für die Rolle erzeugen.
2. Auf dem Agent-Host:
```sh
sudo -u bind ddi-agent-dns enroll --config /etc/ddi-suite/agent-dns.yaml --force \
--server <host:8444> --token <token> --ca-hash sha256:<hex>
systemctl restart ddi-agent-dns
```
(`--force` ersetzt die vorhandene Identität; der Befehl steht ohne `--force` fertig in der Oberfläche.)
3. Den neuen Agent ggf. wieder seinem Cluster zuordnen und eine Revision ausrollen. Die zuletzt angewendete Revisionsnummer bleibt im Zustand erhalten.
Während der ganzen Zeit läuft BIND bzw. Kea mit der letzten Konfiguration weiter.
### 8.3 Master-Key-Rotation
Die Master-Key-Datei kann mehrere Schlüssel enthalten (`<id> <base64>` je Zeile); der erste verschlüsselt, alle entschlüsseln.
```sh
systemctl stop ddi-server
cp -a /etc/ddi-suite/master.key /root/master.key.bak
ddi-server gen-master-key --out /tmp/new.key --id k2 # neue Datei mit einem Schlüssel
# Zeile "k2 …" aus /tmp/new.key als ERSTE Schlüsselzeile in master.key einfügen, alte Zeile darunter lassen
shred -u /tmp/new.key
chown ddi-suite /etc/ddi-suite/master.key && chmod 0400 /etc/ddi-suite/master.key
systemctl start ddi-server
```
Neue und geänderte Geheimnisse werden ab dann mit `k2` versiegelt; vorhandene bleiben mit dem alten Schlüssel lesbar. Ein Werkzeug, das alle vorhandenen Werte neu verschlüsselt, gibt es noch nicht (ADR 0015 sieht es vor). Die alte Zeile darf deshalb **nicht** entfernt werden, solange Werte unter ihr liegen; wer sie loswerden will, erneuert die betroffenen Geheimnisse (TSIG-Schlüssel rotieren, OIDC-Secret neu eintragen, TOTP neu einrichten …). CA und Bundle-Schlüssel lassen sich nicht neu versiegeln, ohne sie neu zu erzeugen (§5). Beide Schlüsselzeilen gehören in die Sicherung.
Der Server startet nur, wenn die Datei weder für die Gruppe noch für andere lesbar ist (Modus 0400 oder 0600).
### 8.4 TSIG-Schlüssel und HA-Passwort
- **TSIG:** Seite **TSIG-Schlüssel** → „Erneuern“ (bzw. `POST /api/v1/tsig-keys/{id}/rotate`), danach eine Revision ausrollen. Das gilt auch für den Transferschlüssel eines DNS-Clusters. Direkt nach dem Ausrollen kann ein Secondary kurz `tsig indicates error` melden, wenn er den neuen Schlüssel vor dem Primary aktiviert; NOTIFY holt den Transfer danach nach (§10.4). Externe Clients mit dem Schlüssel (`nsupdate`, DHCP-Server von Domain Controllern) mit dem neuen Geheimnis versorgen (Admins: „Geheimnis anzeigen“, wird auditiert).
- **HA-Passwort:** wird beim Anlegen eines DHCP-Clusters erzeugt; es gibt keinen Erneuern-Knopf. Zum Erneuern den Cluster neu anlegen (§9). Benutzer und Passwort stehen auf den Kea-Hosts nur in `ddi-suite-ha-user` und `ddi-suite-ha-password` neben der Kea-Konfiguration, weil Kea 3.x Klartext-Zugangsdaten in der Konfiguration ablehnt.
- **`rndc`-Schlüssel:** `rndc-confgen -a`, BIND neu starten; die Agent-Konfiguration verweist per `rndc_args` auf die Schlüsseldatei.
## 9. Cluster-Mitglieder hinzufügen und entfernen
**DNS (Primary + Secondaries):**
- *Secondary hinzufügen:* Agent auf dem neuen Host installieren und registrieren, in der Oberfläche dem Cluster als `secondary` mit der Adresse hinzufügen, über die er den Primary erreicht. Revision ausrollen. Der Primary erhält ihn in `also-notify`, der Secondary holt alle Zonen per AXFR. Firewall: 53/tcp und 53/udp zwischen beiden.
- *Secondary entfernen:* aus dem Cluster entfernen, ausrollen, danach Agent löschen oder deinstallieren. Ein entfernter, noch laufender Agent erhält wieder die Konfiguration der Standardgruppe (alle Zonen ohne Cluster als Primary) – deshalb erst entfernen, dann den Dienst stoppen.
- *Primary wechseln:* neuen Primary als `primary` eintragen und den alten im selben Schritt zum `secondary` machen oder entfernen (es gibt genau einen Primary), ausrollen. Dynamische Zonen: deren Daten liegen nur beim bisherigen Primary (Journal); vor dem Wechsel per AXFR auf dem neuen Primary bereitstellen oder nach dem Wechsel neu befüllen lassen (Domain Controller registrieren ihre Records erneut).
**DHCP (Kea-HA):**
- *Zweiten Server hinzufügen:* Agent registrieren, dem Cluster als `secondary` mit der Adresse des HA-Listeners hinzufügen (Port `ha_port`, Standard 8001), ausrollen. Beide Peers synchronisieren die Leases und gehen in `hot-standby` (bzw. `load-balancing`). Voraussetzung Kea ≥ 3.0 auf beiden.
- *Server entfernen:* aus dem Cluster entfernen und ausrollen; der verbleibende arbeitet als Einzelserver. Dann den Agent löschen und Kea stoppen.
- *HA-Passwort erneuern:* Mitglieder entfernen, Cluster löschen, neu anlegen, Netze bzw. Standardcluster neu zuordnen, ausrollen.
Clusteränderungen wirken wie alle Änderungen erst mit der nächsten Revision und erscheinen vorher unter „Änderungen warten auf Ausrollen“.
## 10. Fehlersuche
### 10.1 Agent lehnt eine Revision ab
Deployment-Ergebnis `rejected` (Prüfung gescheitert, nichts aktiviert) oder `rolled_back` (aktiviert, Health-Check gescheitert, vorige Revision wiederhergestellt). Die Meldung steht beim Deployment in der Oberfläche und im Agent-Log.
- Häufige Ursachen: Pfade der Generator-Einstellungen passen nicht zur Agent-Konfiguration (`zone_dir`, `control_socket` – der Agent lehnt dann ausdrücklich ab), `named.conf` bindet die Includes nicht oder in falscher Reihenfolge ein, Rechte auf `zone_dir`/`secondary/`, fehlender Kea-Hook (Kea 3.x lädt Hooks nur aus dem eingebauten Verzeichnis), BIND-Optionen der Distribution kollidieren mit generierten Optionen (`recursion`, `allow-transfer` …).
- Eine abgelehnte Revision wird **nicht** automatisch erneut versucht. Nach der Korrektur eine neue Revision erzeugen oder dieselbe Revision per „Jetzt ausrollen“ erneut pushen.
- Prüfen von Hand auf dem Agent-Host: `named-checkconf -z /etc/bind/named.conf`, `kea-dhcp4 -t /etc/kea/kea-dhcp4.conf`.
### 10.2 Rollback
- **Über die Oberfläche:** Revisionen → ältere Revision → „Zurückrollen“ erzeugt eine **neue** Revision mit dem Inhalt der alten (höhere Nummer, also auch höhere SOA-Seriennummer). `auto_pull`-Agents übernehmen sie sofort, `manual`-Agents nach „Jetzt ausrollen“.
- **Automatisch im Agent:** Scheitert der Health-Check, stellt der Agent die letzte funktionierende Revision wieder her. Auf einem DNS-Primary sinkt dabei die Seriennummer; Secondaries behalten die höhere, bis die nächste Revision kommt. Die nächste Revision aus der Oberfläche behebt das.
- Revisionen ältere als M4 (ohne gespeicherte Generator-Eingabe) und Revisionen, deren Geheimnisse fehlen, lassen sich nicht zurückrollen (`rollback_unavailable`).
### 10.3 Kea übernimmt die neue Konfiguration nicht (`config-reload`)
Kea liest bei `config-reload` die Datei, **mit der es gestartet wurde**. Zeigt `dhcp.config_file` des Agents auf eine andere Datei, meldet der Reload Erfolg, Kea arbeitet aber mit der alten Konfiguration weiter; nur der Dateivergleich im Health-Check des Agents fällt auf („Konfiguration weicht ab“). Abhilfe: `config_file` des Agents auf die Datei setzen, die die Kea-Unit mit `-c` übergibt (Standard `/etc/kea/kea-dhcp4.conf`), und Kea neu starten.
Verwandte Punkte: Der Control-Socket muss in Keas Socket-Verzeichnis liegen (`/run/kea`, Modus 0750, Umgebungsvariable `KEA_CONTROL_SOCKET_DIR`), die Lease-Datei in Keas Datenverzeichnis; die Generator-Einstellungen (`control_socket`, `lease_file`, `ha_password_file`) müssen zu den Pfaden auf den Hosts passen.
### 10.4 Secondary überträgt keine Zonen
- `journalctl -u named` auf dem Secondary: `tsig indicates error` bzw. `BADKEY`/`BADSIG` heißt, Primary und Secondary haben unterschiedliche Schlüssel. Direkt nach einer Schlüsseländerung normal (der Secondary war schneller); NOTIFY löst den Transfer wieder aus. Bleibt es dabei: prüfen, dass beide Agents dieselbe Revision anwenden (Oberfläche), und `rndc retransfer <zone>` auf dem Secondary.
- `BADTIME`: Uhren gehen auseinander (TSIG erlaubt 5 Minuten) → NTP prüfen.
- `connection refused`/Timeout: 53/tcp vom Secondary zum Primary freigeben; die Adressen der Mitglieder im Cluster müssen die sein, unter denen sich die Server gegenseitig erreichen.
- `refused` beim Transfer: Der Primary kennt den Secondary nicht (Cluster nicht ausgerollt) oder der Schlüssel fehlt in `allow-transfer`.
- Eine noch nicht übertragene Zone zeigt der Health-Check als „eingeschränkt“, nicht als Fehler.
### 10.5 Agent nicht verbunden
- `ddi-agent-<rolle> status`: Ist das Zertifikat abgelaufen? Stimmt die Serveradresse?
- Netz: Agent → Server 8444/tcp ausgehend. Ein Proxy, der TLS aufbricht, verhindert mTLS.
- Log „certificate … unknown/replaced/revoked“: neu registrieren (§8.2).
- Der Agent fällt bei Störungen des Streams auf Polling zurück; „verbunden“ zeigt nur den Stream.
### 10.6 Lizenz: Lesemodus
Im Zustand `read_only` (Testzeitraum abgelaufen, Lizenz abgelaufen oder gesperrt, Keygen länger als die Karenzzeit nicht erreichbar) lehnt der Server alle Änderungen und das Ausrollen ab; Lesen, Export und die Lizenzverwaltung bleiben möglich, Agents sperren und löschen ebenfalls. **DNS und DHCP laufen unverändert weiter**, Agents holen weiterhin ihre Soll-Revision. Abhilfe: Lizenzschlüssel eintragen bzw. erneuern (Seite **Lizenz** oder auf dem Server `ddi-server license set-key <schluessel.txt`: liest den Schlüssel von stdin, gibt Status, Ablauf und Grenzen aus, nie den Schlüssel; Exit-Code 0 aktiviert, 2 Eingabe leer, zu lang oder mehrzeilig, 3 Schlüssel abgelehnt, 4 Lizenzserver nicht erreichbar oder Antwort nicht vertrauenswürdig, 1 sonstiger Fehler; bei 3 und 4 wird nichts gespeichert), Erreichbarkeit von `https://license.beugsiud.de` (443/tcp ausgehend) prüfen, in isolierten Netzen eine Offline-Lizenzdatei importieren. Einzelheiten: [LICENSING.md](LICENSING.md).
### 10.7 Prüfung „Ping/DNS“ meldet „nicht verfügbar“ oder Fehler
Die Prüfung einzelner Einträge (`POST /addresses/check`, ADR 0025) läuft vom Server aus:
- **Ping `unavailable`:** Der Server versucht zuerst unprivilegierte ICMP-Sockets (`net.ipv4.ping_group_range`), dann einen Raw-Socket mit `CAP_NET_RAW` aus seiner systemd-Unit. „Nicht verfügbar“ heißt, beides ging nicht: Unit älter als v0.8.1 (`install.sh --upgrade`), die Capability in einem Container nicht vorhanden (z. B. LXC ohne `CAP_NET_RAW`) oder der Server läuft nicht unter Linux. Abhilfe ohne Capability: `sysctl -w net.ipv4.ping_group_range="0 2147483647"` und dauerhaft in `/etc/sysctl.d/`. „Nicht verfügbar“ sagt nichts über den Host aus.
- **Ping `unreachable`:** keine Antwort innerhalb von zwei Versuchen à 1 s – Host aus oder ICMP auf dem Weg (Firewall) gesperrt.
- **DNS `error` oder `missing`:** Gefragt werden die Mitglieder des DNS-Clusters der Zone (Primary zuerst), sonst die Nameserver-Adressen aus den Generator-Einstellungen, bei Zonen ohne Cluster außerdem die DNS-Agents unter der Adresse ihrer letzten Verbindung, erst dann der Resolver des Servers („system“). Der Server braucht 53/udp und 53/tcp zu ihnen. Die Hinweise unter dem Ergebnis nennen die häufigsten Gründe für „fehlt“: nur der Resolver des Servers wurde gefragt, oder die Änderung ist **noch nicht ausgerollt**. In **dynamischen Zonen** (z. B. einer Active-Directory-Domäne) trägt der DNS-Agent die Einträge der Adresstabelle beim Ausrollen per `nsupdate` ein ([ADR 0026](adr/0026-eintraege-in-dynamischen-zonen.md)); fehlt dort etwas, zuerst den Bericht des Agents zur Revision ansehen. `mismatch` heißt, der DNS-Server antwortet anders als die Adresstabelle.
### 10.8 Weitere Hinweise
- Server startet nicht, „master key file … must not be accessible by group or others“: `chmod 0400 /etc/ddi-suite/master.key`.
- Server startet nicht, „decryption failed“: falscher Master-Key zur Datenbank (§5).
- CSV-Import: rund 1.100 Zeilen pro Sekunde (gemessen, [SYSTEM-REQUIREMENTS.md §4](SYSTEM-REQUIREMENTS.md#4-messungen)); eine Datei an der Grenze von 50.000 Zeilen braucht knapp eine Minute. Die Anfrage hat dafür eine eigene Frist von 10 Minuten (statt der allgemeinen 60 s).
- Login gesperrt (HTTP 429): Ratenbegrenzung, nach einer Minute wieder ein Versuch. Ein vergessenes Admin-Passwort setzt ein anderer Admin zurück (Benutzer → Passwort zurücksetzen). `ddi-server bootstrap-admin` funktioniert nur, solange es noch keinen Benutzer gibt; deshalb immer mindestens zwei Administratoren oder OIDC-Zugang einrichten.
### 10.9 Einträge in dynamischen Zonen (ADR 0026)
Die von DDI verwalteten Einträge einer dynamischen Zone (Adresstabelle und Records der Zone) trägt der DNS-Agent auf dem Primary nach dem Neuladen von BIND per dynamischem Update ein: `nsupdate -k` mit dem Systemschlüssel `ddi-agent-update` aus der Revision, an `dns_address` der Agent-Konfiguration. Er löscht ausschließlich Einträge, die die vorige Revision verwaltet hat und die neue nicht mehr enthält; Einträge der Domain Controller, anderer Clients oder von Hand bleiben unberührt. Secondaries erhalten die Änderungen per NOTIFY/IXFR.
- `nsupdate` muss auf dem Primary vorhanden sein (Debian/Ubuntu: `bind9-dnsutils`, EL9: `bind-utils`; der Installer richtet beides ein). Programmname oder Pfad: Option `dns.nsupdate` in `agent-dns.yaml` (Standard `nsupdate`).
- Agent-Log: „managed records of a dynamic zone updated“ mit `zone`, `records` (Anzahl der verwalteten Einträge) und `deleted`.
- Scheitert ein Update, scheitert das Aktivieren, und der Agent rollt auf die vorige Revision zurück, die ihren eigenen Stand wieder einträgt. `REFUSED` heißt meist, dass der Schlüssel in der Update-ACL der Zone fehlt; `NOTAUTH` mit `BADSIG`/`BADKEY` im BIND-Log, dass BIND einen anderen Schlüssel kennt. Direkt nach dem Anlegen einer Zone wartet der Agent bis zu 10 s, bis BIND sie geladen hat.
- Einen verwalteten Eintrag, den jemand von Hand ändert oder löscht, setzt die nächste Revision auf den Stand von DDI zurück.