Etminan measures every monitored host from the kernel, seals the result in a TPM, and hands the verdict to a separate device that the host can never reach. If root is compromised, it can refuse to answer — it cannot lie and get away with it.
AIDE, Tripwire, and auditd-based integrity checks all share the same weak point: the checksum database and the alarm live on the box being watched.
Under a full-root-compromise threat model, that's fatal. A userspace agent alone can't be trusted here — root can patch the hashing binary in memory, edit the baseline it compares against, or simply lie about the result. The alarm never fires, because the thing that would fire it is the thing that got compromised.
Etminan is built to answer that threat model directly, not to catch a wider net of generic issues. Measurements have to come from somewhere root cannot forge after the fact — that means the kernel's IMA subsystem extending a TPM PCR, and a signed, remotely-verified quote checked by a device the compromised host never touches.
The monitored host never judges itself. Every decision — pass, fail, or "needs a human" — is made on hardware the host cannot reach.
Linux IMA measures file executions and reads under policy and extends the results into TPM PCR 10 — a one-way operation even root cannot rewrite after the fact.
The verifier pulls a signed TPM2_Quote on its own schedule,
replays the host's IMA log, and confirms it matches the signed digest. A
compromised host can stay silent — it cannot fabricate a validly signed quote.
Any change from the last approved baseline lands in review. It only becomes trusted after an explicit, signed operator approval with a required reason.
Two things every operator sees: the plain-language verdict on a pending change, and exactly what fires when something's actually wrong.
Every new or changed measurement lands here first — a plain-language read before the raw hash-level detail, so a generalist operator can act on the first line.
A genuine finding never waits for a human to notice — it's printed, signed into the audit log, and pushed to every configured channel in the same pass.
A single host is never both — the verifier's trust value comes from being administratively separate from the hosts it checks.
A relay, not a trust anchor. On request, it sweeps in-scope paths, requests a signed TPM quote, and returns it with the IMA log delta.
No TPM required — quote verification is pure offline signature math against the enrolled AK public key. Not just no runtime device access: zero TPM library dependency in the shipped binary, which cross-compiles clean to a static musl target. The only place an alarm ever fires from.
--reason,
folded into the tamper-evident batch payload.Two shapes, chosen deliberately per integration. Package-manager checks are an in-process Rust trait — a closed, well-known set of package managers. Change-ticket systems and alert channels are external plugins under the shared Etminan Plugin API — nobody is switching ticket systems or paging tools for an attestation tool, so the verifier ships zero in-process HTTP clients for either and instead runs a separately-vetted, allowlisted, SHA-256-pinned script per integration.
Does an installed package own the changed path? Checked against dpkg/rpm on a dedicated, non-quote-taking connection.
baseline review.PackageChecker trait — apk/pacman are a new
impl away.Was this change actually planned? The verifier runs every enabled plugin and shows every confirmation it finds — never just the first match.
Beyond the self-contained, hash-chained audit log and email: PagerDuty, Slack, and a generic webhook ship as reference plugins under the same Plugin API as change-source correlation.
etminan-verifier plugins verify checks every configured plugin
on demand, and run does the same automatically every
cycle, turning it into an ordinary warning-severity finding.The short path below covers the common case. For profiles, package-manager plugins, and day-to-day review, see the full user guide.
The postinst step creates the etminan-agent (group
tss) and etminan-verifier
system users and their config/state directories — but enables nothing. You choose
which role(s) this host plays.
# install the package sudo dpkg -i etminan_<version>_amd64.deb # monitored host — needs a TPM sudo systemctl enable --now etminan-agent-ima-policy.service etminan-agent.service # verifier device sudo systemctl enable --now etminan-verifier.timer
Copy deploy/agent.env.example to
/etc/etminan-agent/agent.env. There is
no baked-in default for watched paths — an
agent started with neither setting watches nothing and warns loudly on startup, rather
than silently assuming another project's file list.
# listen address for quote requests from the verifier ETMINAN_AGENT_LISTEN=0.0.0.0:7620 # pick ONE: a named profile from profiles.conf, or an inline override ETMINAN_PROFILE=fds-host #ETMINAN_WATCHED_PATHS=/usr/local/bin/myapp,/etc/myapp
Copy deploy/verifier.env.example to
/etc/etminan-verifier/verifier.env and point it at where
enrollment state and the baseline database should live.
ETMINAN_VERIFIER_STATE=/var/lib/etminan-verifier/state.json ETMINAN_BASELINE_DB=/var/lib/etminan-verifier/baseline.db ETMINAN_NOTIFY_EMAIL=security@example.com # unset disables the email alarm leg
Two independent key pairs: an operator signing key (makes every baseline approval tamper-evident) and a per-device mTLS identity (the agent↔verifier channel is mandatory mTLS, no plaintext fallback).
# on the verifier — operator signing key, keep it safe etminan-verifier keygen --out /etc/etminan-verifier/operator.key # on both the agent and the verifier — mTLS identity etminan-agent keygen-tls etminan-verifier keygen-tls
From the verifier, for each monitored host. This takes a first quote and prints the AK fingerprint — confirm it out-of-band (e.g. read it off the host's console) before trusting it. It's the one trust-on-first-use moment in the whole design.
etminan-verifier enroll --host web-01 --addr 10.0.0.5:7620
Every measurement from the first quote lands as a pending candidate, never auto-trusted. Review it once, then approve as a single signed batch with a reason.
etminan-verifier baseline review --host web-01 etminan-verifier baseline approve --host web-01 \ --key /etc/etminan-verifier/operator.key \ --reason "initial baseline for web-01"
Hosted directly from this site for the moment — every release is a GPG-signed tar.gz (plus a .deb where packaging
exists). Always verify the signature before installing; never trust an unsigned
tarball.
etminan-agent + etminan-verifier,
one package, one version.
etminan.deb and signed tar.gzMandatory access control for both binaries — built and scaffolded locally, not yet built into a signed package or dropped in here.
apparmor-etminan-agentapparmor-etminan-verifierNot started yet. Tracked as a follow-on once the AppArmor packages are published and the policy-authoring pass has a go/no-go.
selinux-etminan-agent (planned name)selinux-etminan-verifier (planned name)There's no ticketing system on this site — just email. Questions about deploying Etminan, feedback on the design, or a security report all go to the same place.
✉ Click to reveal & email us
Reporting something security-sensitive? Encrypt it with the same key packages on this
site are signed with — 7387 4214 090F 9137 862D 0AF1 E1E5 41B7 B424 36DF,
fetchable from hkps://keys.openpgp.org (see the Downloads section above).