Fleet SIEM Log Forwarding
SigmaShake Fleet's audit event stream can be forwarded to your existing SIEM or data pipeline in near real time. This is an Enterprise feature, configured per-org, and strictly opt-in — no audit data leaves the Fleet perimeter unless a destination is explicitly enabled.
Supported destinations
| Kind | Transport | Auth | Batching |
|---|---|---|---|
splunk | HTTPS POST to Splunk HEC /services/collector/event | Authorization: Splunk <hec_token> | 900 KB chunks, one HEC envelope per event |
generic_webhook | HTTPS POST (JSON body) | HMAC-SHA256 signature header X-SigmaShake-Signature: t=<sec>,v1=<hex> | 1 MB chunks (configurable) |
cribl | HTTPS POST to Cribl HTTP-in source | Authorization: Bearer <token> | 900 KB NDJSON chunks |
confluent_rest | Confluent Cloud Kafka REST v3 streaming produce | HTTP Basic (cluster-scoped Kafka API key) | 900 KB concatenated JSON records, org_id used as partition key |
All adapters support both stream mode (per-event, low-latency) and archive mode (R2-backed content-addressed rollups with a Merkle root for tamper-evidence).
Delivery model
Fleet writes audit events to a dual-mode pipeline:
- Stream path — each event is enqueued to a per-destination Cloudflare Queue and delivered to the SIEM within seconds. Each delivery runs on a 25-second budget with automatic exponential-backoff retry (2^attempt × 30 s, capped at 30 min, plus jitter). Non-retryable errors (4xx except 408/429) surface to the operator via
last_error. - Archive path — every 5 minutes (or whenever a flush threshold is met), batched events are gzipped as NDJSON, written to a content-addressed R2 object (
audit/residency=.../{sha256}.ndjson.gz), and fanned out to each enabled archive destination. Each object carries a Merkle root so downstream consumers can detect tampering.
Deliveries are idempotent at the batch level: a UNIQUE(destination_id, r2_key) constraint on the delivery log ensures an archive batch is never double-delivered even if a worker is retried after acknowledgement failure.
Configuring a destination
Destinations are managed via the Fleet REST API under /api/siem/destinations (enterprise license + siem:admin RBAC required). A destination row has:
kind— one of the adapter kinds abovemode—stream,archive, orbothconfig_json— non-secret adapter config (endpoint, cluster_id, topic, index, …)credentials_ciphertext+credentials_iv— AES-GCM-encrypted credential blob (plaintext is never stored; see Credential handling)redact_fields_json— optional dotted-path list (e.g.["user.email", "token"]) applied to every event before egress
A POST /api/siem/destinations/:id/test endpoint issues a synthetic health_check event to verify connectivity before enabling the destination.
Adapter config shapes
Splunk HEC
{
"kind": "splunk",
"mode": "both",
"config_json": {
"endpoint": "https://hec.example.com",
"index": "sigmashake",
"sourcetype": "sigmashake:audit"
},
// credentials: { "hec_token": "…" }
}
Cribl HTTP-in
{
"kind": "cribl",
"mode": "both",
"config_json": {
"endpoint": "https://in.logstream.<tenant>.cribl.cloud:10080",
"path": "/cribl",
"source_label": "sigmashake-fleet"
},
// credentials: { "bearer_token": "…" }
}
Cribl Cloud ingest ports 443, 8088, 10080, and 10200 are permitted. HEC-compatible Cribl sources can use the splunk adapter instead.
Confluent Cloud Kafka REST v3
{
"kind": "confluent_rest",
"mode": "both",
"config_json": {
"endpoint": "https://pkc-xxxx.us-east-1.aws.confluent.cloud",
"cluster_id": "lkc-abcdef",
"topic": "sigmashake-audit"
},
// credentials: { "api_key": "…", "api_secret": "…" }
}
The credentials must be a cluster-scoped Kafka API key (created via the Confluent Cloud UI under your cluster's "API Keys" tab, or confluent api-key create --resource <lkc-id>), not a Confluent Cloud control-plane API key. Records are keyed by org_id, so all events for a single tenant land on the same Kafka partition and downstream consumers can rely on per-tenant ordering.
The adapter uses Confluent's streaming-produce response format in strict mode: if any per-record error_code in the response is ≥ 400, the whole batch is marked failed (retryable for 429/5xx, operator-fix for other 4xx). This avoids silent data loss on partial failures.
Generic webhook
{
"kind": "generic_webhook",
"mode": "both",
"config_json": {
"endpoint": "https://siem.example.com/ingest",
"max_bytes": 1000000
},
// credentials: { "hmac_secret": "…" }
}
Every request is signed as X-SigmaShake-Signature: t=<unix_seconds>,v1=<hex_hmac_sha256(t + "." + body, secret)>. Receivers should verify the timestamp is within 5 minutes of now and recompute the HMAC before processing.
Credential handling
Credentials are never stored in plaintext:
- The Fleet Worker holds an
SIEM_DEK_MASTERsecret (32-byte AES key, rotatable viaSIEM_DEK_MASTER_V2). - On destination create/update, the credential object is serialized, encrypted with AES-GCM (random 12-byte IV), and only the ciphertext + IV + key version are persisted.
- Decryption happens in-Worker at delivery time. Credentials are never logged and never appear in
last_erroroutput. - Bearer/API tokens pass a CRLF-injection check (
validateHeaderValue) before being placed in anAuthorizationheader.
Network security
All adapters enforce the same egress controls:
- HTTPS only. HTTP endpoints are rejected at config-write time.
- SSRF defence. Endpoint hostnames are validated against private IPv4 ranges (
10/8,172.16/12,192.168/16,127/8,169.254/16) and IPv6 loopback / ULA / link-local ranges.localhostand0.0.0.0are always blocked. - Port allowlists. Splunk/webhook/Confluent are pinned to port
443. The Cribl adapter additionally permits8088/10080/10200for Cribl Cloud ingest. redirect: 'manual'. No adapter follows 3xx redirects — a redirect from the SIEM is treated as a misconfiguration and fails closed.- DNS-rebind re-validation. Each adapter re-runs its endpoint validator at delivery time, not just at config write time.
Observability
Each destination row exposes last_success_at, last_error, and last_error_at. A per-batch fleet_siem_delivery_log row records attempt count, status, Merkle root, destination-specific reference (e.g. HEC ackId, Kafka offset), and timing. Delivery-log rows are auto-purged after 90 days.
Redaction
The optional redact_fields_json array accepts dotted-path field selectors. During delivery, the event payload is deep-copied and matching fields are replaced with the literal string [REDACTED] before the adapter serializes the batch. This runs before chunking, so the outgoing wire format never contains the original value.
Adding a new adapter kind
The adapter model is pluggable. Implementers provide a Destination with validateConfig, deliver, and healthCheck, then register it in src/lib/siem/registry.ts. The forwarder and archiver have zero knowledge of concrete adapter kinds.