Zum Inhalt springen

Signierter Webhook

Der signierte Webhook verbindet Perstat mit Ihren eigenen Gegenstellen: einem Incident-Werkzeug, das generische Alarmquellen annimmt, einer selbst betriebenen Automatisierung oder einem Skript. Chat-Ziele, PagerDuty und Opsgenie haben eigene, darauf zugeschnittene Connectoren.

Legen Sie in der App unter Integrationen einen Connector der Art Webhook an und geben Sie ihm eine URL (Owner und Admin). Perstat erzeugt ein Signatur-Secret und zeigt es genau einmal. Danach erscheint es maskiert, und Sie können es jederzeit rotieren. Die Rotation ersetzt das Secret sofort, und das alte prüft ab diesem Moment nicht mehr. Aktualisieren Sie Ihre Gegenstelle also im selben Schritt.

Mit webhooks:write legt die REST-API Webhooks an, ändert, testet und löscht sie und rotiert ihre Secrets. So verwalten auch Ihre Provisionierungsskripte die Webhooks.

Das Secret hat die Form whsec_ gefolgt von 64 hexadezimalen Kleinbuchstaben und Ziffern. Behandeln Sie die ganze Zeichenkette als undurchsichtigen Schlüssel.

http://-URLs werden für lokale Gegenstellen angenommen. Für alles, was ein fremdes Netz durchquert, verwenden Sie https://. Das Ziel muss öffentlich erreichbar sein: Ein Host, der auf eine private, Loopback- oder Link-local-Adresse auflöst, wird bei jedem Versand abgewiesen.

Was ein Endpunkt erhält, hängt davon ab, ob Ihre Organisation eine aktive Eskalationskette hat: einen Tarif mit Bereitschaftsplan (ab Sentinel, siehe Tarife und Limits), eine eingeschaltete Bereitschaft und eine aktivierte Kette.

Ohne aktive Kette (in jedem anderen Tarif, oder wenn Bereitschaft oder Kette ausgeschaltet sind) erhält jeder aktive Endpunkt die Ereignisse, die er unter Integrationen abonniert hat:

Ereignis Wann
incident.opened Ein Incident geht auf, kritisch oder eingeschränkt. Ein von einer Abhängigkeitsregel zurückgehaltener Alarm wird zugestellt, sobald er freigegeben ist.
incident.resolved Der Incident wird geschlossen.
incident.test Sie drücken Test am Connector. Synchron zugestellt, nur an diesen Endpunkt.

Mit aktiver Kette steuert die Kette die ausgehenden Incident-Meldungen. Ein Endpunkt erhält Meldungen zu kritischen Incidents, und zwar von den Stufen, die ihn als Ziel nennen, bei Minute 0 oder später:

Ereignis Wann
incident.escalation Eine Stufe, die den Endpunkt nennt, löst aus. Das ist auch die erste Meldung bei einer Stufe zu Minute 0: Das Ereignis sagt “dieser Incident ist unquittiert”, nicht “ein neuer ist gerade aufgegangen”.
incident.resolved Der Incident wird geschlossen. Die Entwarnung geht an jeden Endpunkt, den eine Stufe alarmiert hat, abonniert oder nicht, und an jeden aktiven Endpunkt, der sie abonniert hat. Was die Kette öffnet, schließt die Kette.
incident.test Wie oben.

Mit aktiver Kette bleibt das Abonnement von incident.opened ohne Wirkung. Eine Stufe überspringt einen stillgelegten Endpunkt, und sie überspringt alle Endpunkte, solange der Incident quittiert ist, solange sein Monitor deaktiviert oder archiviert ist und solange der Monitor in einem Wartungsfenster liegt. Ein während des Incidents stillgelegter Endpunkt erhält die Entwarnung für einen Incident, über den er alarmiert wurde, trotzdem.

Ein POST je Ereignis, mit JSON-Body:

POST <Ihre URL>
content-type: application/json
user-agent: perstat-webhook/1.0
x-perstat-signature: sha256=<64 Hex-Zeichen, Kleinschreibung>
{
"event": "incident.escalation",
"incident": {
"id": "inc_9f3c2a7e1b4d48c0a6e5f1d2c3b4a596",
"severity": "critical",
"cause": "HTTP 503",
"summary": "API antwortet nicht",
"monitor": { "id": "mon_4b1e8d2c7a9f4e6b8c0d1e2f3a4b5c6d", "name": "API" },
"started_at_unix": 1757930400,
"resolved_at_unix": null
},
"organization": { "id": "org_2d7a9c4e1f3b4a5c8d6e7f8a9b0c1d2e", "name": "Acme" }
}
Feld Bedeutung
event Eines der Ereignisse oben.
incident.id Über alle Ereignisse eines Incidents stabil. Öffnen und schließen Sie damit denselben Fall auf Ihrer Seite. PagerDuty erhält den Wert als dedup_key, Opsgenie als alias.
incident.severity critical oder degraded. Über eine aktive Kette erhalten Sie critical. Der Test sendet ebenfalls critical.
incident.cause Freitext: das Detail des ersten fehlgeschlagenen Checks oder die bei einem manuellen Incident eingetragene Ursache. test bei einer Testzustellung.
incident.summary Optional, null, wenn leer.
incident.monitor id und name des verknüpften Monitors. null bei einem manuellen Incident ohne Monitor und beim Test.
incident.started_at_unix Unix-Sekunden.
incident.resolved_at_unix Unix-Sekunden, null, bis der Incident geschlossen ist.
organization id und name der Organisation, der der Endpunkt gehört.

Der Body ist stabil und wächst additiv: Lesen Sie, was Sie brauchen, und ignorieren Sie Felder, die Sie nicht kennen.

Der Header trägt sha256= gefolgt vom HMAC-SHA256 über den rohen Anfrage-Body, mit dem Secret als Schlüssel, genau so, wie es Ihnen angezeigt wurde: Seine ASCII-Bytes sind der Schlüssel, es wird nicht dekodiert. Berechnen Sie den MAC über die empfangenen Bytes, vor jedem JSON-Parsen, und vergleichen Sie in konstanter Zeit.

Node:

import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, rawBody, header) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return expected.length === header.length
&& timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}

Python:

import hashlib
import hmac
def verify(secret: str, raw_body: bytes, header: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)

Ein Body, der die Prüfung nicht besteht, kam nicht von Perstat oder wurde unterwegs verändert. Antworten Sie mit 401 und tun Sie sonst nichts.

Die Signatur deckt allein den Body ab, ohne Zeitstempel und ohne Zustellungs-ID, eine mitgeschnittene Anfrage besteht die Prüfung also auch beim erneuten Einspielen. Stellen Sie über TLS zu und behandeln Sie ein wiederholtes Paar (incident.id, event) als bereits erledigt: Für ein Incident-Werkzeug ist ein zweimal geschlossener Alarm harmlos, ein zweimal geöffneter ist Lärm.

  • Eine 2xx-Antwort gilt als zugestellt. Jeder andere Status, auch ein 3xx, gilt als fehlgeschlagen, und Weiterleitungen werden nicht verfolgt.
  • Der Timeout beträgt 10 Sekunden je Zustellung. Antworten Sie schnell und erledigen Sie die Arbeit danach.
  • Perstat stellt jedes Ereignis einmal zu.
  • Das Ergebnis der letzten Zustellung, mit Fehlertext, falls es einen gab, steht am Connector unter Integrationen und neben der Stufe in der Kette.

Halten Sie die Gegenstelle erreichbar, und gleichen Sie verpasste Ereignisse über die REST-API ab: Lesen Sie einen Incident über seine incident.id, oder listen Sie mit incidents:read die offenen Incidents der Organisation auf.