Skip to content

SIEM output

Emitting every integrity finding and every signed operator action as CEF or LEEF over syslog, for Splunk, QRadar, and generic structured-log ingesters.

Enterprise edition

SIEM output is part of the Etminan Enterprise edition. The siem module is compiled only under the enterprise Cargo feature; in the Standard build alarm::emit_siem is a no-op and nothing is emitted. See Editions for the full matrix.

SIEM output is a structured addition for a SOC — one event per finding and per signed operator action — alongside, never instead of, the notification channels. It is off by default: not every deployment runs a SIEM, so nothing is emitted unless it's explicitly switched on.

Consistent with the rest of Etminan: no in-process HTTP client

SIEM output goes over syslog — a local socket or UDP — never an in-process HTTP client. Anything that needs HTTP (Slack, PagerDuty) is an external notify plugin. CEF/LEEF over syslog is the SOC's native ingestion path, so it needs no plugin at all.

Turning it on

One environment variable in /etc/etminan-verifier/verifier.env picks the format and switches it on:

ETMINAN_SIEM_FORMAT=cef            # cef | leef ; unset/empty/other = OFF
ETMINAN_SIEM_SYSLOG_ADDR=siem.internal:514   # optional: UDP to a remote collector
#                                  unset -> local /dev/log; your syslog daemon forwards it
  • cef — ArcSight Common Event Format, parsed by Splunk (and ArcSight).
  • leef — IBM QRadar's Log Event Extended Format, parsed natively by QRadar.
  • json — Etminan's own flat, single-line (newline-delimited) schema for generic structured-log ingesters (Elasticsearch, Loki, Vector, Fluent Bit, a custom forwarder). This project defines and owns the shape; it is deliberately not modelled on any third-party tool's input.

An unrecognized value logs a warning and stays off — it never silently guesses a format.

Transport and severity

Events default to the local syslog datagram socket /dev/log (your syslog daemon forwards them to the central SIEM — the usual enterprise topology), or go as UDP to ETMINAN_SIEM_SYSLOG_ADDR=host:port. The facility is local0. Emission is strictly best-effort — a broken or absent syslog target must never break a run, so send errors are swallowed.

Event severity syslog severity CEF numeric (0–10)
critical err (3) 10
warning warning (4) 5
action / other info (6) 3

The event model

Everything Etminan emits is one of two categories, split on the cat field so your SIEM can route them to different data models.

flowchart LR
    A[run/check finding] -->|alarm::fire → emit_findings| S{ETMINAN_SIEM_FORMAT}
    B[signed operator action] -->|audit_log::append → emit_audit| S
    S -->|cef| C[CEF line over syslog]
    S -->|leef| D[LEEF 2.0 line over syslog]
    C --> T[(local0 / /dev/log or UDP)]
    D --> T
    T --> F[Splunk / QRadar / Elastic / Loki]
Event cat When
finding finding An integrity check result — one per finding from a run/check cycle, fed only the findings new this cycle (transition-based, so no per-cycle re-fire).
operator-action operator-action A signed trust-changing action, taken from the hash-chained audit log. The internal security_finding mirror action is skipped here to avoid double-reporting a finding already emitted.

Format strings

The <version> header field carries the running etminan-verifier version at emit time.

CEF header

CEF:0|Etminan|etminan-verifier|<version>|<sigId>|<name>|<sev 0-10>|<extension>

LEEF 2.0 header (x09 declares the TAB attribute delimiter)

LEEF:2.0|Etminan|etminan-verifier|<version>|<eventId>|x09|<TAB-separated attrs>

Field dictionary

Field names are stable. Signature and payload blobs are deliberately never placed on the wire — they live in the hash-chained audit log, not the SIEM event.

Field (CEF / LEEF) finding operator-action
sigId / eventId the finding kind (pcr-mismatch, template-hash-mismatch, unexpected-pcr-selection, sla-exceeded, pending-overflow, plugin-failure, check-error, audit-chain-invalid, integrity-summary-rejected, …) the audit action (host_enrolled, baseline_approved, baseline_rejected, baseline_exclusion_created/_revoked, operator_key_authorized/_revoked/_rotated, tls_cert_rotated, ak_rotated, profile_assigned, notify_policy_set, …)
cat finding operator-action
dhost / dst monitored host id resource acted on (host id, or a key for registry actions)
cs1 (cs1Label=kind) / kind the finding kind —
suser / usrName — the signing operator key (hex)
act — the audit action
msg the finding text compact k:v, k:v detail summary (blobs dropped)
sev (LEEF) / CEF numeric LEEF sev is the raw word (critical/warning/action); CEF is the numeric mapping above same

Sample events

CEF — finding

CEF:0|Etminan|etminan-verifier|<version>|pcr-mismatch|Host integrity finding|10|dhost=web-01 cat=finding cs1Label=kind cs1=pcr-mismatch msg=quoted PCR10 digest does not match the replayed IMA measurement log

CEF — operator action

CEF:0|Etminan|etminan-verifier|<version>|baseline_approved|Signed operator action|3|suser=3a1f9c2b…e7 cat=operator-action act=baseline_approved dhost=web-01 msg=path:/usr/local/bin/app, reason:scheduled deploy 4821

LEEF — finding (attributes are TAB-separated)

LEEF:2.0|Etminan|etminan-verifier|<version>|pcr-mismatch|x09|cat=finding    dst=web-01  kind=pcr-mismatch   sev=critical    msg=quoted PCR10 digest does not match the replayed IMA measurement log

Injection-safe by construction

Finding text can come straight off an IMA log on a possibly-compromised host. CEF/LEEF header and value fields escape the delimiters and newlines and strip any remaining control byte (ESC/BEL/backspace) so an attacker-controlled field can't inject a forged second syslog line or terminal escape sequences into a SOC's raw view.

Shipped integrations

Both live in integrations/ in the source distribution.

Neither has been run against the product it targets

We do not operate a Splunk or QRadar instance, so nothing here has been installed on one. What is checked, on every build: the event list is generated from the verifier's own emitting code, the Splunk add-on's extraction patterns are compiled and run against real events this verifier produces, and the QRadar event table is compared against that same list — CI fails if any of them disagree. The first deployment with a real instance is where this stops being a draft. Tell us what it took.

The event list

signature_id (CEF) and eventId (LEEF) both carry the same value: a finding kind or an audit action. There are 59 of them. They are listed with names and descriptions in integrations/qradar/etminan-events.csv, which is generated from alarm::kind, audit_log::actions and rbac::Action rather than kept by hand — a new finding kind that nobody adds to it fails the build.

The list is the Enterprise superset. A Standard build never emits the witness and dual-control events; a QID that never fires costs nothing.

Splunk

integrations/splunk/TA-etminan/ is an add-on: sourcetypes etminan:cef and etminan:leef, field extraction for both, and CIM tagging.

cp -r integrations/splunk/TA-etminan $SPLUNK_HOME/etc/apps/
$SPLUNK_HOME/bin/splunk restart

Point an input at the verifier's syslog stream with the matching sourcetype. TA-etminan/samples/ holds four events in each format for Settings → Add Data → Upload, so you can see the extractions before wiring up a live input.

Findings are tagged alert; operator actions are tagged both change and audit, because a signed operator action is both at once. The fields land as signature_id, etminan_category, dest, kind, user, action, severity and message. CEF carries severity as a 0-10 number and LEEF as a word; the add-on maps the number back onto the word, so a search does not have to know which format it is reading.

QRadar

Send LEEF. QRadar parses it natively from the header, and then the only work left is the QID map. CEF also works but needs integrations/qradar/etminan-lsx.xml, a Log Source Extension that exists for sites standardised on CEF across every product.

Import integrations/qradar/etminan-events.csv with qidmap_cli.sh on the console, or paste it into the DSM Editor. Its family column is not part of a QID entry — it tells you which of the three groups a row came from, so findings, operator actions and access decisions can take different low-level categories.

Import one severity for everything

The CSV deliberately has no severity column, and an earlier version of this page was wrong to suggest a table of them. A static severity per event type cannot be correct here: audit-witness-unreachable and check-error are each raised as critical in some circumstances and warning in others, decided where the event is constructed rather than declared with the type. Give every QID the same default and let the event's own severity — the CEF number, the LEEF sev attribute — drive the offense magnitude. That value is on every event and it is the authoritative one.

Deployment checklist

  1. Turn on the producer — set ETMINAN_SIEM_FORMAT (and ETMINAN_SIEM_SYSLOG_ADDR for a remote collector) in the verifier's unit environment, and confirm events reach the SIEM.
  2. Splunk — install TA-etminan, load the sample files through Add Data to confirm the extractions, then set the sourcetype on the live input.
  3. QRadar — add a LEEF Log Source, import the event table as QIDs with one uniform severity, and check that an offense's magnitude follows the event's own severity rather than the QID default.

Where it plugs into the alarm path

SIEM emission is one leg of the verifier's shared channel fan-out. After a run produces findings and the admin notification policy is applied, alarm::dispatch_all calls both the notify channels and emit_siem. The two are independent: the SIEM stream and the audit log always receive findings even when notify plugins are disabled, and the SIEM leg is transition-based (only findings new this cycle), so a SOC gets one structured event per new finding rather than a re-fire every cycle.