Webhooks
Webhooks erlauben es, externe Systeme bei DNS-Änderungen automatisch zu benachrichtigen – z. B. einen ChatOps-Bot, ein internes Audit-Backend oder eine Backup-Pipeline, die nach jeder Zone-Änderung einen Snapshot zieht.
Webhook anlegen
Pro User unter Einstellungen → API & Sicherheit → Webhooks → Neuer Webhook:
- Name – Anzeigename.
- URL – HTTPS-Endpoint, der den POST entgegennimmt. Localhost / private IPs sind per Default blockiert (SSRF-Schutz, siehe unten).
- Events – Welche Events triggern sollen. Default
["*"]= alles.
Nach dem Speichern wird das Shared Secret einmalig angezeigt – damit prüfst du auf Empfänger-Seite die Signatur.
Verfügbare Events
| Event | Wann es feuert |
|---|---|
zone.imported | Eine Zone wurde per Import erstellt oder ersetzt. |
record.created | Neuer Record (per UI oder API). |
record.updated | Record-Inhalt oder TTL geändert. |
record.deleted | Record gelöscht. |
record.bulk | Bulk-Operation (mehrere Create/Delete in einem Call). |
Payload
Body ist JSON. Beispiel für record.updated:
{
"v": 1,
"event": "record.updated",
"timestamp": "2026-04-27T14:23:01.456Z",
"app": "PDNS Manager",
"actor_user_id": 17,
"data": {
"server": "master-fra1",
"zone": "example.com.",
"record": {
"name": "www.example.com.",
"type": "A",
"ttl": 60,
"old": [{"content": "203.0.113.10"}],
"new": [{"content": "203.0.113.20"}]
}
}
} Signaturprüfung (Pflicht!)
Jeder ausgehende Webhook-Request enthält den Header:
X-DNS-Manager-Signature: sha256=<hex> Wert ist eine HMAC-SHA256-Signatur über den rohen JSON-Body-Bytes, signiert mit dem Webhook-Shared-Secret. Auf Empfänger-Seite immer prüfen, sonst kann jeder Skript-Kid einen gefälschten Webhook an deinen Endpoint schicken.
Verifikation in Python
import hmac, hashlib
SECRET = b"dein-webhook-secret"
def verify(raw_body: bytes, header_value: str) -> bool:
if not header_value or not header_value.startswith("sha256="):
return False
sent = header_value.split("=", 1)[1].lower()
calc = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(sent, calc)
# In FastAPI/Flask: raw_body = await request.body() Verifikation in Node.js
import crypto from "node:crypto";
const SECRET = "dein-webhook-secret";
export function verify(rawBody, header) {
if (!header?.startsWith("sha256=")) return false;
const sent = header.slice("sha256=".length);
const calc = crypto.createHmac("sha256", SECRET).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(sent, "hex"), Buffer.from(calc, "hex"));
} SSRF-Schutz
Webhook-URLs werden vor dem Speichern und vor jedem Versand validiert. Standardmäßig blockiert:
localhost,127.0.0.0/8::1- RFC1918 (
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16) - Link-Local (
169.254.0.0/16,fe80::/10) - Multicast / Broadcast
Für interne Setups (z. B. ein Webhook gegen einen Internal-Service im selben Compose-Netz) gibt es einen Override:
# in der .env
WEBHOOK_ALLOW_PRIVATE_URLS=true Retry-Verhalten
Webhooks laufen als Background-Task. Bei einem nicht-2xx-Antwortcode wird mit Backoff einige Male wiederholt. Dauerhafte Fehler landen im Backend-Log – im Panel siehst du in der Webhook-Liste den letzten Status.
Secret rotieren
In der Webhook-Liste ein Eintrag → Secret rotieren. Das alte Secret ist sofort ungültig, das neue wird einmalig angezeigt.