Skip to main content
Redhat Developers  Logo
  • Products

    Platforms

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat AI
      Red Hat AI
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • See all Red Hat products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat Developer Hub
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat OpenShift Local
    • Red Hat Developer Sandbox

      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Red Hat OpenShift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • See all technologies
    • Programming languages & frameworks

      • Java
      • Python
      • JavaScript
    • System design & architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer experience

      • Productivity
      • Tools
      • GitOps
    • Automated data processing

      • AI/ML
      • Data science
      • Apache Kafka on Kubernetes
    • Platform engineering

      • DevOps
      • DevSecOps
      • Red Hat Ansible Automation Platform for applications and services
    • Secure development & architectures

      • Security
      • Secure coding
  • Learn

    Featured

    • Kubernetes & cloud native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • AI/ML
      AI/ML Icon
    • See all learning resources

    E-books

    • GitOps cookbook
    • Podman in action
    • Kubernetes operators
    • The path to GitOps
    • See all e-books

    Cheat sheets

    • Linux commands
    • Bash commands
    • Git
    • systemd commands
    • See all cheat sheets

    Documentation

    • Product documentation
    • API catalog
    • Legacy documentation
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore the Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Agent-driven attestation: How Keylime's push model rethinks remote integrity verification

Keylime's push model: Eliminating contradictions in remote attestation

April 8, 2026
Anderson Sasaki Sergio Arroutb Sergio Correia
Related topics:
LinuxKubernetesSecuritySystem design
Related products:
Red Hat Enterprise LinuxRed Hat OpenShift

    Remote attestation has a fundamental tension at its core. The whole point is to verify that a system is trustworthy, but to do that you run a network server accepting inbound connections, and expose ports to the world. You're asking an untrusted machine to act as a service endpoint before you've even verified it.

    Keylime's new push model eliminates this contradiction. Instead of the verifier polling agents, agents drive the attestation process themselves by initiating connections, submitting evidence, and never exposing a single port. This feature is supported in Red Hat Enterprise Linux 10.2 (RHEL) and is available upstream in the Rust agent and Python verifier.

    This article is for platform engineers, security architects, and anyone running infrastructure where the adage "trust but verify" isn't good enough. You don't need prior Keylime experience to follow along. We explain the concepts as we go.

    The problem with pull

    In Keylime's traditional pull model, the verifier is the active party. It maintains a list of enrolled agents, periodically connects to each one over HTTPS, requests a TPM quote and measurement logs, then validates the response. The agent runs an HTTP server and waits.

    This works, but it creates real problems:

    • Attack surface: Every agent exposes an HTTP server to the network. That's a listening port on every machine you're trying to protect — exactly the kind of surface area security teams spend their careers minimizing.
    • Network complexity: The verifier needs direct connectivity to every agent. In enterprise environments with NATs, firewalls, and segmented networks, this means punching holes in exactly the places you don't want them.
    • Scaling: The verifier must maintain O(n) active connections, one for every enrolled agent, with polling loops consuming resources whether or not there's anything new to verify.
    • Rigidity: Attestation frequency is controlled by the verifier's polling interval, not by the workload's actual security needs.

    The different approaches are illustrated in Figure 1.

    Illustration of pull and push models.
    Figure 1: Illustration of pull and push models.

    Inverting the relationship

    The push model flips the architecture, as illustrated in Figure 2. Agents become HTTP clients, connecting outbound to the verifier to submit their attestation evidence, and then closing the connection. The verifier is a service endpoint that processes evidence as it arrives.

    Keylime agent as HTTPS client.
    Figure 2: Keylime agent as HTTPS client.

    No agent ports. No inbound firewall rules. No NAT traversal. Agents only need outbound HTTPS to a single verifier endpoint, as show in Figure 3.

    Push model with no agent's ports exposed.
    Figure 3: Push model with no agent's ports exposed.

    The Rust agent goes further. It opens privileged resources (IMA measurement logs at /sys/kernel/security/ima/ascii_runtime_measurements, UEFI event logs at /sys/kernel/security/tpm0/binary_bios_measurements) while still running as root, then irreversibly drops privileges to an unprivileged user. The privilege drop follows CERT C POS36-C guidelines: After calling setuid(), the agent verifies it cannot regain root by confirming setuid(0) fails. For the rest of its lifetime, the agent operates unprivileged, accessing those security-sensitive files through the file descriptors it opened before the drop. This is summarized in Figure 4.

    Push model privileges are designed to be minimal.
    Figure 4: Push model privileges are designed to be minimal.

    Payload delivery (the pull model's mechanism for pushing encrypted secrets and scripts to agents) is deliberately absent from the push model. Removing the ability for server components to execute arbitrary code on attested nodes is a security-first design choice, not a missing feature.

    How it works

    The push model attestation lifecycle has four phases (shown in Figure 5):

    • Register: Prove your TPM identity
    • Authenticate: Get a session token through a cryptographic challenge
    • Negotiate: Agree on what evidence to collect
    • Submit: Send that evidence on a recurring schedule
    Push model messages sequence diagram.
    Figure 5: Push model messages sequence diagram.

    Registration

    Key terms: The endorsement key (EK) is a unique key burned into the TPM at manufacturing. The attestation key (AK) is derived from the EK and used to sign attestation evidence.

    The agent starts by registering with the Keylime registrar, providing its identity: The TPM's endorsement key, attestation key, and any available certificates. The registrar performs trust decisions, verifying the EK against a certificate trust store, invoking webhooks for custom logic when certificates aren't available (common with cloud vTPMs), and binding identities to the node's logical identifier.

    Authentication

    What is proof of possession (PoP)? Instead of using pre-provisioned certificates, the agent proves it holds the TPM's private key by signing a challenge from the verifier. This avoids the operational overhead of distributing and managing client certificates.

    Once registered, the agent needs to authenticate to the verifier before submitting evidence. The push model uses challenge-response proof of possession (PoP), authenticating directly through the TPM with no need for pre-provisioned client certificates. The agent connects over standard TLS (server-verified only, no mTLS), which keeps certificate management simple (as shown in Figure 6). The protocol is a three-step exchange:

    1. The agent sends POST sessions with its agent_id and declares that it supports tpm_pop authentication.
    2. The verifier responds with a challenge nonce. Critically, it produces an identical response structure regardless of whether the agent_id is known. This information-hiding design prevents attackers from enumerating valid agent identifiers.
    3. The agent uses TPM2_Certify to produce a proof of possession. The AK certifies itself, signing the challenge nonce. The agent sends this proof using PATCH sessions:id. If the signature validates against the AK on file, then the verifier issues a session token.
    Push model authentication sequence diagram.
    Figure 6: Push model authentication sequence diagram.

    The token is wrapped in a SecretToken type that prevents accidental leakage. Debug and display implementations show only a truncated SHA-256 hash prefix (SecretToken(<hash:a1b2c3d4>)), and the raw value is accessible only through an explicit reveal() call used solely for Authorization: Bearer headers.

    Capabilities negotiation

    What are PCRs? Platform configuration registers (PCR) are tamper-evident registers inside the TPM that record measurements of boot and runtime software. They can only be extended (that is, appended to), never overwritten, providing a cryptographic chain of the system's software state.

    Before collecting evidence, the agent tells the verifier what it can produce. This isn't a static declaration, and the Rust agent's StructureFiller queries the actual TPM hardware at runtime to discover:

    • Supported hash algorithms: SHA-1, SHA-256, SHA-384, SHA-512, SM3-256 (whatever the TPM reports)
    • PCR banks: Which PCRs are available under each algorithm
    • AK certification data: The public portion of the attestation key and its properties
    • Supported signing schemes: What the TPM can use to sign quotes

    It also reports IMA and UEFI log capabilities. The IMA log is declared as appendable with supports_partial_access: true (the agent can send incremental updates), while the UEFI log is appendable: false (it's static after boot).

    The verifier responds with what evidence it wants, including a challenge nonce for the TPM quote and the specific PCR selections.

    Evidence submission

    IMA and UEFI logs at a glance: The integrity measurement architecture (IMA) log records every file the kernel loads at runtime. The UEFI boot log records the boot sequence and is static after startup. Together, they give the verifier a complete picture of what software ran on the machine.

    The agent collects its evidence and submits it in a single PATCH request. Behind the scenes:

    • TPM quotes are generated fresh, signing the verifier's nonce with the AK over the requested PCR banks.
    • IMA logs are read incrementally. The agent maintains a MeasurementList that tracks (entry_count, file_offset) pairs. On each attestation cycle, it seeks to the last known position and reads only new entries. This avoids re-reading the entire measurement log, which can grow to hundreds of thousands of entries on long-running systems.
    • UEFI event logs are read from the file descriptor opened before the privilege drop. Because the measured boot log is static after boot, it doesn't need the incremental tracking that IMA requires. The agent re-reads and sends the full log each cycle the verifier requests it.
    Keylime agent evidences collected.
    Figure 7: Keylime agent evidences collected.

    The verifier responds 202 Accepted immediately and verifies the evidence asynchronously. This keeps the protocol responsive. The agent isn't blocked waiting for potentially expensive cryptographic verification and policy evaluation. The response includes a seconds_to_next_attestation value that the agent uses to schedule its next cycle, giving the verifier server-side control over attestation frequency.

    Continuous attestation

    The agent enters a loop: Sleep for an interval specified by the server, renegotiate capabilities (the verifier may have updated its requirements or policy), collect fresh evidence, and submit. If the token expires or the verifier returns 401, the agent immediately re-authenticates. If attestation fails, the agent retries with exponential backoff. If registration is lost, it starts over from scratch.

    Security by design

    The push model's security advantages go well beyond mandating no open ports.

    Information hiding: The authentication protocol is designed so that the verifier reveals as little as possible before authentication completes. During the challenge-response exchange, the verifier produces the same response structure for valid and invalid agent identifiers. An attacker probing the API cannot determine whether a particular agent is enrolled.

    Token opacity: Session tokens never appear in logs. The SecretToken wrapper ensures that any attempt to log, debug-print, or format a token produces only its hash prefix. The actual token value is gated behind reveal(), which is called only when constructing the HTTP Authorization header.

    Rate limiting: The verifier supports dual-layer rate limiting — per-IP and per-agent — to protect against denial-of-service attacks on the session creation endpoint. Both layers are independently configurable with separate windows and thresholds.

    Privilege separation: The agent opens /sys/kernel/security files as root, then irreversibly drops to an unprivileged user (typically keylime:keylime) with supplementary group tss for TPM access. The three-phase drop — validate target user, open privileged resources, drop and verify — follows POSIX best practices and explicitly confirms the drop is irreversible.

    Fail-fast on corruption: If the TPM mutex is poisoned (indicating a thread panicked while holding the TPM lock), the agent calls process::exit(1) immediately. No graceful shutdown, no attempt to recover potentially corrupted TPM state. The rationale is explicit in the code: TPM hardware state may be inconsistent, any internal corruption is a critical security event, and only a full process restart can safely reinitialize TPM contexts. A supervisor process or systemd handles the restart with a clean slate.

    Resilient by default

    The Rust agent is engineered for the reality of unreliable networks (illustrated in Figure 8):

    • Reasonable but progressive backoff policy: Exponential backoff with configurable initial delay (default 10 seconds), maximum delay (default 5 minutes), and retry count (default 5 attempts) handles transient failures without overwhelming the verifier.
    • Retry-after header support: The verifier can tell the agent exactly when to come back, in either seconds or HTTP-date format. The agent's RetryAfterMiddleware parses both and defers to server-directed timing, avoiding double-delays with the backoff strategy.
    • Many states: A state machine with clear recovery paths governs the agent lifecycle. There are seven states (Unregistered, Registered, Negotiating, Attesting, RegistrationFailed, AttestationFailed, and terminal Failed) with well-defined transitions. RegistrationFailed sleeps and retries registration. AttestationFailed sleeps and retries negotiation. A 401 during attestation triggers immediate re-authentication. Only unrecoverable errors reach the terminal Failed state.
      Push model finite state machine.
      Figure 8: Push model finite state machine.
    • Verifier-controlled intervals: To prevent overwhelming traffic, the seconds_to_next_attestation field in the verifier's response lets the server stagger agents. This slows attestation during high load, or increases frequency when security posture demands it.

    Deployment scenarios

    You're probably already seeing the advantage to Keylime's push model. There are many industries that stand to benefit. Here are a few example use cases.

    Edge and IoT

    Thousands of devices behind NATs with intermittent connectivity. In the pull model, every unreachable device wastes verifier resources on failed polling. In the push model, devices attest when they have connectivity and the verifier simply processes evidence as it arrives. No VPN tunnels, no port forwarding, no complex firewall rules — just outbound HTTPS.

    Red Hat OpenShift and Kubernetes

    Ephemeral pods with dynamic IPs are natural fit for agent-driven attestation. Pods attest on startup, push evidence to the verifier through the cluster's standard egress path, and disappear without the verifier needing to track their IP lifecycle. The push model works naturally with Kubernetes networking, service meshes, and security policies that restrict inbound connections to pods.

    High-security enterprise

    For environments requiring frequent attestation and strict compliance, the push model combines PoP authentication (no pre-provisioned certificates needed — the TPM proves identity), no exposed agent ports (aligning with zero-trust network principles), and server-controlled attestation intervals. The deliberate removal of payload delivery means the attestation infrastructure cannot be used as a vector for code execution on attested nodes.

    Getting started

    Deploying the push model requires configuring two components: The verifier (which receives and validates evidence) and the agent (which collects and submits it).

    Verifier configuration

    In /etc/keylime/verifier.conf:

    [verifier]
    mode = push
    # Challenge-response authentication
    challenge_lifetime = 300
    # Rate limiting
    session_create_rate_limit_per_ip = 50
    session_create_rate_limit_window_ip = 60
    session_create_rate_limit_per_agent = 15
    session_create_rate_limit_window_agent = 60

    Agent configuration

    In the Rust agent's configuration:

    [agent]
    verifier_url = "https://verifier.example.com:8881"
    enable_authentication = true
    attestation_interval_seconds = 60
    # Retry behavior
    exponential_backoff_max_retries = 5
    exponential_backoff_initial_delay = 10000
    exponential_backoff_max_delay = 300000

    Enrolling an Agent

    Once both sides are configured, enroll the agent with the tenant command-line interface:

    $ keylime_tenant -v 10.0.1.5 \
    -t 10.0.2.5 \
    -u d432fbb3-d2f1-4a97-9ef7-75bd81c00000 \
    --push-model \
    --runtime-policy policy.json

    The --push-model flag tells the verifier to expect evidence from this agent rather than polling for it.

    What's next

    The push model opens the door to capabilities that were impractical in a polling architecture:

    • Event-driven attestation: Attest on specific system events—sudo invocations, package installations, configuration changes—not just on a timer.
    • Adaptive intervals: Adjust attestation frequency based on workload risk profile or threat intelligence.
    • Distributed verification: With agents pushing to a service endpoint, verification can be horizontally scaled across multiple verifier instances behind a load balancer.

    The push model is available in the upstream Rust Keylime agent and Keylime verifier, and ships as a supported feature in RHEL 10.2. The enhancement proposal details the full design rationale, including the adversarial model and trust mechanisms.

    For detailed information on RHEL system integrity with Keylime: RHEL 10 Security Hardening Guide.

    Related Posts

    • Zero trust GitOps: Build a secure, secretless GitOps pipeline

    • Improve code quality and security with PatchPatrol

    • Agent Skills: Explore security threats and controls

    Recent Posts

    • Agent-driven attestation: How Keylime's push model rethinks remote integrity verification

    • Harness engineering: Structured workflows for AI-assisted development

    • Running Karpathy's autoresearch on Red Hat OpenShift AI: 198 experiments, zero intervention

    • Distributed tracing for agentic workflows with OpenTelemetry

    • Set up a CI framework using Red Hat Ansible Automation Platform, Podman, and Horreum

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue