Skip to content

Install the verifier

etminan-verifier runs on a separate, independently-administered device and makes every trust decision. It needs no TPM — quote verification is pure offline signature math. This chapter installs it, generates its identity, starts the etminan-verifierd trust daemon that owns every trust-changing action, bootstraps the first admin operator, and enables the hourly attestation timer.

One machine, one role

Do not install the verifier on a machine that also runs etminan-agent. The verifier is your trust root; its value comes from being unreachable by a host compromise. Give it its own box.

Install the package

The verifier binary is portable, and rides the same signed package as the agent. Import the Etminan Team signing key first, then:

gpg --verify etminan_<version>_amd64.deb.asc etminan_<version>_amd64.deb
sha256sum -c etminan_<version>_amd64.deb.sha256
sudo apt install ./etminan_<version>_amd64.deb
gpg --verify etminan-<version>.x86_64.rpm.asc etminan-<version>.x86_64.rpm
sha256sum -c etminan-<version>.x86_64.rpm.sha256
sudo dnf install ./etminan-<version>.x86_64.rpm

The verifier has no libtss2 requirement, so it builds anywhere Rust does — including macOS and static musl:

cargo build --release -p etminan-verifier                 # native (incl. macOS)
cargo zigbuild --release \
    --target x86_64-unknown-linux-musl -p etminan-verifier # static musl

Add --features enterprise to build the Enterprise edition (SIEM, dual control, RBAC). See Editions.

The package creates the etminan-verifier service user, the state directory /var/lib/etminan-verifier/, and /etc/etminan-verifier/ with a verifier.env.example to copy.

Configure the verifier

Copy the example environment file and review it:

sudo cp /etc/etminan-verifier/verifier.env.example /etc/etminan-verifier/verifier.env
sudo $EDITOR /etc/etminan-verifier/verifier.env

The defaults are sensible for a first deployment (state in /var/lib/etminan-verifier, a 24-hour pending-review SLA). Notification channels, change-source correlation, and SIEM output are all off until you configure them. The complete list is in Verifier configuration.

Initialize the verifier

A fresh verifier needs three things before it can enrol and judge a host: its own TLS identity, the running etminan-verifierd trust daemon that owns the single signing key, and a bootstrapped first admin operator.

1. Generate the verifier's TLS identity

etminan-verifier keygen-tls

This writes the verifier's mTLS keypair under /var/lib/etminan-verifier/tls and prints its SHA-256 certificate fingerprint. Every agent this verifier talks to pins that fingerprint in ETMINAN_VERIFIER_CERT_FINGERPRINT — copy it somewhere handy for the first-attestation walkthrough.

2. Start the trust daemon

etminan-verifierd performs every trust-changing action — enrol approvals, baseline decisions, identity changes. Operators never hold a signing key of their own. The daemon holds the only signing key and signs on an operator's behalf, attributing each action to operator:<label>. Operators reach it through etminan-verifier op <command>, and the daemon authenticates every caller by the kernel-supplied peer credentials of the connection (SO_PEERCRED) — there are no operator key files to generate, distribute, or protect.

sudo systemctl enable --now etminan-verifierd.service

The unit listens on the local socket /run/etminan-verifierd/etminan-verifierd.sock. Confirm it is up:

etminan-verifier op ping

Authorization is default-deny / fail-closed: until an identity is enrolled the daemon refuses every trust-changing action, and it records both every allow and every deny to the tamper-evident, hash-chained audit log.

3. Bootstrap the first admin operator

The identity registry starts empty, so the very first identity is added by root, once, directly on the verifier host:

sudo etminan-verifier op bootstrap --uid 1001 --label "alice"

--uid is the Unix UID the daemon recognises (over SO_PEERCRED) as this admin; --label is how it attributes their signed actions. From then on that admin adds further operators — scoped to a host-group where you want least privilege — without root:

# an operator confined to the region-A host group
etminan-verifier op identity add --uid 1002 --role operator \
    --scope "region-a-*" --label "carol"

etminan-verifier op identity list

Roles are admin, operator, and viewer; an operator is confined to its host scope. The full model — roles, scopes, revocation, and the optional TOTP 2FA (off by default) — is in RBAC.

There is no operator key file

Every trust-changing action goes through the daemon over etminan-verifier op …, authorized by peer-UID and signed with the daemon's single key. There are no operator key files to generate, distribute, or protect, and no --key signing path. See RBAC.

Enterprise edition

Dual control (four-eyes approval) builds on this daemon and requires the Enterprise build. It is optional and off by default. RBAC itself is Standard — the daemon above is the default access-control model, not an Enterprise add-on.

Enable the hourly timer

The verifier is not a daemon — it runs one complete pass over every enrolled host per invocation, scheduled by a timer:

sudo systemctl enable --now etminan-verifier.timer

The timer runs etminan-verifier run hourly (OnBootSec=10min, OnUnitActiveSec=1h), firing the alarm path on anything wrong. You can always run a single pass by hand — etminan-verifier run — or check one host interactively with etminan-verifier check (used in the next chapter).

flowchart LR
    timer["etminan-verifier.timer<br/>(hourly)"] --> run["etminan-verifier run"]
    run --> loop{"for each<br/>enrolled host"}
    loop --> check["request quote · verify ·<br/>replay · diff baseline"]
    check --> loop
    loop -->|findings| alarm["alarm path<br/>(email / SIEM / plugins)"]
    style timer fill:#0f1c18,stroke:#7fd99a,color:#e9efec
    style alarm fill:#1e332b,stroke:#7fd99a,color:#e9efec

Next steps