ZPO spricht JSON über eine REST-Schnittstelle unter dem Basis-Pfad
/api/v1. Die Web-Oberfläche selbst läuft über dieselbe API — was die
Oberfläche kann, kann auch ein Skript.
Anfragen und Antworten sind JSON (Content-Type: application/json).
Fehler kommen als {"error": "…"} mit dem passenden HTTP-Status:
401 ohne gültige Anmeldung, 403 bei fehlender Berechtigung,
404 bei unbekanntem Endpunkt.
Die Zugriffskontrolle ist rollenbasiert: Jede geschützte Route fordert
eine Berechtigung, die über die Rollen des Benutzers aufgelöst wird.
Unbekannte Pfade unter /api/v1 enden in einem JSON-404 — nicht in der
Oberfläche.
Authorization: Bearer <jwt> — Benutzer-Sitzung aus dem Login
X-API-Key: <schlüssel> — API-Schlüssel für Skripte und Dienste
Login:POST /api/v1/auth/login mit username und password liefert
token und das Benutzerprofil. Das Token signiert ZPO mit dem
jwt_secret aus der Konfiguration; die Lebensdauer steuert
access_token_ttl_minutes (siehe Konfiguration). Ist
für den Benutzer Zwei-Faktor aktiv, antwortet der Login ohne code mit
200, totp_required: true und einer challenge. Die geht zusammen mit
dem sechsstelligen Code aus der Authenticator-App (oder einem Einmalcode)
an POST /api/v1/auth/login/totp ({"challenge": …, "code": …}) und
liefert dort Token und Profil. Eine Challenge gilt fünf Minuten, lässt
fünf Codes zu und ist nach der Anmeldung verbraucht. Skripte können den
Code stattdessen auch gleich beim Login im Feld code mitschicken.
API-Schlüssel werden unter apikeys angelegt und tragen einen Scope:
read (nur lesende Methoden — ein schreibender Aufruf endet in 403)
oder readwrite (Vorgabe) — oder mcp: Ein solcher Schlüssel gilt nur an
der MCP-Schnittstelle, an der REST-API nicht. Der Schlüssel wird einmalig beim Anlegen
angezeigt und lässt sich später nur noch widerrufen. Requests mit
X-API-Key (an /mcp auch als Bearer zpo_…) unterliegen einer Begrenzung pro Schlüssel und Minute
(api_key_rate_limit_per_minute); die Begrenzung greift vor der
Schlüsselprüfung und drosselt damit auch Brute-Force mit ungültigen
Schlüsseln.
Proxy-Knoten (HAProxy) und DNS-Knoten (MikroTik RouterOS, AdGuard Home).
Der Verbindungstest schreibt den Zustand fort und zählt als schreibender
Zugriff.
Bei einem Proxy-Knoten mit https://-Verwaltungs-Adresse trägt das Feld
tls_fingerprint den SHA-256-Fingerabdruck des Zertifikats (das Setup-Skript
gibt ihn aus). Ohne Fingerabdruck prüft ZPO gegen die CAs des Systems. Mit
http:// ist das Feld nicht erlaubt.
Methode
Pfad
Zweck
GET
/api/v1/proxy-nodes
Proxy-Knoten auflisten
POST
/api/v1/proxy-nodes
Proxy-Knoten eintragen
PUT
/api/v1/proxy-nodes/:id
Proxy-Knoten ändern
DELETE
/api/v1/proxy-nodes/:id
Proxy-Knoten löschen
POST
/api/v1/proxy-nodes/:id/test
Verbindungstest ausführen
POST
/api/v1/proxy-nodes/:id/disabled
Knoten deaktivieren bzw. aktivieren ({"disabled": true})
Herunterladen: {"format": "fullchain"} — fullchain, cert, chain, der, p7b; mit Schreibrecht auch key, bundle, pfx, pfx-legacy (diese beiden mit password)
POST
/api/v1/certificates/upload
Fremdzertifikat hochladen
GET
/api/v1/acme-accounts
ACME-Konten auflisten
POST
/api/v1/acme-accounts
ACME-Konto anlegen
PUT
/api/v1/acme-accounts/:id
ACME-Konto ändern
DELETE
/api/v1/acme-accounts/:id
ACME-Konto löschen
GET
/api/v1/dns-providers
DNS-01-Provider-Zugänge auflisten
GET
/api/v1/dns-providers/catalog
Katalog der gepflegten Provider-Masken
POST
/api/v1/dns-providers
Provider-Zugang anlegen
POST
/api/v1/dns-providers/test
Zugang testen: {"id", "provider", "values", "domain"} — legt _acme-challenge.<domain> an und entfernt ihn wieder
Je Standort LAPI und Proxys mit ihrem Zustand, Gesamtzustand der LAPI (health) und angebundene Systeme (clients), dazu der Abgleich (Matrix, zuletzt übertragen)
POST
/api/v1/crowdsec/lapis
LAPI eines Standorts eintragen: {"site_id", "url", "tls_fingerprint", "key", "machine"} — tls_fingerprint nur bei https, SHA-256 mit oder ohne Doppelpunkte
Allowlists aus CrowdSec selbst (cscli allowlists), nur lesend — braucht den Maschinen-Zugang
GET
/api/v1/crowdsec/bans
Aktive Entscheidungen je Standort samt Stand des Abgleichs; type ist ban oder captcha (Stufe 1)
GET
/api/v1/crowdsec/history?value=<adresse>
Verlauf einer Adresse oder eines Netzes: die Vorfälle, die ZPO für die Sperrstufen zählt (at, scenario, site, escalated — führte zur Sperre), neueste zuerst, höchstens 100
POST
/api/v1/crowdsec/bans/lift
Sperre aufheben: {"lapi_id", "scope", "value", "everywhere"} — braucht den Maschinen-Zugang der LAPI
GET
/api/v1/crowdsec/policy
Die zwei Sperrstufen: captcha_off, cookie_minutes, max_number, escalate_after, window_hours, ban_hours, ban_max_hours, direct_ban (Szenarien je Zeile), dazu captcha_stage_hours (Dauer der Stufe 1, nur lesend)
PUT
/api/v1/crowdsec/policy
Sperrstufen ändern; 0 heißt Vorgabe
GET
/api/v1/stats
Statistik: ?period=24h|7d|30d&site=&node=&host= — Kennzahlen, Verlauf, Dienste, Ranglisten der Alarme. totals.active_bans zählt die echten Sperren, totals.active_captchas die Adressen, die nur die Prüfseite bekommen
Der Schutz eines Dienstes sind Felder am Dienst (/api/v1/hosts):
waf_level (patches, strict, off), bot_challenge, traps, captcha_off,
login_preset (proxmox, wordpress, gitlab, odoo, custom oder leer),
login_paths, login_max_attempts, error_rate_limit, max_connections.