Keylime Remote Attestation on Linux: TPM 2.0 Trust for Server Fleets in 2026

Deploy Keylime continuous remote attestation on Linux with TPM 2.0: registrar, verifier, Rust agent, IMA runtime policies, and revocation webhooks in 2026.

Keylime TPM 2.0 Attestation Guide 2026

Updated: June 28, 2026

Keylime is a CNCF open-source remote attestation framework that uses TPM 2.0 to continuously verify Linux servers booted with the expected firmware, kernel, and userspace, and have not been tampered with at runtime. Unlike one-shot boot attestation, Keylime keeps polling each agent's IMA measurement log and PCR quotes, so a kernel module loaded ten minutes after boot still gets caught. In this guide I'll walk through how Keylime fits with measured boot and IMA, how to deploy the verifier/registrar/agent topology on Linux, and which policies actually catch attacks in a fleet.

  • Keylime ties TPM 2.0 PCR quotes and IMA runtime measurements into a continuous attestation loop with revocation hooks, not just boot-time verification.
  • The reference agent rewritten in Rust (keylime-agent-rs) replaced the Python agent as default in Keylime 7.12.0 and is the recommended deployment for 2026.
  • A working topology needs three services (Registrar for enrolment, Verifier for continuous quote polling, Tenant as the CLI/API for adding agents) plus an agent on every monitored host.
  • Runtime policies are JSON allowlists of file hashes; pair them with kernel ima_policy=tcb and a signed IMA policy for meaningful runtime integrity.
  • Revocation actions trigger webhooks or local scripts when an agent fails quote verification, enabling automated isolation in fleet orchestration.
  • Keylime needs a real or virtual TPM (swtpm) plus measured boot (UEFI, shim, GRUB or sd-boot, kernel) to be useful. Without measured boot, PCRs 0–7 carry no trust.

What is Keylime used for?

Keylime is used for continuous remote attestation: a verifier service repeatedly asks each enrolled Linux host to prove, with a TPM-signed quote, that its Platform Configuration Registers (PCRs) match expected values and that its IMA log has only recorded files matching an approved allowlist. The project began at MIT Lincoln Laboratory, became a CNCF incubating project in 2024, and is widely used in confidential-computing pipelines, edge fleets, and HPC clusters where bare-metal hosts need to prove their integrity to a control plane before being granted secrets or workloads.

Three properties make Keylime worth deploying on top of TPM 2.0 alone. First, attestation is continuous. By default the verifier polls every two seconds, so a runtime tamper is detected in seconds rather than at next reboot. Second, the same protocol can release a payload (a key, a Kubernetes join token, a LUKS passphrase) only after a successful first quote, which closes the gap between boot and key delivery. Third, the system is policy-driven via JSON runtime policies and webhook revocation actions, so integration with orchestration tools (Ansible, Kubernetes admission, or systemd unit lifecycle) is a matter of configuration rather than custom code.

It's not a SIEM, an EDR, or a substitute for SELinux or signed kernel modules. It's the cryptographic ground truth those other layers can hang off. If you're already running Linux IMA and EVM for kernel-level file integrity, Keylime is the natural way to externalise verification of those measurements so a compromised host cannot lie about its own integrity.

How does Keylime use TPM 2.0 and IMA?

Keylime composes three TPM 2.0 primitives (Endorsement Keys, Attestation Keys, and PCR quotes) with the Linux kernel's Integrity Measurement Architecture (IMA). On enrolment, the agent reads the TPM's EK certificate (burned in by the vendor), proves possession of the corresponding private key via a TPM2_MakeCredential / TPM2_ActivateCredential challenge, and registers an AK that will sign subsequent quotes. The registrar verifies the EK certificate against a configured trust store (e.g. Intel, AMD, Nuvoton, Infineon vendor CAs), which is what stops an attacker from registering a fake software TPM as a real bare-metal host.

For boot integrity, Keylime checks the values in PCRs 0–7 (firmware, option ROMs, boot loader, kernel command line) against either a static manifest or a computed expected value derived from the UEFI event log. With Unified Kernel Images and systemd-boot, expected PCR values are predictable, which makes Keylime policies much easier to maintain across fleet rolling upgrades. PCR 10 carries the running IMA hash chain; the verifier walks the IMA measurement log (/sys/kernel/security/ima/ascii_runtime_measurements) and recomputes PCR 10 to confirm the log is authentic.

Runtime policies are then applied to that authenticated log. The verifier accepts a measurement if its hash is in the runtime policy's digests map, or if the file is signed by a key in verification-keys. Anything else (an unknown binary, a modified config file with ima_policy=tcb watching it) triggers revocation. The kernel side requires ima_policy=tcb ima_template=ima-sig on the kernel command line for signature verification to work end-to-end.

Keylime architecture: registrar, verifier, tenant, agent

A working Keylime deployment is four components. Three of them are control-plane services usually colocated on a hardened management host; the fourth runs on every monitored Linux server.

ComponentRoleDefault portState
RegistrarEnrols agents, verifies EK certificates, stores AK public keys8890/8891SQLite or Postgres
VerifierPolls agents for quotes, evaluates runtime policy, fires revocations8881SQLite or Postgres
TenantCLI/REST client that adds agents, uploads policies, delivers payloadsn/a (client)Stateless
AgentRuns on each monitored host, owns the TPM, returns signed quotes9002Local config + TPM

Mutual TLS is mandatory between every pair of components. By default the tenant generates a CA (cv_ca) on first run, but for production you should point it at a real intermediate CA you can revoke. The registrar's database is the trust root for which TPMs are enrolled; the verifier's database holds the per-agent policy assignment and current state machine (Get Quote, Failed, Tenant Failed, etc.). Read the Keylime documentation for the full state machine. Every state transition is logged and is the primary signal for fleet monitoring.

For high availability, the registrar and verifier both support Postgres backends and can be scaled horizontally as long as they share storage; the agent only talks to one verifier at a time, so verifier failover is typically handled by reassigning agents via the tenant API.

How do you install Keylime on Linux?

The install steps below are for Ubuntu 24.04 LTS or Fedora 41 with TPM 2.0 hardware (or swtpm for lab work). You need the tpm2-tss userspace stack, tpm2-abrmd or the kernel resource manager, and Rust 1.78+ to build the reference agent. All commands run as root unless noted.

1. Prepare the TPM and kernel

# Confirm TPM 2.0 presence (must be 2.0, not 1.2)
ls /dev/tpm0 /dev/tpmrm0
cat /sys/class/tpm/tpm0/tpm_version_major   # expect: 2

# Confirm IMA is enabled with a useful policy
mount | grep securityfs
cat /sys/kernel/security/ima/policy 2>/dev/null | head

# Persistent kernel cmdline for measured-boot + IMA
# /etc/default/grub: GRUB_CMDLINE_LINUX="ima_policy=tcb ima_template=ima-sig ima_appraise=fix"
# Then: update-grub  (or grub2-mkconfig -o /boot/grub2/grub.cfg)

2. Install control-plane services

# Fedora 41
dnf install -y keylime-verifier keylime-registrar keylime-tenant tpm2-tools

# Ubuntu 24.04 (use the Keylime PPA or build from source for >=7.12.0)
add-apt-repository ppa:keylime/keylime
apt update
apt install -y keylime-verifier keylime-registrar keylime-tenant tpm2-tools

# Initialise the demo CA (replace with your real intermediate in prod)
keylime_tenant -c ca init -d /var/lib/keylime/cv_ca

systemctl enable --now keylime_registrar keylime_verifier

3. Install the Rust agent on the monitored host

# Build from the upstream release tag (7.12.0+ as of 2026)
git clone https://github.com/keylime/rust-keylime
cd rust-keylime
git checkout v0.2.7
cargo build --release
install -m 0755 target/release/keylime_agent /usr/local/bin/

# /etc/keylime/agent.conf
# registrar_ip = 10.0.0.10
# verifier_ip  = 10.0.0.10
# tpm_ownerpassword =
# enable_revocation_notifications = true
# revocation_actions_dir = /usr/libexec/keylime/revocation-actions

systemctl enable --now keylime_agent

Enrolling an agent and writing a runtime policy

Enrolment is one tenant command per host. Behind the scenes it pulls the AK from the registrar, asks the verifier to start polling, and uploads the runtime policy plus optional payload to release on first successful quote.

# 1. Build a runtime policy from a known-good host
keylime-policy create runtime \
  --ramdisk-dir /boot \
  --rootfs / \
  --ima-measurement-list /sys/kernel/security/ima/ascii_runtime_measurements \
  --output /etc/keylime/policies/web01.json

# 2. (Optional) Build a measured-boot policy from this host's UEFI log
keylime-policy create measured-boot \
  --eventlog-file /sys/kernel/security/tpm0/binary_bios_measurements \
  --output /etc/keylime/policies/mb-web01.json

# 3. Enrol the agent (UUID is in /var/lib/keylime/agent_uuid on the host)
keylime_tenant -c add \
  -t 10.0.1.20 \
  -u d432fbb3-d2f1-4a97-9ef7-75bd81c00000 \
  --runtime-policy /etc/keylime/policies/web01.json \
  --mb-policy /etc/keylime/policies/mb-web01.json \
  --cert default

# 4. Watch the state machine
keylime_tenant -c cvstatus -u d432fbb3-d2f1-4a97-9ef7-75bd81c00000

A healthy agent moves Start, Get Quote, Provide V, Get Quote (loop). The instant a measurement appears that's not in the runtime policy and is not signed by a trusted key, the verifier transitions to Invalid Quote and triggers revocation. Honestly, in my experience, the most common false positive on a fresh policy is package managers writing temporary files into /var/lib/dpkg or /var/cache/dnf. Exclude those paths in the policy's excludes list rather than widening the allowlist.

Revocation actions and webhooks

When an agent fails quote verification, the Keylime verifier signs a revocation message with its CA key and broadcasts it. Two consumers act on it: the agent itself (which can execute local scripts in revocation_actions_dir) and any subscribed webhook receiver registered with the tenant. This is where Keylime stops being a monitoring tool and starts being an enforcement control.

#!/bin/bash
# /usr/libexec/keylime/revocation-actions/local_action_kill_workloads
# Runs on the offending host when it gets its own revocation
set -eu
logger -t keylime "Revocation received: $1"
systemctl stop docker.service kubelet.service nginx.service
ip link set eth0 down
# Register a webhook receiver with the verifier (Keylime 7.10+)
# /etc/keylime/verifier.conf
# [revocations]
# enabled_revocation_notifications = webhook,zmq
# webhook_url = https://soc.internal.example/keylime/revocation

The webhook payload is JSON with the agent UUID, IP, failure reason, and the offending IMA entry if applicable. In a multi-layer IDS, this is the cleanest signal to feed into Wazuh or your SIEM, because it's cryptographically authenticated by the verifier rather than relying on log shipping from a possibly-compromised host. Combine with nftables drop rules on the SDN controller and you have automated network isolation in under five seconds from runtime tamper to quarantine. (If you're already running an nftables zero-trust ruleset, the revocation hook is one line of curl on your SDN side.)

Rust agent vs Python agent

Keylime's original agent was written in Python. As of Keylime 7.12.0 (April 2025) and across 2026 releases, the Rust agent (rust-keylime) is the upstream default and the Python agent is in deprecation. The differences matter for fleet operators.

AspectRust agent (rust-keylime)Python agent (legacy)
Binary size~8 MB static~120 MB with Python runtime
Memory footprint15–25 MB RSS80–150 MB RSS
Dependenciestpm2-tss onlytpm2-tss, tpm2-pytss, Python 3.9+, many wheels
Quote latency~40 ms typical~180 ms typical
Container/UKI friendlyYes, drops into a UKI initrdDifficult
Upstream statusDefault, actively developedDeprecated, security fixes only

The practical impact is that the Rust agent fits inside a Unified Kernel Image initrd, so attestation can complete before the root filesystem is mounted and a LUKS passphrase released. That closes the gap where Python agents started after rootfs and so could never attest the early boot environment without help from a separate dracut module.

Operating Keylime in production

Three operational concerns dominate after the first hundred agents are enrolled: policy drift, TPM resource manager contention, and verifier scaling.

Policy drift happens because every package upgrade changes file hashes. Bake policy generation into your image pipeline: build a golden image, boot it under swtpm in CI, snapshot the IMA log at known-good idle, and ship the JSON policy alongside the image. Hosts of the same image version share the same policy hash; mismatches surface immediately at enrol time. Avoid the temptation to regenerate policies live on production hosts, because that defeats the integrity guarantee.

TPM contention appears as flapping agents on hosts that also run tpm2-totp, full-disk encryption with TPM-bound LUKS, or measured-boot tools like systemd-pcrlock. The kernel resource manager (/dev/tpmrm0) serialises access well, but PCR extends from another process can race the agent if you use raw /dev/tpm0. Standardise on tpmrm0 across the fleet. I hit this exact bug shipping an attestation rollout to a fleet that also bound LUKS to PCR 7, and the fix really was one config line.

Verifier scaling is mostly a Postgres exercise. A single verifier process comfortably handles ~500 agents at the default 2-second poll interval; beyond that, raise quote_interval to 10 seconds for less critical hosts and run multiple verifier processes behind a stable assignment map. Track keylime_verifier_agents and keylime_verifier_quote_duration_seconds via the built-in Prometheus exporter shipped since 7.8.0, and alert on either growing unbounded.

Finally, treat the registrar's EK certificate trust store as a security boundary equivalent to your CA. Audit additions to /var/lib/keylime/cv_ca/cacert.crt and the vendor cert directory under version control. An attacker who can add their own EK CA can register a software TPM as a real host and bypass every PCR check downstream.

Frequently Asked Questions

Does Keylime require physical TPM 2.0 hardware?

No. For lab and CI use, swtpm with libtpms provides a fully functional virtual TPM 2.0 that Keylime treats identically to hardware. For production attestation guarantees you do need real hardware TPMs, because the EK certificate chain is what proves the TPM is genuine and not an attacker-controlled software emulator.

Is Keylime a replacement for SELinux or AppArmor?

No. SELinux and AppArmor enforce policy on running processes; Keylime measures and verifies. The two are complementary: Keylime can detect that an unexpected binary executed (via IMA), while SELinux can stop it from doing damage in the first place. In a hardened fleet you run both.

How often does the Keylime verifier poll an agent?

Default is every 2 seconds, configurable via quote_interval in verifier.conf. Production fleets typically run 2 s for security-critical hosts (PKI, secret stores) and 10–30 s for less sensitive workloads. The TPM quote operation itself takes ~40 ms on modern hardware, so polling isn't the bottleneck; Postgres write throughput is.

Can Keylime release secrets only to an attested host?

Yes. The tenant's --payload flag uploads an encrypted bundle that the verifier releases to the agent only after the first successful quote. Common payloads are a LUKS passphrase, a Kubernetes join token, a Vault unseal key, or a Sigstore signing key. Anything where you want delivery gated on cryptographic proof of integrity fits the pattern.

What happens to enrolled hosts if the verifier goes down?

Agents continue running and producing quotes locally, but nobody is verifying them. New enrolments will fail until the verifier is back. If the registrar also fails, existing agents keep operating but no new hosts can join. Treat both as tier-1 services with monitoring and replicated Postgres; the failure mode is loss of detection, not loss of workload.

Yuki Tanaka
About the Author Yuki Tanaka

Linux kernel security engineer with a background in eBPF and LSM. Likes hardening more than she likes sleeping.