feat(webhook-relay): read the alerts vendors push to riposte-relay (1.0.0)

Riposte accepts no inbound connection, so a vendor that can only push
(Rapid7 InsightIDR Essential basic detection rules) posts to the relay,
and an alert rule on this integration reads one relay source back over
outbound HTTPS.

fetch_messages takes the rule's watermark as `since` and walks every
page within the run, since the next watermark is this run's instant. A
window holding more than `limit` fails the run instead of truncating, so
the watermark stays put. relay_status and test_connection tell a 404
from the proxy (this Riposte's address is not listed) from a refused
token. The generic mapper keeps the raw body, the sender's address and
the checks the delivery passed (custom:webhook_verified).

Tested against a local relay: 2503 messages over three pages, a
millisecond watermark, the limit, unknown source and bad token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xa1QDbsPTYiVREv2Cvs3Zn
This commit is contained in:
2026-10-16 11:23:03 +00:00
parent ff461464fd
commit 6b76270cc6
6 changed files with 230 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
id: webhook_relay
name: Webhook Relay
version: 1.0.0
description: "Reads the alerts that vendors pushed to riposte-relay, the public mailbox for alert webhooks (riposte/relay/). Riposte accepts no inbound connection, so a source whose only output is a webhook — Rapid7 InsightIDR Essential basic detection rules, for one — posts to the relay, and an alert rule on this integration reads one relay source back over outbound HTTPS. The relay knows no vendor: the rule chooses the client, the alert type and the mapper. A read consumes nothing; the rule's watermark is the read's `since`, so after a failover the standby reads the same mailbox from its own watermark."
changelog: "1.0.0 — Initial release: fetch_messages (ingestion: one alert per relayed message, `since` watermark, page walk), relay_status, test_connection, and a generic mapper that keeps the raw body and the checks the message passed."
category: ingestion
config_schema:
properties:
base_url:
type: string
description: "The relay's public address, e.g. https://hermod.acknowledge.tools"
read_token:
type: string
description: "The relay's consumer token. The relay holds only its SHA-256 (RELAY_CONSUMER_TOKEN_SHA256); this Riposte must also call from an address in RELAY_CONSUMER_ALLOW_IPS."
x-soar-sensitive: true
required:
- base_url
- read_token
commands:
- id: fetch_messages
name: webhook-relay-fetch-messages
description: "Messages a relay source received since a watermark, oldest first, every page followed. Ingestion: results path = messages, one alert per message, dedup on the relay's message id. Each message carries source, received_at, remote_ip, headers, verified (the checks it passed: path_token, header_token, hmac, ip), body (or body_b64) and json when the body parses. Reading consumes nothing."
risk: read
inputs_schema:
properties:
source:
type: string
description: "Relay source id, e.g. r7-acme — the mailbox to read. The client this alert rule is bound to must be the one this source was made for."
since:
type: string
description: "Lower bound on received_at: ISO-8601, UNIX seconds or milliseconds. Incremental fetch watermark."
limit:
type: number
description: "Most messages one run may gather (default 10000, the relay's own per-source cap). A window holding more fails the run rather than dropping the rest: the watermark stays put and the next run reads it again."
required: [source]
outputs_schema: { properties: {} }
ingest:
results_path: messages
dedup_key: id
incremental_field: since
- id: relay_status
name: webhook-relay-status
description: "Per relay source: messages waiting, oldest and newest, accepted and rejected counts by reason (ip, path_token, header_token, hmac, size, rate, full…), last accepted, last read. Counters restart with the relay. A rising rejected count on a source is a misconfigured vendor — or someone trying."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }
- id: test_connection
name: webhook-relay-test-connection
description: "Verify the relay answers this Riposte and the read token (used by the Test button)."
risk: read
inputs_schema:
properties: {}
required: []
outputs_schema: { properties: {} }