Configuration reference¶
Etminan has no configuration-file format of its own. Both binaries are
configured entirely through ETMINAN_* environment variables, plus a small
set of plain-text data files (plugin allowlists, watched-path profiles, the
plugin catalog) whose paths are themselves named by environment variables. In a
packaged install the variables live in the systemd EnvironmentFile=:
| Binary | Env file (packaged) | systemd unit |
|---|---|---|
etminan-agent |
/etc/etminan-agent/agent.env |
etminan-agent.service |
etminan-verifier |
/etc/etminan-verifier/verifier.env |
etminan-verifier.service / .timer |
etminan-portal (Enterprise entitlement service) |
/etc/etminan-portal/portal.env (operator-chosen) |
etminan-portal.service |
Copy the shipped deploy/agent.env.example / deploy/verifier.env.example to
those paths and edit. Comments (#) and blank lines are fine — the files are
sourced by systemd. Everything below is grounded in the code: every default is
the literal fallback in the corresponding std::env::var(...) call site.
Reading the tables
- Default is the exact code default. (unset) means the variable has no default and being unset disables or skips the feature. Unless a row says otherwise, an empty value is treated as unset.
- Numeric and boolean values are validated, not trusted: a malformed value is generally rejected with a startup warning and the default is used (fail-safe), except the dual-control action list, which fails closed — an unrecognised token is a hard error so a typo can never silently leave a control off.
- Rows marked with the Enterprise admonition are only meaningful in a build
made with the
enterpriseCargo feature; in the free Standard binary those variables are read by nothing and setting them has no effect.
etminan-agent¶
The agent is a relay on each monitored host: it returns a TPM-signed quote plus the IMA log delta and makes no trust decision.
Transport & verifier pinning (mutual TLS)¶
mTLS is mandatory — there is no plaintext fallback. See Mutual TLS for the full enrolment handshake.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_AGENT_LISTEN |
0.0.0.0:7620 |
host:port the quote-serving daemon binds. |
ETMINAN_AGENT_LISTEN=0.0.0.0:7620 |
ETMINAN_TLS_DIR |
/var/lib/etminan-agent/tls |
Directory holding this agent's own mTLS cert.pem/key.pem, created once by etminan-agent keygen-tls. |
ETMINAN_TLS_DIR=/var/lib/etminan-agent/tls |
ETMINAN_VERIFIER_CERT_FINGERPRINT |
(unset) | SHA-256 fingerprint (exactly 64 hex chars / 32 bytes) of the one verifier this agent accepts connections from — printed by etminan-verifier keygen-tls. Required in normal operation; unset is only valid together with the permissive flag below. |
ETMINAN_VERIFIER_CERT_FINGERPRINT=9f86d081... |
ETMINAN_ALLOW_PERMISSIVE_TLS |
(unset → off) | 1/true/yes (case-insensitive) opts into enrolment-bootstrap mode: real mTLS but the client cert is not pinned, so any caller is accepted, and only Quote is served (the IMA-log cursor is not advanced; SetProfile/PackageLookup are refused). With the fingerprint unset and this flag also unset, the agent refuses to start — a forgotten pin fails loudly, never silently open. Remove it once enrolled. |
ETMINAN_ALLOW_PERMISSIVE_TLS=1 |
IMA log & policy¶
The log must be the SHA-256 variant, and IMA policy has no path filter (filtering happens on the verifier — every log line carries the full path).
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_IMA_LOG_PATH |
/sys/kernel/security/ima/ascii_runtime_measurements_sha256 |
The SHA-256 IMA measurement-log node (securityfs). The runtime_measurements_count file is derived as its sibling. Override only if securityfs is mounted elsewhere. Must be the _sha256 variant. |
ETMINAN_IMA_LOG_PATH=/sys/kernel/security/ima/ascii_runtime_measurements_sha256 |
ETMINAN_IMA_POLICY_PATH |
/sys/kernel/security/ima/policy |
Where etminan-agent write-ima-policy writes the one-shot boot policy. |
ETMINAN_IMA_POLICY_PATH=/sys/kernel/security/ima/policy |
Watched paths & profiles¶
There is no baked-in default watch set — an agent with neither
ETMINAN_WATCHED_PATHS nor ETMINAN_PROFILE watches nothing and prints a
startup warning. Resolution precedence (highest first): ETMINAN_WATCHED_PATHS
→ a centrally-assigned profile (op assign-profile) → ETMINAN_PROFILE.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_WATCHED_PATHS |
(unset) | Comma-separated absolute paths to sweep/measure before every attestation cycle. Direct override — takes precedence over ETMINAN_PROFILE, no profile file involved. |
ETMINAN_WATCHED_PATHS=/usr/local/bin/fds-proxy,/etc/systemd/system,/etc/fds |
ETMINAN_PROFILE |
(unset) | Name of a [section] in profiles.conf whose path list is used. The bootstrap tier — a name pushed by op assign-profile overrides it. |
ETMINAN_PROFILE=fds-host |
ETMINAN_PROFILES_PATH |
/etc/etminan-agent/profiles.conf |
Location of the named-profile definitions file (see profiles.conf). | ETMINAN_PROFILES_PATH=/etc/etminan-agent/profiles.conf |
ETMINAN_ASSIGNED_PROFILE_PATH |
/var/lib/etminan-agent/assigned_profile |
Where a profile name pushed by op assign-profile is persisted across restarts (only the selected name is stored/sent — the path list always stays in profiles.conf). Relocate only if /var/lib/etminan-agent is not writable. |
ETMINAN_ASSIGNED_PROFILE_PATH=/var/lib/etminan-agent/assigned_profile |
etminan-verifier¶
The verifier makes every trust decision. It needs no TPM. Store/identity paths
below already match the systemd unit's WorkingDirectory and the AppArmor
profile — only change them if you need a different location.
Store, state & identity¶
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_BASELINE_DB |
/var/lib/etminan-verifier/baseline.db |
The SQLite baseline + hash-chained audit-log store (its .audit-head anchor sits alongside). Managed entirely by the tool — see baseline.db. |
ETMINAN_BASELINE_DB=/var/lib/etminan-verifier/baseline.db |
ETMINAN_VERIFIER_STATE |
/var/lib/etminan-verifier/state.json |
Per-host enrolment record + cumulative IMA/PCR-10 replay cursor — see state.json. | ETMINAN_VERIFIER_STATE=/var/lib/etminan-verifier/state.json |
ETMINAN_TLS_DIR |
/var/lib/etminan-verifier/tls |
This verifier's own mTLS identity (etminan-verifier keygen-tls); its fingerprint is what you pin into each agent's ETMINAN_VERIFIER_CERT_FINGERPRINT. |
ETMINAN_TLS_DIR=/var/lib/etminan-verifier/tls |
The daemon (etminan-verifierd)¶
The privilege-separated daemon owns verifier state and the single signing key; the op client reaches it over the socket. These are the daemon's own paths, distinct from the CLI store vars above.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_VERIFIERD_SOCKET |
/run/etminan-verifierd/etminan-verifierd.sock |
Unix socket the daemon listens on and the op client connects to (0660 etminan-verifier:etminan-verifier). Override only for tests / non-root dev runs. |
ETMINAN_VERIFIERD_SOCKET=/run/etminan-verifierd/etminan-verifierd.sock |
ETMINAN_VERIFIERD_DB |
/var/lib/etminan-verifier/baseline.db |
The baseline + audit store the daemon owns — the same file the CLI reads via ETMINAN_BASELINE_DB. |
ETMINAN_VERIFIERD_DB=/var/lib/etminan-verifier/baseline.db |
ETMINAN_VERIFIERD_KEY |
/var/lib/etminan-verifier/daemon-signing.key |
The single Ed25519 key the daemon holds and signs every operator action with. Must be owned by the daemon user (0600); operators hold no key. |
ETMINAN_VERIFIERD_KEY=/var/lib/etminan-verifier/daemon-signing.key |
ETMINAN_TOTP_SESSION_TTL_SECS |
28800 (8h) |
Lifetime of a TOTP op login session before re-authentication is required. |
ETMINAN_TOTP_SESSION_TTL_SECS=28800 |
ETMINAN_VERIFIERD_ALLOW_INSECURE_BOOTSTRAP |
(unset) | DEV/TEST ONLY. Permits the first-admin op bootstrap without root. Never set in production. |
ETMINAN_VERIFIERD_ALLOW_INSECURE_BOOTSTRAP=1 |
Attestation-loop tuning¶
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_MAX_CATCHING_UP_CYCLES |
6 |
After this many consecutive CatchingUp cycles a host escalates to a stale-attestation alarm (backstop against a host that never converges or feeds a fabricated log_truncated delta). 0 disables the backstop (not recommended). Malformed → default. |
ETMINAN_MAX_CATCHING_UP_CYCLES=6 |
ETMINAN_PENDING_REVIEW_SLA_HOURS |
24 |
Hours a pending_review item may sit before run/baseline review escalate it to a warning finding and mark it [SLA EXCEEDED]. Must be a positive integer — zero/negative/non-numeric is refused with a loud warning and falls back to 24. |
ETMINAN_PENDING_REVIEW_SLA_HOURS=72 |
ETMINAN_TEST_SIMULATE_REPLAY |
(unset) | Test harness only. When set, the check path substitutes a replayed nonce to exercise anti-replay rejection. Never set in production. | ETMINAN_TEST_SIMULATE_REPLAY=1 |
Notifications — email¶
The only place a finding leaves the box. See Notifications.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_NOTIFY_EMAIL |
(unset → email leg off) | Destination address for alarm email (delivered via local sendmail). Unset disables only the email leg. |
ETMINAN_NOTIFY_EMAIL=security@example.com |
ETMINAN_NOTIFY_FROM |
noreply@etminan |
From: address for alarm email. |
ETMINAN_NOTIFY_FROM=etminan@example.com |
ETMINAN_NOTIFY_EMAIL_SEVERITIES |
(unset → all) | Comma-separated severity allowlist for the email leg (critical, warning, info). Unset = every severity. |
ETMINAN_NOTIFY_EMAIL_SEVERITIES=critical,warning |
ETMINAN_NOTIFY_EMAIL_FORMAT |
[{kind}] {host_id}: {text} |
Per-finding line template for the email body. Placeholders: {kind} {host_id} {text} {severity}. |
ETMINAN_NOTIFY_EMAIL_FORMAT=[{severity}] {host_id} {kind}: {text} |
ETMINAN_NOTIFY_EMAIL_TEMPLATE |
(unset → built-in layout) | Path to a subject/body template file overriding the default email layout (body must contain the findings placeholder). Missing/unreadable → warning + built-in layout, never a dropped alert. | ETMINAN_NOTIFY_EMAIL_TEMPLATE=/etc/etminan-verifier/email.tmpl |
Notifications — plugin channels¶
Extra channels (PagerDuty, Slack, generic webhook, or any customer script) are external, allowlisted, SHA-256-pinned scripts — the verifier has no in-process HTTP client. See notify-plugins.conf.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_NOTIFY_CHANNELS |
(unset → off) | Comma-separated channel names to enable. Each name must also have a line in the notify allowlist below, or it is skipped with a warning. | ETMINAN_NOTIFY_CHANNELS=pagerduty,slack |
ETMINAN_NOTIFY_PLUGINS_CONF |
/etc/etminan-verifier/notify-plugins.conf |
Path to the notify-plugin allowlist file. | ETMINAN_NOTIFY_PLUGINS_CONF=/etc/etminan-verifier/notify-plugins.conf |
ETMINAN_NOTIFY_<CHANNEL>_SEVERITIES |
(unset → all) | Dynamic per-channel severity filter — <CHANNEL> is the channel name upper-cased. Comma-separated critical/warning/info. Unset = every severity. |
ETMINAN_NOTIFY_PAGERDUTY_SEVERITIES=critical |
ETMINAN_NOTIFY_<CHANNEL>_FORMAT |
[{kind}] {host_id}: {text} |
Dynamic per-channel line template (same placeholders as the email format), overriding the channel's built-in formatted field with no plugin re-hash. |
ETMINAN_NOTIFY_SLACK_FORMAT={severity}: {host_id} {text} |
Concrete <CHANNEL> instances
For the shipped reference plugins these expand to
ETMINAN_NOTIFY_PAGERDUTY_SEVERITIES, ETMINAN_NOTIFY_SLACK_SEVERITIES,
ETMINAN_NOTIFY_WEBHOOK_SEVERITIES, and the matching _FORMAT triplet. Any
channel name you invent gets its own pair automatically.
SIEM output (CEF / LEEF over syslog)¶
Enterprise edition
The free Standard binary contains no SIEM code — these variables are read by nothing and setting them has no effect (and emits no warning). See SIEM output.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_SIEM_FORMAT |
(unset → off) | The on switch and format selector: cef or leef (case-insensitive). Empty/unset/any other value = OFF (an unrecognised value warns and stays off). When on, one syslog event is emitted per finding and per signed operator action. |
ETMINAN_SIEM_FORMAT=cef |
ETMINAN_SIEM_SYSLOG_ADDR |
(unset → local /dev/log) |
Set host:port to send UDP to a remote collector instead of the local syslog socket. Facility is local0; severity maps critical→err, warning→warning, operator-action→info. |
ETMINAN_SIEM_SYSLOG_ADDR=siem-collector.internal:514 |
Change-source correlation (inbound)¶
Informational only — never gates or auto-approves anything. External, allowlisted, SHA-256-pinned plugins. See Change sources.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_CHANGE_SOURCES |
(unset → off) | Comma-separated change-source names to enable; each needs a line in the allowlist below or it is skipped with a warning. | ETMINAN_CHANGE_SOURCES=request-tracker |
ETMINAN_CHANGE_SOURCE_PLUGINS_CONF |
/etc/etminan-verifier/change-source-plugins.conf |
Path to the change-source allowlist file. | ETMINAN_CHANGE_SOURCE_PLUGINS_CONF=/etc/etminan-verifier/change-source-plugins.conf |
Dual control / four-eyes¶
Enterprise edition
In the free Standard binary the four-eyes gate is compiled out and
dual-control is an unknown subcommand — a single signature still applies.
See Dual control.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_DUAL_CONTROL_ACTIONS |
(unset → off) | The env floor for which actions require a second co-signer; off/none/empty = off. The only supported token is baseline-approve (default expands to it). An unknown token is a hard error — never a silent single-signed bypass. The signed op dual-control-policy set --actions baseline-approve [--threshold N] --reason <r> latch (itself four-eyes) is authoritative over this env floor. |
ETMINAN_DUAL_CONTROL_ACTIONS=baseline-approve |
ETMINAN_DUAL_CONTROL_TTL_HOURS |
168 |
Hours a pending request stays open before it expires (7 days). Non-positive/malformed → default. | ETMINAN_DUAL_CONTROL_TTL_HOURS=72 |
ETMINAN_DUAL_CONTROL_THRESHOLD |
2 |
Number of distinct approvers required (M-of-N). Clamped to a minimum of 2 (below that isn't four-eyes). Captured per request at creation, so a later change doesn't affect open ones. | ETMINAN_DUAL_CONTROL_THRESHOLD=3 |
RBAC has no environment switch
RBAC is not configured through an environment variable. Role-based access
control is enforced by the etminan-verifierd daemon itself: it authenticates
each operator by kernel peer-UID and applies the caller's role
(admin / operator / viewer) and host-group scope, default-deny. It is a
Standard capability (not gated by the enterprise feature). See
RBAC.
Plugin catalog & sandbox¶
The optional certified-plugin catalog (plugins list/install/update), a
convenience layer over the manual allowlists.
| Variable | Default | Meaning & allowed values | Example |
|---|---|---|---|
ETMINAN_PLUGIN_CATALOG_URL |
https://etminan.dev/downloads/plugins-catalog.json |
Catalog manifest URL (see manifest.json). Point at a private/offline mirror; relative entry URLs resolve against it. | ETMINAN_PLUGIN_CATALOG_URL=https://mirror.internal/etminan/plugins-catalog.json |
ETMINAN_PLUGIN_INSTALL_DIR |
/etc/etminan-verifier |
Install root — downloaded plugins land in <this>/<type>-plugins/<filename>. |
ETMINAN_PLUGIN_INSTALL_DIR=/etc/etminan-verifier |
ETMINAN_PLUGIN_USER |
etminan-verifier-plugin |
Unprivileged user every vetted plugin subprocess is dropped to (setgroups→setgid→setuid) so a compromised plugin can't read baseline.db or the operator key. Set explicitly empty to opt out of the drop (plugins then run as the verifier's own user). A named user that doesn't exist → warning. |
ETMINAN_PLUGIN_USER=etminan-verifier-plugin |
| --- |
Plugin-process environment (passthrough)¶
The verifier binary never reads these — they are inherited by the plugin
subprocess from the same env file (systemd EnvironmentFile=). The verifier
clears the environment before exec and passes each plugin only the ETMINAN_*
namespace plus basic system vars, so an unrelated secret elsewhere in the file
never reaches a plugin. A plugin you write yourself may need entirely different
variables — check its own documentation. The values below are what the shipped
reference plugins expect. Protect the env file's permissions
(root-readable-only); the plaintext-token posture is the same as the verifier's
own TLS/operator private keys.
Notify reference plugins¶
| Variable | Consumed by | Meaning | Example |
|---|---|---|---|
ETMINAN_PAGERDUTY_ROUTING_KEY |
notify-plugins/pagerduty.sh |
PagerDuty Events API v2 routing key. | ETMINAN_PAGERDUTY_ROUTING_KEY=R0ABCD... |
ETMINAN_SLACK_WEBHOOK_URL |
notify-plugins/slack.sh |
Slack incoming-webhook URL. | ETMINAN_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T000/B000/xxxx |
ETMINAN_WEBHOOK_URL |
notify-plugins/webhook.sh |
Generic JSON webhook endpoint. | ETMINAN_WEBHOOK_URL=https://example.com/hooks/etminan |
ETMINAN_WEBHOOK_AUTH_HEADER |
notify-plugins/webhook.sh |
Optional auth header sent verbatim. | ETMINAN_WEBHOOK_AUTH_HEADER=Authorization: Bearer changeme |
Change-source reference plugins¶
| Variable | Consumed by | Meaning | Example |
|---|---|---|---|
ETMINAN_RT_BASE_URL |
change-sources/request-tracker.sh |
Request Tracker base URL. | ETMINAN_RT_BASE_URL=https://rt.example.com |
ETMINAN_RT_API_TOKEN |
RT plugin | RT 5+ API token (sent as Authorization: token <value>). |
ETMINAN_RT_API_TOKEN=1-234-abcd... |
ETMINAN_RT_HOST_FIELD |
RT plugin | Optional RT custom-field name to also search; unset → Subject-only search. | ETMINAN_RT_HOST_FIELD=Host |
ETMINAN_RT_WINDOW_HOURS |
RT plugin | Hours before/after a pending entry's first-seen time that count as "near" (default 24 in the plugin). | ETMINAN_RT_WINDOW_HOURS=48 |
ETMINAN_RT_DEBUG_QUERY |
RT plugin | Any value → print the constructed TicketSQL to the plugin's stderr. | ETMINAN_RT_DEBUG_QUERY=1 |
ETMINAN_FREEITSM_BASE_URL |
change-sources/freeitsm.sh |
FreeITSM base install URL (API at <this>/api/v1/...). |
ETMINAN_FREEITSM_BASE_URL=https://itsm.example.com |
ETMINAN_FREEITSM_API_TOKEN |
FreeITSM plugin | Token (sent as Authorization: Bearer <token>). |
ETMINAN_FREEITSM_API_TOKEN=fitsm_changeme |
ETMINAN_FREEITSM_COMPANY_ID |
FreeITSM plugin | Optional tenant/company restriction on a multi-tenant install. | ETMINAN_FREEITSM_COMPANY_ID=1 |
ETMINAN_FREEITSM_WINDOW_HOURS |
FreeITSM plugin | Same window semantics as ETMINAN_RT_WINDOW_HOURS. |
ETMINAN_FREEITSM_WINDOW_HOURS=24 |
ETMINAN_FREEITSM_DEBUG_QUERY |
FreeITSM plugin | Any value → print the constructed query params to stderr. | ETMINAN_FREEITSM_DEBUG_QUERY=1 |
Not a real variable
ETMINAN_SECRET shows up if you grep the source, but it is not runtime
configuration — it only exists as a substring in a passthrough-filter unit
test.
Configuration files¶
agent.env¶
The complete, annotated agent env file — copy to /etc/etminan-agent/agent.env.
Everything is commented because every value has a code default; uncomment only
what you override, but you must set either a pinned fingerprint or the
permissive bootstrap flag, and either a profile or a watched-path list.
# /etc/etminan-agent/agent.env — sourced by etminan-agent.service
# --- Transport ---------------------------------------------------------------
#ETMINAN_AGENT_LISTEN=0.0.0.0:7620
#ETMINAN_TLS_DIR=/var/lib/etminan-agent/tls
# Pin the one verifier this agent trusts (printed by `etminan-verifier
# keygen-tls`). REQUIRED in normal operation.
ETMINAN_VERIFIER_CERT_FINGERPRINT=<64-hex-char sha256 from etminan-verifier keygen-tls>
# First-enrolment bootstrap ONLY: accept any client cert (still real mTLS,
# just unpinned; serves only Quote). Leave the fingerprint UNSET while this is
# on, then pin it and REMOVE this line. With neither set, the agent refuses to
# start.
#ETMINAN_ALLOW_PERMISSIVE_TLS=1
# --- TPM: no override — the agent always uses the resource-managed /dev/tpmrm0 --
# --- IMA (defaults correct on a standard kernel; log MUST be the sha256 file) -
#ETMINAN_IMA_LOG_PATH=/sys/kernel/security/ima/ascii_runtime_measurements_sha256
#ETMINAN_IMA_POLICY_PATH=/sys/kernel/security/ima/policy
# --- Watched paths (pick ONE approach; there is NO default watch set) --------
# A) named profile from profiles.conf:
ETMINAN_PROFILE=fds-host
#ETMINAN_PROFILES_PATH=/etc/etminan-agent/profiles.conf
#ETMINAN_ASSIGNED_PROFILE_PATH=/var/lib/etminan-agent/assigned_profile
# B) direct override (takes precedence over ETMINAN_PROFILE):
#ETMINAN_WATCHED_PATHS=/usr/local/bin/fds-proxy,/etc/systemd/system,/etc/fds
verifier.env¶
The complete, annotated verifier env file — copy to
/etc/etminan-verifier/verifier.env. The shipped deploy/verifier.env.example
is the authoritative long-form; below is a working subset covering the common
Standard install plus the Enterprise switches.
# /etc/etminan-verifier/verifier.env — sourced by etminan-verifier.service/.timer
# --- Store, state & identity (defaults match the systemd WorkingDirectory) ---
#ETMINAN_BASELINE_DB=/var/lib/etminan-verifier/baseline.db
#ETMINAN_VERIFIER_STATE=/var/lib/etminan-verifier/state.json
#ETMINAN_TLS_DIR=/var/lib/etminan-verifier/tls
# --- Attestation-loop tuning -------------------------------------------------
#ETMINAN_CHECK_CONCURRENCY=32
#ETMINAN_MAX_CATCHING_UP_CYCLES=6
#ETMINAN_PENDING_REVIEW_SLA_HOURS=24
# --- Notifications: master switch + email ------------------------------------
#ETMINAN_NOTIFY=on
#ETMINAN_NOTIFY_SUPPRESS_KINDS=sla-exceeded
ETMINAN_NOTIFY_EMAIL=security@example.com
#ETMINAN_NOTIFY_FROM=noreply@etminan
#ETMINAN_NOTIFY_EMAIL_SEVERITIES=critical,warning
#ETMINAN_NOTIFY_EMAIL_FORMAT=[{kind}] {host_id}: {text}
#ETMINAN_NOTIFY_EMAIL_TEMPLATE=/etc/etminan-verifier/email.tmpl
# --- Notifications: plugin channels ------------------------------------------
#ETMINAN_NOTIFY_CHANNELS=pagerduty,slack
#ETMINAN_NOTIFY_PLUGINS_CONF=/etc/etminan-verifier/notify-plugins.conf
#ETMINAN_NOTIFY_PAGERDUTY_SEVERITIES=critical
#ETMINAN_NOTIFY_SLACK_SEVERITIES=critical,warning
# Consumed by the plugin processes themselves (not the verifier binary):
#ETMINAN_PAGERDUTY_ROUTING_KEY=changeme
#ETMINAN_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T000/B000/xxxx
#ETMINAN_WEBHOOK_URL=https://example.com/hooks/etminan
#ETMINAN_WEBHOOK_AUTH_HEADER=Authorization: Bearer changeme
# --- SIEM (Enterprise; off unless a format is set; cef or leef) --------------
#ETMINAN_SIEM_FORMAT=cef
#ETMINAN_SIEM_SYSLOG_ADDR=siem-collector.internal:514
# --- Change-source correlation (inbound, informational only) -----------------
#ETMINAN_CHANGE_SOURCES=request-tracker
#ETMINAN_CHANGE_SOURCE_PLUGINS_CONF=/etc/etminan-verifier/change-source-plugins.conf
#ETMINAN_RT_BASE_URL=https://rt.example.com
#ETMINAN_RT_API_TOKEN=changeme
# --- Dual control / four-eyes (Enterprise) -----------------------------------
# Env floor; the signed `op dual-control-policy set` latch is authoritative.
# Only supported token: baseline-approve.
#ETMINAN_DUAL_CONTROL_ACTIONS=baseline-approve
#ETMINAN_DUAL_CONTROL_TTL_HOURS=168
#ETMINAN_DUAL_CONTROL_THRESHOLD=2
# --- Plugin catalog & sandbox ------------------------------------------------
#ETMINAN_PLUGIN_CATALOG_URL=https://etminan.dev/downloads/plugins-catalog.json
#ETMINAN_PLUGIN_INSTALL_DIR=/etc/etminan-verifier
#ETMINAN_PLUGIN_USER=etminan-verifier-plugin
#ETMINAN_PLUGIN_ALLOW_REDIRECTS=1
baseline.db¶
ETMINAN_BASELINE_DB (default /var/lib/etminan-verifier/baseline.db) is a
SQLite database managed entirely by etminan-verifier — never hand-edit
it. It holds:
- the approved baseline (
approved_measurements, keyed byhost_id) — the(path, sha256)set each host is trusted to report; - pending review items (drift proposed, never auto-accepted);
- the hash-chained, signed audit log of every trust-changing action, whose
tamper-evident head is anchored in a sibling
.audit-headfile; - the legacy operator-key registry (retained read-only for
verify-signatures), and (Enterprise) the dual-control pending requests and the signed dual-control policy where applicable.
Back it up as a unit with its .audit-head anchor. Editing it directly breaks
the audit chain and is itself detectable.
state.json¶
ETMINAN_VERIFIER_STATE (default /var/lib/etminan-verifier/state.json) is a
JSON file, also tool-managed, mapping each host_id to its enrolment record
(pinned AK fingerprint, verifier-cert pin, assigned profile name) and the
cumulative IMA / PCR-10 replay cursor used to bind the next log delta to the
previous state. It is written atomically under an exclusive file lock across the
whole load-modify-save cycle. Do not hand-edit while the verifier runs.
The plugin allowlist files¶
Two separate allowlists, one per plugin category, each named by its own
*_PLUGINS_CONF variable. Keeping them separate means allowlisting one category
can never be confused with another. Both share the exact same format and
the exact same enforcement: a plugin runs only if it has a line here and
its file is root-owned and not group/world-writable and its exact byte
content matches the pinned SHA-256 — all re-checked fresh before every single
execution, never a directory scan.
| File (default path) | Variable | Names matched against |
|---|---|---|
/etc/etminan-verifier/notify-plugins.conf |
ETMINAN_NOTIFY_PLUGINS_CONF |
ETMINAN_NOTIFY_CHANNELS |
/etc/etminan-verifier/change-source-plugins.conf |
ETMINAN_CHANGE_SOURCE_PLUGINS_CONF |
ETMINAN_CHANGE_SOURCES |
Format: one plugin per line, three whitespace-separated fields; # comments
and blank lines ignored.
# <name> <absolute-path-to-executable> <sha256_hex>
pagerduty /etc/etminan-verifier/notify-plugins/pagerduty.sh c4f440fd72dd90390022215201b49eda32fbb82bda74885902cd3fe7fc3382e7
slack /etc/etminan-verifier/notify-plugins/slack.sh 8dc10f47d8598059339b5a3545896b4e5c91f4ea8513a33fdfb3384d5bd49b96
<name>— must match an entry in the corresponding enable variable.<path>— absolute path to the plugin executable.<sha256_hex>— 64 hex chars; recompute after every edit:sha256sum /path/to/plugin.
The hashes above only match the reference plugins as shipped in this release — recompute them if you copy or modify a plugin.
profiles.conf¶
ETMINAN_PROFILES_PATH (default /etc/etminan-agent/profiles.conf) defines
named watched-path sets so many hosts can share one reusable list. A host selects
one via ETMINAN_PROFILE (or centrally via op assign-profile).
An ETMINAN_ASSIGNED_PROFILE_PATH file records the last centrally-pushed name.
# /etc/etminan-agent/profiles.conf
[fds-host]
/usr/local/bin/fds-proxy
/usr/local/bin/fds-admin
/usr/local/bin/fds-portal
/usr/local/bin/fds-shell
/etc/systemd/system
/etc/fds
[generic-host]
/usr/local/bin/myapp
/etc/myapp
Two parsing behaviours to know:
- A repeated
[name]section is additive, not "last wins" —[a]/one[a]/twoyieldsa → [/one, /two]. To replace a profile, delete the old section rather than appending a second with the same name. - A path line before the first
[name]header is silently discarded — there is no "default" section; always put a header first.
An empty section ([name] with no paths) is valid and watches nothing.
The plugin catalog manifest (manifest.json)¶
Served at ETMINAN_PLUGIN_CATALOG_URL (default
https://etminan.dev/downloads/plugins-catalog.json) for
etminan-verifier plugins list/install/update. Real JSON has no comments.
The published manifest also requires a detached signature file at the same URL
plus .sig (hex-encoded Ed25519 over the exact bytes) — a dedicated catalog key,
deliberately not the GPG release-signing key.
{
"catalog_version": 1,
"plugins": [
{
"name": "pagerduty",
"type": "notify",
"version": "1.0.0",
"sha256": "<sha256 of the plugin file>",
"url": "https://etminan.dev/downloads/plugins/notify/pagerduty-1.0.0.sh",
"description": "PagerDuty Events API v2 notification channel"
},
{
"name": "request-tracker",
"type": "change-source",
"version": "1.0.0",
"sha256": "<sha256 of the plugin file>",
"url": "https://etminan.dev/downloads/plugins/change-source/request-tracker-1.0.0.sh",
"description": "Request Tracker (RT) REST2 API change-ticket correlation"
}
]
}
Each entry's type is one of notify / change-source. install verifies the
downloaded file's SHA-256 against sha256 before writing it to
<ETMINAN_PLUGIN_INSTALL_DIR>/<type>-plugins/; you still add the allowlist line
yourself. A relative url resolves against the catalog URL, so pointing at an
offline mirror relocates the downloads too. In-house plugins you wrote need no
catalog and no signature: just the manual allowlist line.
See also¶
- Mutual TLS
- Notifications · SIEM output · Change sources
- Dual control · RBAC
man etminan-agent,man etminan-verifier— the deeper per-variable references.