Skip to content

SIEM output

Emitting every integrity finding and every signed operator action as CEF or LEEF over syslog, for Splunk, QRadar, and other 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.

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]
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, …) 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, …)
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.

Splunk — normalize to CIM

Ingest as CEF; a dedicated sourcetype = etminan:cef is recommended. Split on cat and normalize findings to Alerts, operator actions to Change (so registry/baseline changes surface in Splunk ES change analysis).

Etminan Splunk CIM field Data model
cat=finding events Alerts (and/or Endpoint integrity)
dhost dest, dvc Alerts / Endpoint
cs1 (kind) signature, category Alerts
CEF severity severity Alerts
msg description Alerts
cat=operator-action events Change
suser user Change
act action / change_type Change
dhost object / dest Change

A few field aliases (dhost→dest, suser→user) plus event tags get you CIM-compliant today; a certified Splunk Technology Add-on is a planned follow-on.

QRadar — map eventId to QID

LEEF is parsed natively — add a Log Source of type LEEF. A DSM / Log-Source Extension assigns each eventId a QID → category + severity so findings raise proper offenses.

Etminan eventId QRadar category (high / low-level) Severity
pcr-mismatch, template-hash-mismatch, unexpected-pcr-selection, audit-chain-invalid Suspicious Activity → System Integrity Violation High (8–10)
operator-rejected Policy → Host-Based Policy Violation High
sla-exceeded, pending-overflow, plugin-failure, check-error Potential Exploit / Policy → Warning Medium
operator-action eventIds (baseline_approved, operator_key_*, *_rotated, …) Audit → Configuration / Authentication Change Low–Medium

Deployment checklist

  1. Turn on the producer — set ETMINAN_SIEM_FORMAT (and ETMINAN_SIEM_SYSLOG_ADDR for a remote collector), and confirm events reach the SIEM.
  2. Splunk — set the sourcetype, apply the field aliases + CIM tags above, validate against the Change / Alerts data models.
  3. QRadar — add the LEEF Log Source; import the QID map / DSM extension when available — generic LEEF parsing works in the meantime.

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, 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 or failing, 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.