Files
DDI/docs/API.md
T
sir_melonchenandClaude Opus 5.5 cb7f4c9f3d
ci / Go (lint, test, vuln) (push) Successful in 6m43s
ci / Protobuf (push) Successful in 7s
ci / End-to-end (server, agents, BIND, Kea) (push) Successful in 3m35s
install / Installation (debian:12) (push) Successful in 3m20s
ci / Web (lint, test, audit, build) (push) Successful in 12m41s
ci / Shell and licence headers (push) Successful in 14s
ci / Integration (real BIND and Kea) (push) Successful in 2m50s
ci / Development environment (compose) (push) Successful in 15s
install / Installation (rocky:9) (push) Successful in 4m1s
install / Installation (ubuntu:24.04) (push) Successful in 3m23s
ci / Release build (dry run) (push) Failing after 10m38s
install / Installation (debian:13) (push) Successful in 3m22s
install / Installation (ubuntu:22.04) (push) Successful in 3m33s
feat(license): gate features by entitlement and enforce maxAgents
Clusters (DDI_CLUSTER), Kea HA pairs (DDI_KEA_HA), dynamic zones
(DDI_DYNAMIC_ZONES) and SSO (DDI_SSO) now need the matching Keygen
entitlement; the trial grants all of them. Only taking a feature into
use is blocked (403 license_feature): existing clusters, HA pairs,
dynamic zones and enabled SSO keep working and stay editable.

maxAgents is checked when creating an enrollment token and, under an
advisory lock, at enrollment; revoked agents do not count. GET /license
reports available_features and the agent usage. The UI shows the
features on the license page and disables locked controls, and
keygen-setup.sh creates the entitlements and assigns them to licenses
(ADR 0027).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:51:55 +02:00

8.3 KiB
Raw Blame History

REST-API

Die vollständige, verbindliche Beschreibung ist die OpenAPI-Spezifikation server/api/openapi.yaml (ADR 0012, ADR 0019). Dieses Dokument erklärt nur die übergreifenden Regeln.

Grundlagen

  • Basis-URL: https://<server>/api/v1
  • Format: JSON (application/json), Felder in snake_case
  • CSV-Export und -Import: text/csv, Audit-Export zusätzlich als JSON Lines

Anmeldung und Sitzung

  1. POST /auth/login mit {"username", "password"} setzt das Sitzungs-Cookie ddi_session (HttpOnly, SameSite=Lax, Secure bei HTTPS) und liefert das CSRF-Token.
  2. Ist für das Konto TOTP aktiv, ist die Sitzung zunächst mfa_required: true. Erst POST /auth/totp/verify mit {"code"} schließt die Anmeldung ab.
  3. Muss das Passwort geändert werden (must_change_password), ist bis zu POST /auth/password nur die Passwortänderung erlaubt.
  4. Single Sign-On: Der Browser ruft GET /auth/oidc/start auf und landet nach dem Identitätsanbieter über GET /auth/oidc/callback wieder in der Anwendung.

Jede zustandsändernde Anfrage (POST, PUT, DELETE) braucht den Header X-CSRF-Token: <csrf_token aus der Sitzung>. Anfragen von fremden Seiten (Origin/Sec-Fetch-Site) werden abgelehnt.

Sitzungen enden nach 12 Stunden oder nach 1 Stunde Inaktivität (konfigurierbar), bei Abmeldung, Passwortänderung (andere Sitzungen) und Deaktivierung des Kontos.

Rollen

Recht Viewer Operator Admin
Netze, Adressen, Zonen, Records, Cluster, TSIG-Schlüssel (ohne Geheimnis), Lizenzstatus ansehen, exportieren, eigene Filter ✓ ✓ ✓
Netze, Adressen, Zonen und Records ändern, Import ✓ ✓
Revisionen erzeugen, ausrollen, zurückrollen ✓ ✓
Cluster und TSIG-Schlüssel verwalten, Geheimnis eines Schlüssels anzeigen, Agents, Generator-Einstellungen ✓
Benutzer, SSO, Lizenz verwalten, Audit-Log ✓

GET /auth/session liefert die effektiven Rechte (permissions).

Fehler

Fehler sind Problem-Dokumente nach RFC 9457 (application/problem+json):

{
  "type": "about:blank",
  "title": "Unprocessable Entity",
  "status": 422,
  "code": "validation_failed",
  "detail": "validation failed",
  "request_id": "5f2c…",
  "problems": [{ "code": "mac_required", "field": "mac" }]
}
  • code ist stabil und Teil des Vertrags; die Oberfläche übersetzt ihn. detail ist Englisch und nur für Logs gedacht.
  • problems listet Feldfehler mit eigenem code, optional field und params.
  • request_id entspricht dem Header X-Request-ID und findet sich in den Server-Logs wieder.

Wichtige Statuscodes: 401 (nicht angemeldet, mfa_required), 403 (forbidden, csrf, license_read_only, license_limit, license_feature, password_change_required), 404, 409 (Konflikt, z. B. network_overlap, version_conflict), 422 (Validierung), 429 (zu viele Anmeldeversuche, mit Retry-After).

Schreibschutz durch die Lizenz

Ist der Server schreibgeschützt (siehe LICENSING.md), antworten alle ändernden Endpunkte mit 403 license_read_only. Lesen, Export, Anmeldung, eigene Filter und die Lizenzverwaltung bleiben erlaubt. Überschreitet eine Änderung ein Limit der Lizenz (Adressen, Agents), antwortet der Server mit 403 license_limit (Parameter kind, limit); braucht sie eine nicht freigeschaltete Funktion, mit 403 license_feature (Parameter feature, ADR 0027).

Gleichzeitige Änderungen

Netze, Adressen, Cluster, TSIG-Schlüssel, Zonen und Records tragen eine version. Änderungen (PUT) müssen die gelesene Version mitsenden; hat jemand anderes inzwischen geändert, antwortet der Server mit 409 version_conflict.

Adresstabelle

GET /addresses filtert, sortiert und paginiert serverseitig:

Parameter Bedeutung
q Suche in IP (Präfix), MAC (beliebige Schreibweise), Hostname, FQDN, Beschreibung, Verantwortlichem
network_id, type (mehrfach), tag, lease_state Filter
sort, order Sortierung (ip, type, mac, hostname, fqdn, network, last_seen, lease, updated_at; asc/desc)
limit (max. 1000), offset Seite

Die Antwort enthält items und total. GET /addresses/export akzeptiert dieselben Filter und liefert CSV.

POST /addresses/preview beschreibt vorab die Auswirkungen einer Änderung auf DNS und DHCP (z. B. dhcp.add_reservation). Massenänderungen (/addresses/bulk) und CSV-Import (/addresses/import, mit dry_run=true zur Prüfung) gelten ganz oder gar nicht; Probleme werden pro Adresse bzw. Zeile gemeldet. /addresses/bulk kennt außerdem set_description, regenerate_hostnames (neue Zufallsnamen für dynamic-Einträge) und dry_run: Dann ändert sich nichts, die Antwort meldet Probleme und unter effects die zusammengefassten Auswirkungen (Codes wie bei der Vorschau, Parameter count; kommt ein Code nur einmal vor, stehen seine Parameter dabei).

DHCP-Bereiche und IP-Listen (ADR 0025)

Ein Netz hat keine Pools mehr; sein DHCP-Bereich besteht aus den dynamic-Einträgen. Network.pools ist eine abgeleitete, schreibgeschützte Liste der zusammenhängenden Bereiche; NetworkRequest kennt kein pools. dynamic-Einträge ohne Hostnamen erhalten einen Namen dyn- + 8 Zeichen [a-z0-9]. Address.lease_hostname ist der Name, den der DHCP-Client zuletzt gemeldet hat; er wirkt nicht auf DNS.

  • GET /networks/{id}/ips?offset&limit&filter=all|free|used (Recht ipam.read, limit bis 1024, Standard 256): jede Adresse des Netzes in Adressreihenfolge mit Zustand free, network, broadcast, gateway oder used (dann mit address). all und free nur für IPv4-Netze bis /16, sonst 422 network_too_large.
  • POST /networks/{id}/range (Recht ipam.write): {"start", "end", "action": "assign"|"clear", "type", "types", "dry_run"}. assign legt für jede freie Adresse des Bereichs einen Eintrag vom Typ dynamic, static oder reserved an (Netz-, Broadcast- und Gateway-Adresse sowie belegte Adressen zählen als skipped); clear löscht die Einträge der Typen types (Standard dynamic und reserved). Höchstens 65.536 Adressen, ein Audit-Eintrag network.range_assign bzw. network.range_clear je Aufruf. Fehlercodes: range_invalid, range_outside_network, range_too_large, invalid_type; Regelverstöße der Einträge (z. B. dhcp_disabled) als validation_failed.
  • POST /addresses/check (Recht ipam.read, 1–100 IDs): je Eintrag Ping (unprivilegiertes ICMP-Echo, Status reachable, unreachable oder unavailable mit Grund) und für Einträge mit DNS-Namen die Vorwärts- und Rückwärtsauflösung bei den autoritativen Servern der Zone (Status ok, missing, mismatch, error, mit befragtem Server und Antworten). Unbekannte IDs: 404 not_found.

Geheimnisse

TSIG-Schlüssel und HA-Passwörter erzeugt der Server selbst (ADR 0023). Die API liefert sie nur an einer Stelle aus: GET /tsig-keys/{id}/secret (Recht settings.admin, jeder Abruf steht im Audit-Log – ohne das Geheimnis). Dateien mit Schlüsselmaterial erscheinen in Vorschau, Revision, Diff und offenen Änderungen mit secret: true und leerem Inhalt; Diffs nennen für sie nur den Status. Das HA-Passwort eines DHCP-Clusters wird nie angezeigt.

Der Systemschlüssel ddi-agent-update (ADR 0026) entsteht mit der ersten dynamischen Zone und steht mit system: true in GET /tsig-keys. Mit ihm gleichen die DNS-Agents die von DDI verwalteten Records dynamischer Zonen ab (Dateien ddi-suite-update.key, geheim, und dynamic-records.json). Er lässt sich rotieren, aber nicht löschen (409 key_system); sein Name ist reserviert (409 key_exists).

Ziele von Revisionen

Dateien in Vorschau, Revision und Diff tragen ein target: leer für die Standardgruppe (Agents ohne Cluster), sonst die Agent-ID eines Cluster-Mitglieds. targets beschreibt die Ziele mit Rolle, Cluster und Mitgliedsrolle zum Zeitpunkt der Revision. Ein Agent erhält die Dateien seines Ziels, wenn die Revision eines hat, sonst die der Standardgruppe.

Betriebsendpunkte

Außerhalb von /api/v1, ohne Anmeldung:

Pfad Zweck
GET /healthz Prozess lebt
GET /readyz bereit (Datenbank erreichbar)
GET /metrics Prometheus-Metriken