Audit webhook forwarding
Mirror every audit row to your SIEM. HMAC-signed.
An optional out-of-band mirror POSTs every audit-log row to a webhook receiver. JSON-per-row body, HMAC-SHA256 signature in X-Z4J-Audit-Signature, full SSRF and DNS-pin protection. The brain's primary HMAC-chained audit log stays the source of truth; the forwarder fans the data out to Splunk HEC, Datadog Logs, Sumo, Elastic, or any HTTPS endpoint. A slow receiver does not slow the brain: a leader-gated background worker delivers rows past a durable cursor, in order, advancing only on a 2xx, so a receiver outage or a brain restart is a delay rather than a gap.
Ships with
- JSON body matches the canonical HMAC-chain form, plus the row_hmac for receiver-side chain verification
- HMAC-SHA256 over the literal request body; same envelope shape as the generic webhook notification channel
- Required HMAC secret with a 32-byte floor enforced at Settings load (unauthenticated mirror would let downstream parsers trust forged rows)
- Same SSRF and DNS-pin guard as notifications: loopback, RFC1918, link-local, cloud metadata, CGNAT, IPv4-mapped IPv6 all blocked
- Durable cursor in the database: at-least-once delivery in chain order, exponential backoff that survives restarts, nothing dropped while the receiver is down
Highlights
- Test suite covers settings validation, payload shape, signature determinism, a receiver down for an hour then recovering with every row in order, 5xx backoff without cursor movement, restart resume, and two replicas under the leader lease
- z4j_audit_forward_lag_rows and z4j_audit_forward_failures_total{reason} plus an admin status endpoint show the backlog and the cursor; the swallowed-exceptions series keeps the existing Grafana alert firing
- Forwarder is constructed only when the URL is set: no worker, no cursor row, no extra import cost on deployments that do not use it
Related