The included patches implement graceful shutdown for both pull and push models, cancelling pending operations, and waiting for critical in-flight operations to finish before shutting down. Backport the following upstream PRs: - https://github.com/keylime/keylime/pull/1809 - Document supported configuration options - Sync missing and removed options from configuration templates - https://github.com/keylime/keylime/pull/1868 - Remove 'enable_authentication' from agent config templates - https://github.com/keylime/keylime/pull/1855 - Add push-model documentation - https://github.com/keylime/keylime/pull/1869 - Add verifier graceful shutdown - https://github.com/keylime/keylime/pull/1883 - Ignore SIGTERM and SIGINT signals on Manager and parent processes - https://github.com/keylime/keylime/pull/1886 - Move socket from /tmp to /var/run/keylime Also, update the keylime-selinux to the latest release (43.2.1) to include the following changes: - https://github.com/RedHat-SP-Security/keylime-selinux/pull/33 - Allow Keylime to perform socket operation on /var/run/keylime - https://github.com/RedHat-SP-Security/keylime-selinux/pull/34 - Allow Keylime to read /proc/net to populate certificates Subject Alternative Names (SAN) Documentation updates and configuration template updates were included to allow the graceful shutdown patch to apply cleanly. This also modifies the test runner to use pytest, adding python3-pytest to the BuildRequires. This was necessary to make the fixtures created in conftest.py to be used, which is not available when running with unittest. Resolves: RHEL-151493 Resolves: RHEL-151408 Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
1911 lines
71 KiB
Diff
1911 lines
71 KiB
Diff
From 077762aa335de0cf99e190bd5afb5b77f5403a89 Mon Sep 17 00:00:00 2001
|
|
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
|
|
Date: Tue, 17 Feb 2026 16:43:04 +0100
|
|
Subject: [PATCH] Document agent-driven (push) attestation
|
|
|
|
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
|
|
---
|
|
docs/assets/push-model-architecture.svg | 86 ++++
|
|
docs/assets/push-model-sequence.svg | 122 +++++
|
|
docs/conf.py | 1 +
|
|
docs/design.rst | 1 +
|
|
docs/design/overview.rst | 11 +-
|
|
docs/design/push_model.rst | 226 +++++++++
|
|
docs/index.rst | 1 +
|
|
docs/installation.rst | 11 +
|
|
docs/man/keylime_push_model_agent.8.rst | 226 +++++++++
|
|
docs/man/keylime_verifier.8.rst | 3 +-
|
|
docs/rest_apis.rst | 30 ++
|
|
docs/rest_apis/3_0/3_0.rst | 21 +
|
|
docs/rest_apis/3_0/verifier.rst | 608 ++++++++++++++++++++++++
|
|
docs/user_guide.rst | 1 +
|
|
docs/user_guide/configuration.rst | 7 +
|
|
docs/user_guide/push_model.rst | 370 ++++++++++++++
|
|
16 files changed, 1721 insertions(+), 4 deletions(-)
|
|
create mode 100644 docs/assets/push-model-architecture.svg
|
|
create mode 100644 docs/assets/push-model-sequence.svg
|
|
create mode 100644 docs/design/push_model.rst
|
|
create mode 100644 docs/man/keylime_push_model_agent.8.rst
|
|
create mode 100644 docs/rest_apis/3_0/3_0.rst
|
|
create mode 100644 docs/rest_apis/3_0/verifier.rst
|
|
create mode 100644 docs/user_guide/push_model.rst
|
|
|
|
diff --git a/docs/assets/push-model-architecture.svg b/docs/assets/push-model-architecture.svg
|
|
new file mode 100644
|
|
index 000000000..82a5672f4
|
|
--- /dev/null
|
|
+++ b/docs/assets/push-model-architecture.svg
|
|
@@ -0,0 +1,86 @@
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400" width="800" height="400">
|
|
+ <defs>
|
|
+ <style>
|
|
+ text { font-family: Tahoma, sans-serif; font-size: 14px; }
|
|
+ .title { font-size: 16px; font-weight: bold; }
|
|
+ .subtitle { font-size: 12px; fill: #666; }
|
|
+ .box { fill: #f0f0f0; stroke: #333; stroke-width: 1.5; rx: 5; ry: 5; }
|
|
+ .arrow { stroke: #333; stroke-width: 1.5; fill: none; marker-end: url(#arrowhead); }
|
|
+ .arrow-push { stroke: #2563eb; stroke-width: 2; fill: none; marker-end: url(#arrowhead-push); }
|
|
+ .label { font-size: 11px; fill: #555; }
|
|
+ .label-push { font-size: 11px; fill: #2563eb; }
|
|
+ .section-title { font-size: 13px; font-weight: bold; fill: #333; }
|
|
+ </style>
|
|
+ <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
|
|
+ <polygon points="0 0, 10 3.5, 0 7" fill="#333"/>
|
|
+ </marker>
|
|
+ <marker id="arrowhead-push" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
|
|
+ <polygon points="0 0, 10 3.5, 0 7" fill="#2563eb"/>
|
|
+ </marker>
|
|
+ </defs>
|
|
+
|
|
+ <!-- Title -->
|
|
+ <text x="400" y="30" text-anchor="middle" class="title">Push-Model Architecture</text>
|
|
+
|
|
+ <!-- Pull Model (left side) -->
|
|
+ <text x="200" y="60" text-anchor="middle" class="section-title">Pull Model (traditional)</text>
|
|
+
|
|
+ <!-- Pull - Agent box -->
|
|
+ <rect x="30" y="120" width="120" height="50" class="box"/>
|
|
+ <text x="90" y="150" text-anchor="middle">Agent</text>
|
|
+ <text x="90" y="165" text-anchor="middle" class="subtitle">(server, port 9002)</text>
|
|
+
|
|
+ <!-- Pull - Registrar box -->
|
|
+ <rect x="230" y="80" width="120" height="50" class="box"/>
|
|
+ <text x="290" y="110" text-anchor="middle">Registrar</text>
|
|
+
|
|
+ <!-- Pull - Verifier box -->
|
|
+ <rect x="230" y="160" width="120" height="50" class="box"/>
|
|
+ <text x="290" y="190" text-anchor="middle">Verifier</text>
|
|
+
|
|
+ <!-- Pull - Agent to Registrar arrow -->
|
|
+ <path d="M 150 130 L 225 105" class="arrow"/>
|
|
+ <text x="180" y="110" class="label">register</text>
|
|
+
|
|
+ <!-- Pull - Verifier to Agent arrow -->
|
|
+ <path d="M 230 185 L 155 160" class="arrow"/>
|
|
+ <text x="170" y="185" class="label">poll quotes</text>
|
|
+
|
|
+ <!-- Divider -->
|
|
+ <line x1="400" y1="55" x2="400" y2="380" stroke="#ccc" stroke-width="1" stroke-dasharray="5,5"/>
|
|
+
|
|
+ <!-- Push Model (right side) -->
|
|
+ <text x="600" y="60" text-anchor="middle" class="section-title">Push Model (new)</text>
|
|
+
|
|
+ <!-- Push - Agent box -->
|
|
+ <rect x="430" y="120" width="120" height="50" class="box" style="stroke: #2563eb;"/>
|
|
+ <text x="490" y="150" text-anchor="middle">Agent</text>
|
|
+ <text x="490" y="165" text-anchor="middle" class="subtitle">(client, no ports)</text>
|
|
+
|
|
+ <!-- Push - Registrar box -->
|
|
+ <rect x="630" y="80" width="120" height="50" class="box"/>
|
|
+ <text x="690" y="110" text-anchor="middle">Registrar</text>
|
|
+
|
|
+ <!-- Push - Verifier box -->
|
|
+ <rect x="630" y="160" width="120" height="50" class="box"/>
|
|
+ <text x="690" y="190" text-anchor="middle">Verifier</text>
|
|
+
|
|
+ <!-- Push - Agent to Registrar arrow -->
|
|
+ <path d="M 550 130 L 625 105" class="arrow-push"/>
|
|
+ <text x="580" y="110" class="label-push">register</text>
|
|
+
|
|
+ <!-- Push - Agent to Verifier arrow -->
|
|
+ <path d="M 550 160 L 625 180" class="arrow-push"/>
|
|
+ <text x="565" y="182" class="label-push">push evidence</text>
|
|
+
|
|
+ <!-- Legend -->
|
|
+ <rect x="30" y="250" width="740" height="130" fill="#fafafa" stroke="#ddd" rx="5" ry="5"/>
|
|
+ <text x="50" y="275" class="section-title">Protocol Flow (Push Model)</text>
|
|
+
|
|
+ <text x="50" y="300" class="label">1. Agent registers with Registrar (same as pull model)</text>
|
|
+ <text x="50" y="320" class="label">2. Agent authenticates with Verifier via PoP (POST /v3/sessions)</text>
|
|
+ <text x="50" y="340" class="label">3. Agent sends capabilities to Verifier (POST /v3/agents/{agent_id}/attestations) — receives challenge nonce</text>
|
|
+ <text x="50" y="360" class="label">4. Agent sends evidence to Verifier (PATCH /v3/agents/{agent_id}/attestations/latest) — receives 202 Accepted</text>
|
|
+ <text x="50" y="375" class="label">5. Agent waits for configured interval, then repeats from step 3</text>
|
|
+</svg>
|
|
diff --git a/docs/assets/push-model-sequence.svg b/docs/assets/push-model-sequence.svg
|
|
new file mode 100644
|
|
index 000000000..d9affe1c9
|
|
--- /dev/null
|
|
+++ b/docs/assets/push-model-sequence.svg
|
|
@@ -0,0 +1,122 @@
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 580" width="700" height="580">
|
|
+ <defs>
|
|
+ <style>
|
|
+ text { font-family: Tahoma, sans-serif; font-size: 12px; }
|
|
+ .title { font-size: 16px; font-weight: bold; }
|
|
+ .state { fill: #f0f0f0; stroke: #333; stroke-width: 1.5; rx: 20; ry: 20; }
|
|
+ .state-initial { fill: #e0e7ff; stroke: #2563eb; stroke-width: 2; rx: 20; ry: 20; }
|
|
+ .state-active { fill: #dbeafe; stroke: #2563eb; stroke-width: 2; rx: 20; ry: 20; }
|
|
+ .state-error { fill: #fee2e2; stroke: #dc2626; stroke-width: 1.5; rx: 20; ry: 20; }
|
|
+ .arrow { stroke: #333; stroke-width: 1.5; fill: none; marker-end: url(#arrowhead); }
|
|
+ .arrow-success { stroke: #16a34a; stroke-width: 1.5; fill: none; marker-end: url(#arrowhead-success); }
|
|
+ .arrow-error { stroke: #dc2626; stroke-width: 1.5; fill: none; marker-end: url(#arrowhead-error); }
|
|
+ .arrow-retry { stroke: #f59e0b; stroke-width: 1.5; fill: none; stroke-dasharray: 5,3; marker-end: url(#arrowhead-retry); }
|
|
+ .label { font-size: 10px; fill: #555; }
|
|
+ .label-success { font-size: 10px; fill: #16a34a; }
|
|
+ .label-error { font-size: 10px; fill: #dc2626; }
|
|
+ .label-retry { font-size: 10px; fill: #f59e0b; }
|
|
+ .state-text { font-size: 13px; font-weight: bold; text-anchor: middle; }
|
|
+ .state-desc { font-size: 10px; text-anchor: middle; fill: #666; }
|
|
+ </style>
|
|
+ <marker id="arrowhead" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
|
|
+ <polygon points="0 0, 8 3, 0 6" fill="#333"/>
|
|
+ </marker>
|
|
+ <marker id="arrowhead-success" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
|
|
+ <polygon points="0 0, 8 3, 0 6" fill="#16a34a"/>
|
|
+ </marker>
|
|
+ <marker id="arrowhead-error" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
|
|
+ <polygon points="0 0, 8 3, 0 6" fill="#dc2626"/>
|
|
+ </marker>
|
|
+ <marker id="arrowhead-retry" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
|
|
+ <polygon points="0 0, 8 3, 0 6" fill="#f59e0b"/>
|
|
+ </marker>
|
|
+ </defs>
|
|
+
|
|
+ <!-- Title -->
|
|
+ <text x="350" y="30" text-anchor="middle" class="title">Push-Model Agent State Machine</text>
|
|
+
|
|
+ <!-- Unregistered state -->
|
|
+ <rect x="50" y="60" width="160" height="55" class="state-initial"/>
|
|
+ <text x="130" y="85" class="state-text">Unregistered</text>
|
|
+ <text x="130" y="100" class="state-desc">Initial state</text>
|
|
+
|
|
+ <!-- Registered state -->
|
|
+ <rect x="300" y="60" width="160" height="55" class="state-active"/>
|
|
+ <text x="380" y="85" class="state-text">Registered</text>
|
|
+ <text x="380" y="100" class="state-desc">Ready for attestation</text>
|
|
+
|
|
+ <!-- Negotiating state -->
|
|
+ <rect x="300" y="190" width="160" height="55" class="state-active"/>
|
|
+ <text x="380" y="215" class="state-text">Negotiating</text>
|
|
+ <text x="380" y="230" class="state-desc">Phase 1: capabilities</text>
|
|
+
|
|
+ <!-- Attesting state -->
|
|
+ <rect x="300" y="320" width="160" height="55" class="state-active"/>
|
|
+ <text x="380" y="345" class="state-text">Attesting</text>
|
|
+ <text x="380" y="360" class="state-desc">Phase 2: evidence</text>
|
|
+
|
|
+ <!-- RegistrationFailed state -->
|
|
+ <rect x="50" y="190" width="160" height="55" class="state-error"/>
|
|
+ <text x="130" y="215" class="state-text">Reg. Failed</text>
|
|
+ <text x="130" y="230" class="state-desc">Will retry</text>
|
|
+
|
|
+ <!-- AttestationFailed state -->
|
|
+ <rect x="540" y="260" width="160" height="55" class="state-error"/>
|
|
+ <text x="620" y="285" class="state-text">Attest. Failed</text>
|
|
+ <text x="620" y="300" class="state-desc">Will retry</text>
|
|
+
|
|
+ <!-- Unregistered -> Registered (success) -->
|
|
+ <path d="M 210 87 L 295 87" class="arrow-success"/>
|
|
+ <text x="250" y="80" class="label-success">registration OK</text>
|
|
+
|
|
+ <!-- Unregistered -> RegistrationFailed (error) -->
|
|
+ <path d="M 130 115 L 130 185" class="arrow-error"/>
|
|
+ <text x="137" y="155" class="label-error">failed</text>
|
|
+
|
|
+ <!-- RegistrationFailed -> Unregistered (retry) -->
|
|
+ <path d="M 80 190 Q 55 155 80 115" class="arrow-retry"/>
|
|
+ <text x="30" y="155" class="label-retry">retry</text>
|
|
+
|
|
+ <!-- Registered -> Negotiating -->
|
|
+ <path d="M 380 115 L 380 185" class="arrow-success"/>
|
|
+ <text x="387" y="155" class="label-success">start negotiation</text>
|
|
+
|
|
+ <!-- Negotiating -> Attesting (success) -->
|
|
+ <path d="M 380 245 L 380 315" class="arrow-success"/>
|
|
+ <text x="387" y="283" class="label-success">201 Created</text>
|
|
+
|
|
+ <!-- Negotiating -> AttestationFailed (error) -->
|
|
+ <path d="M 460 220 L 535 275" class="arrow-error"/>
|
|
+ <text x="510" y="240" class="label-error">error</text>
|
|
+
|
|
+ <!-- Attesting -> Negotiating (loop - success) -->
|
|
+ <path d="M 300 345 Q 245 280 300 220" class="arrow-success"/>
|
|
+ <text x="228" y="280" class="label-success">202 Accepted</text>
|
|
+ <text x="228" y="292" class="label-success">(wait interval)</text>
|
|
+
|
|
+ <!-- Attesting -> AttestationFailed (error) -->
|
|
+ <path d="M 460 350 L 535 295" class="arrow-error"/>
|
|
+ <text x="510" y="335" class="label-error">rejected</text>
|
|
+
|
|
+ <!-- AttestationFailed -> Negotiating (retry) -->
|
|
+ <path d="M 620 260 Q 620 215 465 215" class="arrow-retry"/>
|
|
+ <text x="530" y="210" class="label-retry">retry</text>
|
|
+
|
|
+ <!-- Legend -->
|
|
+ <rect x="50" y="430" width="600" height="130" fill="#fafafa" stroke="#ddd" rx="5" ry="5"/>
|
|
+ <text x="70" y="455" font-weight="bold" font-size="13">Legend</text>
|
|
+
|
|
+ <line x1="70" y1="475" x2="120" y2="475" stroke="#16a34a" stroke-width="2"/>
|
|
+ <text x="130" y="480" class="label">Success transition</text>
|
|
+
|
|
+ <line x1="70" y1="495" x2="120" y2="495" stroke="#dc2626" stroke-width="2"/>
|
|
+ <text x="130" y="500" class="label">Error transition</text>
|
|
+
|
|
+ <line x1="70" y1="515" x2="120" y2="515" stroke="#f59e0b" stroke-width="2" stroke-dasharray="5,3"/>
|
|
+ <text x="130" y="520" class="label">Retry (with exponential backoff)</text>
|
|
+
|
|
+ <text x="350" y="480" class="label">Phase 1: Agent POSTs capabilities, receives challenge nonce</text>
|
|
+ <text x="350" y="500" class="label">Phase 2: Agent PATCHes evidence, receives 202 Accepted</text>
|
|
+ <text x="350" y="520" class="label">The Negotiating/Attesting cycle repeats continuously</text>
|
|
+</svg>
|
|
diff --git a/docs/conf.py b/docs/conf.py
|
|
index 5543afa86..00d9735de 100644
|
|
--- a/docs/conf.py
|
|
+++ b/docs/conf.py
|
|
@@ -154,6 +154,7 @@
|
|
("man/keylime_registrar.8", "keylime_registrar", "Keylime registrar service", [author], 8),
|
|
("man/keylime_verifier.8", "keylime_verifier", "Keylime verifier service", [author], 8),
|
|
("man/keylime_agent.8", "keylime_agent", "Keylime agent service", [author], 8),
|
|
+ ("man/keylime_push_model_agent.8", "keylime_push_model_agent", "Keylime push-model agent service", [author], 8),
|
|
]
|
|
|
|
|
|
diff --git a/docs/design.rst b/docs/design.rst
|
|
index 522ade113..dd72fd4e7 100644
|
|
--- a/docs/design.rst
|
|
+++ b/docs/design.rst
|
|
@@ -7,6 +7,7 @@ Design of Keylime
|
|
:caption: Contents:
|
|
|
|
design/overview.rst
|
|
+ design/push_model.rst
|
|
design/security.rst
|
|
|
|
|
|
diff --git a/docs/design/overview.rst b/docs/design/overview.rst
|
|
index 4c7b52227..985cbc94b 100644
|
|
--- a/docs/design/overview.rst
|
|
+++ b/docs/design/overview.rst
|
|
@@ -51,9 +51,14 @@ Verifier
|
|
The verifier implements the actual attestation of an agent and sends revocation messages if an agent leaves the trusted
|
|
state.
|
|
|
|
-Once an agent is registered for attestation (using the tenant or the API directly) the verifier continuously pulls
|
|
-the required attestation data from the agent. This can include: a quote over the PCRs, the PCR values, NK public key,
|
|
-IMA log and UEFI event log. After that the quote is validated additional validation of the data can be configured.
|
|
+In the default **pull model**, once an agent is registered for attestation (using the tenant or the API directly)
|
|
+the verifier continuously pulls the required attestation data from the agent. This can include: a quote over the
|
|
+PCRs, the PCR values, NK public key, IMA log and UEFI event log. After that the quote is validated additional
|
|
+validation of the data can be configured.
|
|
+
|
|
+Keylime also supports a **push model** where the agent initiates connections to the verifier and proactively
|
|
+submits attestation evidence. This is useful for environments where the verifier cannot directly reach the
|
|
+agent (e.g. behind firewalls or NAT). See :doc:`push_model` for details.
|
|
|
|
Static PCR values
|
|
"""""""""""""""""
|
|
diff --git a/docs/design/push_model.rst b/docs/design/push_model.rst
|
|
new file mode 100644
|
|
index 000000000..29f9061e0
|
|
--- /dev/null
|
|
+++ b/docs/design/push_model.rst
|
|
@@ -0,0 +1,226 @@
|
|
+========================
|
|
+Push-Model Attestation
|
|
+========================
|
|
+
|
|
+.. warning::
|
|
+ Push-model attestation is currently experimental. The feature is functional
|
|
+ but the API and configuration options may change in future releases.
|
|
+ Please report issues at https://github.com/keylime/keylime/issues/?q=label:push-mode
|
|
+
|
|
+Introduction
|
|
+------------
|
|
+
|
|
+Traditional Keylime attestation uses a **pull model** where the verifier continuously
|
|
+polls agents for attestation data. The agent acts as a server and the verifier initiates
|
|
+connections to it. This model requires that the verifier can reach the agent over the
|
|
+network.
|
|
+
|
|
+The **push model** reverses this communication direction: the agent initiates connections
|
|
+to the verifier and proactively sends attestation data. The verifier never connects to
|
|
+the agent. This makes push-model attestation suitable for environments where the
|
|
+verifier cannot directly reach the agent, such as:
|
|
+
|
|
+* **Edge and IoT devices** behind firewalls or NAT
|
|
+* **Hybrid cloud environments** with restricted network policies
|
|
+* **Air-gapped networks** where inbound connections to agents are not permitted
|
|
+* **Dynamic environments** where agent IP addresses change frequently
|
|
+
|
|
+In push mode, the agent is a separate binary (``keylime-push-model-agent``) that
|
|
+implements the push attestation protocol using API version 3.0.
|
|
+
|
|
+Architectural Overview
|
|
+----------------------
|
|
+
|
|
+In pull-model attestation, the verifier runs a polling loop that periodically contacts
|
|
+each registered agent to request a TPM quote and associated evidence. The agent exposes
|
|
+an HTTPS server that responds to these requests.
|
|
+
|
|
+In push-model attestation, this relationship is inverted:
|
|
+
|
|
+* The **agent initiates** all connections to the verifier
|
|
+* The agent does **not expose any HTTP endpoints** (no listening ports)
|
|
+* The verifier accepts incoming attestation data from agents
|
|
+* Verification is performed **asynchronously** after evidence is received
|
|
+* An **event-driven timeout** system replaces the polling loop for monitoring agent
|
|
+ liveness
|
|
+
|
|
+The registrar interaction is unchanged: in both models, the agent registers itself
|
|
+with the registrar during startup.
|
|
+
|
|
+.. figure:: ../assets/push-model-architecture.svg
|
|
+ :width: 600
|
|
+ :align: center
|
|
+ :alt: Diagram showing the push-model architecture where the agent initiates
|
|
+ connections to both the registrar and the verifier, contrasted with the pull
|
|
+ model where the verifier connects to the agent.
|
|
+
|
|
+ **Figure 1:** Push-Model Architecture
|
|
+
|
|
+The Two-Phase Attestation Protocol
|
|
+-----------------------------------
|
|
+
|
|
+Push-model attestation uses a two-phase protocol for each attestation cycle.
|
|
+
|
|
+Phase 1: Capabilities Negotiation
|
|
+""""""""""""""""""""""""""""""""""
|
|
+
|
|
+The agent begins an attestation cycle by sending its capabilities to the verifier.
|
|
+This tells the verifier what types of evidence the agent can produce and what
|
|
+cryptographic algorithms it supports.
|
|
+
|
|
+1. The agent sends a ``POST /v3/agents/{agent_id}/attestations`` request to the
|
|
+ verifier containing its supported evidence types (TPM quote parameters, IMA log
|
|
+ capabilities, UEFI log capabilities) and the public attestation key (AK).
|
|
+
|
|
+2. The verifier creates an attestation resource, selects cryptographic parameters
|
|
+ (signature scheme, hash algorithm, PCRs to quote), generates a random challenge
|
|
+ nonce, and returns a ``201 Created`` response with:
|
|
+
|
|
+ * The challenge nonce for TPM quote generation
|
|
+ * The chosen cryptographic parameters
|
|
+ * The evidence types requested
|
|
+ * A deadline (``challenges_expire_at``) by which evidence must be submitted
|
|
+
|
|
+Phase 2: Evidence Submission
|
|
+"""""""""""""""""""""""""""""
|
|
+
|
|
+The agent collects the requested evidence and submits it to the verifier.
|
|
+
|
|
+1. The agent generates a TPM quote using the challenge nonce from Phase 1,
|
|
+ collects IMA and/or UEFI event logs as requested, and sends a
|
|
+ ``PATCH /v3/agents/{agent_id}/attestations/latest`` request with the evidence.
|
|
+
|
|
+2. The verifier returns a ``202 Accepted`` response immediately. The evidence is
|
|
+ then verified asynchronously in a background worker process.
|
|
+
|
|
+3. If verification succeeds, the attestation is marked as ``pass``. If it fails,
|
|
+ the attestation is marked as ``fail`` with a failure reason
|
|
+ (``broken_evidence_chain`` or ``policy_violation``).
|
|
+
|
|
+4. The response includes a ``seconds_to_next_attestation`` value in the ``meta``
|
|
+ field, indicating when the agent should start its next attestation cycle.
|
|
+
|
|
+After a configurable interval, the agent begins a new cycle from Phase 1.
|
|
+
|
|
+Agent State Machine
|
|
+"""""""""""""""""""
|
|
+
|
|
+The push-model agent operates as a state machine with the following states:
|
|
+
|
|
+.. figure:: ../assets/push-model-sequence.svg
|
|
+ :width: 600
|
|
+ :align: center
|
|
+ :alt: Sequence diagram showing the push-model agent state machine transitions
|
|
+ from Unregistered through Registered, Negotiating, and Attesting states.
|
|
+
|
|
+ **Figure 2:** Push-Model Agent State Machine
|
|
+
|
|
+* **Unregistered**: Initial state. The agent registers with the registrar.
|
|
+* **Registered**: Registration succeeded. The agent begins negotiation with the
|
|
+ verifier.
|
|
+* **Negotiating**: The agent sends capabilities to the verifier (Phase 1) and waits
|
|
+ for the challenge response.
|
|
+* **Attesting**: The agent generates and sends evidence to the verifier (Phase 2).
|
|
+ On success, the agent waits for the configured interval and transitions back to
|
|
+ Negotiating.
|
|
+* **RegistrationFailed**: Registration with the registrar failed. The agent waits
|
|
+ and retries.
|
|
+* **AttestationFailed**: An attestation attempt failed (network error or verifier
|
|
+ rejection). The agent waits and retries from Negotiating.
|
|
+
|
|
+The agent uses exponential backoff when retrying failed operations.
|
|
+
|
|
+Authentication
|
|
+--------------
|
|
+
|
|
+Push-model attestation uses **Proof of Possession (PoP)** authentication instead of
|
|
+the mTLS client certificates used in pull mode. This is necessary because the agent
|
|
+acts as a client (not a server) and does not have certificates signed by the verifier's
|
|
+trusted CA.
|
|
+
|
|
+The PoP authentication flow:
|
|
+
|
|
+1. The agent creates a session by sending ``POST /v3/sessions`` with its agent ID
|
|
+ and supported authentication methods.
|
|
+2. The verifier responds with a challenge nonce.
|
|
+3. The agent proves possession of its AK by signing the challenge using the TPM
|
|
+ (``TPM2_Certify``) and sends the result via ``PATCH /v3/sessions/{session_id}``.
|
|
+4. If the signature is valid, the verifier issues a bearer token.
|
|
+5. The agent includes this token in the ``Authorization`` header of all subsequent
|
|
+ requests.
|
|
+6. Tokens have a configurable expiration time and can be refreshed.
|
|
+
|
|
+The TLS connection uses **server verification only**: the agent verifies the verifier's
|
|
+server certificate but does not present a client certificate. The agent needs the
|
|
+verifier's CA certificate for this verification.
|
|
+
|
|
+For full details on the authorization framework, including the separation between
|
|
+agent and admin authentication, see :doc:`../user_guide/authentication`.
|
|
+
|
|
+Timeout Monitoring
|
|
+------------------
|
|
+
|
|
+In pull mode, the verifier detects unresponsive agents through its polling loop. In
|
|
+push mode, an event-driven timeout system serves this purpose.
|
|
+
|
|
+The verifier monitors push-mode agents as follows:
|
|
+
|
|
+1. When the verifier receives an attestation from an agent, it schedules a timeout
|
|
+ for that agent. The timeout duration is ``quote_interval * 5`` seconds (where
|
|
+ ``quote_interval`` is the verifier's configured quote interval).
|
|
+
|
|
+2. If the agent does not submit a new attestation before the timeout fires, the
|
|
+ verifier sets the agent's ``accept_attestations`` flag to ``False``.
|
|
+
|
|
+3. Once ``accept_attestations`` is ``False``, the verifier rejects new attestation
|
|
+ requests from that agent with a ``403 Forbidden`` response.
|
|
+
|
|
+4. The agent can recover by re-registering or by administrator intervention
|
|
+ (reactivation).
|
|
+
|
|
+Comparison with Pull Model
|
|
+---------------------------
|
|
+
|
|
+.. list-table::
|
|
+ :header-rows: 1
|
|
+ :widths: 30 35 35
|
|
+
|
|
+ * - Aspect
|
|
+ - Pull Model
|
|
+ - Push Model
|
|
+ * - Connection direction
|
|
+ - Verifier connects to agent
|
|
+ - Agent connects to verifier
|
|
+ * - Agent binary
|
|
+ - ``keylime_agent``
|
|
+ - ``keylime_push_model_agent``
|
|
+ * - Agent network requirements
|
|
+ - Must expose HTTP port (default 9002)
|
|
+ - No listening ports required
|
|
+ * - Firewall requirements
|
|
+ - Inbound to agent from verifier
|
|
+ - Outbound from agent to verifier
|
|
+ * - Authentication method
|
|
+ - mTLS (agent as server)
|
|
+ - PoP bearer tokens (agent as client)
|
|
+ * - API version
|
|
+ - v2.x
|
|
+ - v3.0
|
|
+ * - Verification trigger
|
|
+ - Verifier polls on ``quote_interval``
|
|
+ - Agent pushes on ``attestation_interval_seconds``
|
|
+ * - Liveness detection
|
|
+ - Polling loop state machine
|
|
+ - Event-driven timeout (``quote_interval * 5``)
|
|
+ * - Verifier configuration
|
|
+ - ``mode = pull`` (default)
|
|
+ - ``mode = push``
|
|
+ * - Suitable for
|
|
+ - Controlled networks, data centers
|
|
+ - Edge, IoT, NAT, firewalled environments
|
|
+ * - Maturity
|
|
+ - Stable
|
|
+ - Experimental
|
|
+
|
|
+For deployment and configuration instructions, see :doc:`../user_guide/push_model`.
|
|
+For the v3.0 API reference, see :doc:`../rest_apis/3_0/3_0`.
|
|
diff --git a/docs/index.rst b/docs/index.rst
|
|
index 8234217fd..fd5f08bed 100644
|
|
--- a/docs/index.rst
|
|
+++ b/docs/index.rst
|
|
@@ -43,6 +43,7 @@ what the goals of Keylime are and how they are implemented.
|
|
man/keylime_verifier.8
|
|
man/keylime_registrar.8
|
|
man/keylime_agent.8
|
|
+ man/keylime_push_model_agent.8
|
|
man/keylime_policy.1
|
|
|
|
Indices and tables
|
|
diff --git a/docs/installation.rst b/docs/installation.rst
|
|
index 21d35a793..b96574137 100644
|
|
--- a/docs/installation.rst
|
|
+++ b/docs/installation.rst
|
|
@@ -62,6 +62,17 @@ Rust agent
|
|
|
|
Installation instructions can be found in the `README.md <https://github.com/keylime/rust-keylime>`_ for the Rust agent.
|
|
|
|
+Push-model agent
|
|
+~~~~~~~~~~~~~~~~
|
|
+.. note::
|
|
+ The push-model agent (``keylime-push-model-agent``) is a separate binary from
|
|
+ the standard Rust agent. It implements the push attestation protocol where the
|
|
+ agent initiates connections to the verifier. This feature is currently experimental.
|
|
+
|
|
+ Installation instructions are the same as for the Rust agent. The push-model
|
|
+ agent binary is built from the same repository. For configuration and deployment
|
|
+ details, see the :doc:`user_guide/push_model` user guide.
|
|
+
|
|
Keylime Bash installer
|
|
----------------------
|
|
|
|
diff --git a/docs/man/keylime_push_model_agent.8.rst b/docs/man/keylime_push_model_agent.8.rst
|
|
new file mode 100644
|
|
index 000000000..b033db801
|
|
--- /dev/null
|
|
+++ b/docs/man/keylime_push_model_agent.8.rst
|
|
@@ -0,0 +1,226 @@
|
|
+==========================
|
|
+keylime_push_model_agent
|
|
+==========================
|
|
+
|
|
+------------------------------------------------------------
|
|
+Keylime push-model agent for TPM-based remote attestation
|
|
+------------------------------------------------------------
|
|
+
|
|
+:Manual section: 8
|
|
+:Author: Keylime Developers
|
|
+:Date: February 2026
|
|
+
|
|
+SYNOPSIS
|
|
+========
|
|
+
|
|
+**keylime_push_model_agent** [*OPTIONS*]
|
|
+
|
|
+(Most operations require root privileges, use with sudo)
|
|
+
|
|
+DESCRIPTION
|
|
+===========
|
|
+
|
|
+The push-model agent is a long-running service that runs on systems to be attested.
|
|
+Unlike the standard Keylime agent which acts as a server and waits for the verifier
|
|
+to poll it, the push-model agent initiates connections to the verifier and proactively
|
|
+submits attestation evidence.
|
|
+
|
|
+The agent registers with the registrar, authenticates with the verifier using Proof of
|
|
+Possession (PoP), and performs periodic attestation cycles consisting of capabilities
|
|
+negotiation and evidence submission.
|
|
+
|
|
+This agent uses API version 3.0 and requires the verifier to be configured in push
|
|
+mode (``mode = push``).
|
|
+
|
|
+OPTIONS
|
|
+=======
|
|
+
|
|
+**--verifier-url** *URL*
|
|
+ URL of the verifier (must use HTTPS). Default: ``https://localhost:8881``
|
|
+
|
|
+**--registrar-url** *URL*
|
|
+ URL of the registrar. Default: ``http://127.0.0.1:8888``
|
|
+
|
|
+**--agent-identifier** *ID*
|
|
+ Agent UUID. Overrides the ``uuid`` configuration option.
|
|
+
|
|
+**--attestation-interval-seconds** *SECONDS*
|
|
+ Interval between attestation cycles. Default: ``60``
|
|
+
|
|
+**--ca-certificate** *PATH*
|
|
+ CA certificate file for verifying the verifier's TLS certificate. Overrides
|
|
+ ``verifier_tls_ca_cert``.
|
|
+
|
|
+**--api-version** *VERSION*
|
|
+ API version to use. Default: ``v3.0``
|
|
+
|
|
+**--timeout** *MILLISECONDS*
|
|
+ HTTP request timeout. Default: ``5000``
|
|
+
|
|
+**--insecure**
|
|
+ Accept invalid TLS certificates. For testing only.
|
|
+
|
|
+**--avoid-tpm**
|
|
+ Use a mock TPM instead of hardware TPM. For testing only.
|
|
+
|
|
+**--json-file** *FILE*
|
|
+ JSON file for payload data.
|
|
+
|
|
+**--attestation-index** *INDEX*
|
|
+ Attestation index value. Default: ``1``
|
|
+
|
|
+**--session-index** *INDEX*
|
|
+ Session index value. Default: ``1``
|
|
+
|
|
+**--message-type** *TYPE*
|
|
+ Message type (Attestation, EvidenceHandling, Session). Default: ``Attestation``
|
|
+
|
|
+**--method** *METHOD*
|
|
+ HTTP method. Default: ``POST``
|
|
+
|
|
+CONFIGURATION
|
|
+=============
|
|
+
|
|
+Primary configuration is read from ``/etc/keylime/agent.conf`` (TOML format).
|
|
+All options are under the ``[agent]`` section. Command-line arguments override
|
|
+configuration file values.
|
|
+
|
|
+Drop-in overrides: files in ``/etc/keylime/agent.conf.d/`` are applied in
|
|
+lexicographic order.
|
|
+
|
|
+Push-model specific options:
|
|
+
|
|
+**verifier_url**
|
|
+ URL of the verifier. Must use HTTPS. Default: ``https://localhost:8881``
|
|
+
|
|
+**verifier_tls_ca_cert**
|
|
+ Path to CA certificate for verifying the verifier's TLS certificate.
|
|
+ Relative paths are resolved from ``keylime_dir``. Default: ``cv_ca/cacert.crt``
|
|
+
|
|
+**attestation_interval_seconds**
|
|
+ Interval in seconds between attestation cycles. Default: ``60``
|
|
+
|
|
+**api_versions**
|
|
+ API versions to use. Default: ``3.0``
|
|
+
|
|
+**certification_keys_server_identifier**
|
|
+ Server identifier for attestation key certification. Default: ``ak``
|
|
+
|
|
+**uefi_logs_evidence_version**
|
|
+ UEFI logs evidence format version. Default: ``2.1``
|
|
+
|
|
+**exponential_backoff_initial_delay**
|
|
+ Initial retry delay in milliseconds. Default: ``10000``
|
|
+
|
|
+**exponential_backoff_max_retries**
|
|
+ Maximum number of retry attempts. Default: ``5``
|
|
+
|
|
+**exponential_backoff_max_delay**
|
|
+ Maximum retry delay in milliseconds. Default: ``300000``
|
|
+
|
|
+Shared options (same as standard agent):
|
|
+
|
|
+**uuid**
|
|
+ Agent identifier. Default: auto-generated UUID.
|
|
+
|
|
+**registrar_ip**, **registrar_port**
|
|
+ Registrar endpoint. Default: ``127.0.0.1:8890``
|
|
+
|
|
+**registrar_tls_enabled**
|
|
+ Enable TLS for registrar communication. Default: ``false``
|
|
+
|
|
+**registrar_tls_ca_cert**
|
|
+ CA certificate for registrar TLS verification. Default: ``cv_ca/cacert.crt``
|
|
+
|
|
+**tpm_hash_alg**, **tpm_encryption_alg**, **tpm_signing_alg**
|
|
+ TPM algorithms. Defaults: ``sha256``, ``rsa``, ``rsassa``
|
|
+
|
|
+**keylime_dir**
|
|
+ Working directory. Default: ``/var/lib/keylime``
|
|
+
|
|
+**run_as**
|
|
+ User:group to drop privileges to. Default: ``keylime:tss``
|
|
+
|
|
+**enable_iak_idevid**
|
|
+ Enable IAK/IDevID usage. Default: ``false``
|
|
+
|
|
+ENVIRONMENT
|
|
+===========
|
|
+
|
|
+**KEYLIME_AGENT_CONFIG**
|
|
+ Path to agent.conf (highest priority)
|
|
+
|
|
+**KEYLIME_DIR**
|
|
+ Working directory (default: ``/var/lib/keylime``)
|
|
+
|
|
+**RUST_LOG**
|
|
+ Log level configuration. Default in systemd service:
|
|
+ ``keylime_push_model_agent=info,keylime=info``
|
|
+
|
|
+All configuration options can be overridden via environment variables in the form
|
|
+``KEYLIME_AGENT_<OPTION_NAME>`` (e.g. ``KEYLIME_AGENT_VERIFIER_URL``).
|
|
+
|
|
+FILES
|
|
+=====
|
|
+
|
|
+``/etc/keylime/agent.conf``
|
|
+ TOML format configuration file (shared with standard agent)
|
|
+
|
|
+``/etc/keylime/agent.conf.d/``
|
|
+ Drop-in configuration snippets
|
|
+
|
|
+``/var/lib/keylime/cv_ca/cacert.crt``
|
|
+ Default CA certificate for verifier TLS verification
|
|
+
|
|
+``/var/lib/keylime/agent_data.json``
|
|
+ Persisted agent TPM data
|
|
+
|
|
+RUNTIME
|
|
+=======
|
|
+
|
|
+Start directly:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ sudo keylime_push_model_agent --verifier-url https://verifier.example.com:8881
|
|
+
|
|
+Start as a systemd service:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ sudo systemctl enable --now keylime_push_model_agent
|
|
+
|
|
+Check service status:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ sudo systemctl status keylime_push_model_agent
|
|
+ sudo journalctl -u keylime_push_model_agent -f
|
|
+
|
|
+PREREQUISITES
|
|
+=============
|
|
+
|
|
+- Root privileges (use sudo)
|
|
+- TPM 2.0 available (verify with ``tpm2_pcrread``)
|
|
+- Verifier configured with ``mode = push``
|
|
+- Network connectivity from agent to verifier and registrar
|
|
+- Verifier CA certificate available on agent machine
|
|
+
|
|
+NOTES
|
|
+=====
|
|
+
|
|
+- This service conflicts with ``keylime_agent.service``. Only one agent type can
|
|
+ run on a machine at a time.
|
|
+- The push-model agent does not expose any listening ports.
|
|
+- Push-model attestation is currently experimental.
|
|
+- Authentication uses PoP bearer tokens, not mTLS client certificates.
|
|
+
|
|
+SEE ALSO
|
|
+========
|
|
+
|
|
+**keylime_agent**\(8), **keylime_verifier**\(8), **keylime_registrar**\(8), **keylime_tenant**\(1)
|
|
+
|
|
+BUGS
|
|
+====
|
|
+
|
|
+Report bugs at https://github.com/keylime/rust-keylime/issues
|
|
diff --git a/docs/man/keylime_verifier.8.rst b/docs/man/keylime_verifier.8.rst
|
|
index fd7cfb941..5303a5f06 100644
|
|
--- a/docs/man/keylime_verifier.8.rst
|
|
+++ b/docs/man/keylime_verifier.8.rst
|
|
@@ -32,6 +32,7 @@ Primary configuration is read from ``/etc/keylime/verifier.conf`` (or an overrid
|
|
All options are under the ``[verifier]`` section.
|
|
|
|
Essentials:
|
|
+- **mode**: Attestation mode (``pull`` or ``push``). Default: ``pull``
|
|
- **uuid**: Unique identifier for this verifier instance
|
|
- **ip**, **port**: Bind address and HTTP port
|
|
- **registrar_ip**, **registrar_port**: Registrar endpoint
|
|
@@ -108,7 +109,7 @@ NOTES
|
|
SEE ALSO
|
|
========
|
|
|
|
-**keylime_registrar**\(8), **keylime_tenant**\(1), **keylime_agent**\(8)
|
|
+**keylime_registrar**\(8), **keylime_tenant**\(1), **keylime_agent**\(8), **keylime_push_model_agent**\(8)
|
|
|
|
BUGS
|
|
====
|
|
diff --git a/docs/rest_apis.rst b/docs/rest_apis.rst
|
|
index edfe8be1c..aba64c338 100644
|
|
--- a/docs/rest_apis.rst
|
|
+++ b/docs/rest_apis.rst
|
|
@@ -14,10 +14,40 @@ Check the :ref:`Changelog` section for the differences between versions
|
|
rest_apis/2_3/2_3.rst
|
|
rest_apis/2_4/2_4.rst
|
|
rest_apis/2_5/2_5.rst
|
|
+ rest_apis/3_0/3_0.rst
|
|
|
|
Changelog
|
|
_________
|
|
|
|
+Changes from v2.5 to v3.0
|
|
+~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
+
|
|
+API version 3.0 introduces push-model attestation. Unlike previous versions where
|
|
+the verifier polls agents, in v3.0 agents initiate connections and submit
|
|
+attestation evidence to the verifier. The v3.0 endpoints are served by the
|
|
+verifier only; the push-model agent does not expose HTTP endpoints.
|
|
+
|
|
+* Added `POST /v3/agents/{agent_id}/attestations` endpoint to the verifier:
|
|
+ * Allows agents to submit attestation capabilities (Phase 1 of push protocol)
|
|
+ * Returns challenge nonce for TPM quote generation
|
|
+* Added `PATCH /v3/agents/{agent_id}/attestations/latest` endpoint:
|
|
+ * Allows agents to submit attestation evidence (Phase 2 of push protocol)
|
|
+ * Returns `202 Accepted` for asynchronous verification
|
|
+* Added `PATCH /v3/agents/{agent_id}/attestations/{index}` endpoint:
|
|
+ * Submit evidence for a specific attestation by index
|
|
+* Added `GET /v3/agents/{agent_id}/attestations` endpoint:
|
|
+ * Lists all attestation records for an agent
|
|
+* Added `GET /v3/agents/{agent_id}/attestations/latest` endpoint:
|
|
+ * Returns the most recent attestation for an agent, including verification status
|
|
+* Added `GET /v3/agents/{agent_id}/attestations/{index}` endpoint:
|
|
+ * Returns a specific attestation by its index
|
|
+* Added `POST /v3/sessions` endpoint:
|
|
+ * Creates a PoP authentication session and returns a challenge nonce for the agent
|
|
+* Added `PATCH /v3/sessions/{session_id}` endpoint:
|
|
+ * Completes PoP authentication by submitting the TPM-signed challenge response
|
|
+* Introduced PoP (Proof of Possession) bearer token authentication for
|
|
+ agent-to-verifier communication
|
|
+
|
|
Changes from v2.4 to v2.5
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
API version 2.5 was first implemented in Keylime 7.14.0.
|
|
diff --git a/docs/rest_apis/3_0/3_0.rst b/docs/rest_apis/3_0/3_0.rst
|
|
new file mode 100644
|
|
index 000000000..d6cac705d
|
|
--- /dev/null
|
|
+++ b/docs/rest_apis/3_0/3_0.rst
|
|
@@ -0,0 +1,21 @@
|
|
+RESTful API for Keylime (v3.0)
|
|
+------------------------------
|
|
+
|
|
+API version 3.0 introduces push-model attestation, where agents initiate
|
|
+connections to the verifier and proactively submit attestation evidence.
|
|
+
|
|
+Unlike previous API versions where the agent exposed HTTP endpoints for the
|
|
+verifier to poll, in v3.0 the agent acts as a client. The v3.0 endpoints are
|
|
+served by the **verifier only**. The push-model agent does not expose an API.
|
|
+
|
|
+For a conceptual overview of push-model attestation, see
|
|
+:doc:`../../design/push_model`.
|
|
+
|
|
+.. warning::
|
|
+ Push-model attestation is currently experimental. The API may change in
|
|
+ future releases.
|
|
+
|
|
+.. toctree::
|
|
+ :maxdepth: 2
|
|
+
|
|
+ verifier.rst
|
|
diff --git a/docs/rest_apis/3_0/verifier.rst b/docs/rest_apis/3_0/verifier.rst
|
|
new file mode 100644
|
|
index 000000000..3476cc7a3
|
|
--- /dev/null
|
|
+++ b/docs/rest_apis/3_0/verifier.rst
|
|
@@ -0,0 +1,608 @@
|
|
+Verifier
|
|
+~~~~~~~~
|
|
+
|
|
+Push-Model Attestation Endpoints
|
|
+"""""""""""""""""""""""""""""""""
|
|
+
|
|
+These endpoints implement the two-phase push-model attestation protocol. Agents
|
|
+use these endpoints to submit attestation capabilities and evidence. Administrators
|
|
+can use the GET endpoints to view attestation results.
|
|
+
|
|
+For details on authentication requirements, see :doc:`../../user_guide/authentication`.
|
|
+
|
|
+.. http:post:: /v3/agents/{agent_id}/attestations
|
|
+
|
|
+ Phase 1: Submit attestation capabilities and receive a challenge.
|
|
+
|
|
+ The agent sends its supported evidence types, cryptographic algorithms, and
|
|
+ attestation key. The verifier selects parameters and returns a challenge nonce
|
|
+ for TPM quote generation.
|
|
+
|
|
+ :param agent_id: UUID of the agent
|
|
+ :type agent_id: string
|
|
+
|
|
+ **Authentication**: PoP bearer token (agent-only)
|
|
+
|
|
+ **Example request**:
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "attestation",
|
|
+ "attributes": {
|
|
+ "evidence_supported": [
|
|
+ {
|
|
+ "evidence_class": "certification",
|
|
+ "evidence_type": "tpm_quote",
|
|
+ "capabilities": {
|
|
+ "signature_schemes": ["rsassa"],
|
|
+ "hash_algorithms": ["sha256", "sha384", "sha512"],
|
|
+ "available_subjects": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
|
+ "certification_keys": [
|
|
+ {
|
|
+ "key_class": "asymmetric",
|
|
+ "key_algorithm": "rsa",
|
|
+ "key_size": 2048,
|
|
+ "server_identifier": "ak",
|
|
+ "allowable_signature_schemes": ["rsassa"],
|
|
+ "allowable_hash_algorithms": ["sha256", "sha384", "sha512"],
|
|
+ "public": "<base64-encoded AK public key>"
|
|
+ }
|
|
+ ],
|
|
+ "component_version": "2.0",
|
|
+ "evidence_version": "1.0"
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "evidence_class": "log",
|
|
+ "evidence_type": "ima_log",
|
|
+ "capabilities": {
|
|
+ "entry_count": 1024,
|
|
+ "supports_partial_access": true,
|
|
+ "appendable": true,
|
|
+ "formats": ["text/plain"],
|
|
+ "component_version": "1.0",
|
|
+ "evidence_version": "1.0"
|
|
+ }
|
|
+ }
|
|
+ ],
|
|
+ "system_info": {
|
|
+ "boot_time": "2024-01-15T10:30:00Z"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ **Example response** (201 Created):
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "attestation",
|
|
+ "id": "0",
|
|
+ "attributes": {
|
|
+ "stage": "awaiting_evidence",
|
|
+ "evidence_requested": [
|
|
+ {
|
|
+ "evidence_class": "certification",
|
|
+ "evidence_type": "tpm_quote",
|
|
+ "chosen_parameters": {
|
|
+ "challenge": "<base64-encoded nonce>",
|
|
+ "signature_scheme": "rsassa",
|
|
+ "hash_algorithm": "sha256",
|
|
+ "selected_subjects": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
|
+ "certification_key": {
|
|
+ "key_class": "asymmetric",
|
|
+ "key_algorithm": "rsa",
|
|
+ "key_size": 2048,
|
|
+ "server_identifier": "ak"
|
|
+ }
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "evidence_class": "log",
|
|
+ "evidence_type": "ima_log",
|
|
+ "chosen_parameters": {
|
|
+ "starting_offset": 0,
|
|
+ "entry_count": 1024,
|
|
+ "format": "text/plain"
|
|
+ }
|
|
+ }
|
|
+ ],
|
|
+ "system_info": {
|
|
+ "boot_time": "2024-01-15T10:30:00Z"
|
|
+ },
|
|
+ "capabilities_received_at": "2024-01-15T10:30:00.123456Z",
|
|
+ "challenges_expire_at": "2024-01-15T10:35:00.123456Z"
|
|
+ },
|
|
+ "links": {
|
|
+ "self": "/v3/agents/{agent_id}/attestations/0"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ :<json string data.type: Must be ``"attestation"``
|
|
+ :<json array data.attributes.evidence_supported: List of evidence types the agent can produce
|
|
+ :<json string evidence_supported[].evidence_class: ``"certification"`` or ``"log"``
|
|
+ :<json string evidence_supported[].evidence_type: ``"tpm_quote"``, ``"ima_log"``, or ``"uefi_log"``
|
|
+ :<json object evidence_supported[].capabilities: Capabilities for this evidence type
|
|
+ :<json object data.attributes.system_info: System information (e.g. boot time)
|
|
+ :>json string data.id: Attestation index (auto-incremented per agent)
|
|
+ :>json string data.attributes.stage: ``"awaiting_evidence"``
|
|
+ :>json array data.attributes.evidence_requested: Evidence the verifier wants the agent to provide
|
|
+ :>json string evidence_requested[].chosen_parameters.challenge: Base64-encoded challenge nonce for TPM quote
|
|
+ :>json string data.attributes.capabilities_received_at: ISO 8601 timestamp
|
|
+ :>json string data.attributes.challenges_expire_at: Deadline for evidence submission
|
|
+ :>json string data.links.self: URL to this attestation resource
|
|
+
|
|
+ :statuscode 201: Attestation created, challenge issued
|
|
+ :statuscode 400: Invalid request body
|
|
+ :statuscode 403: Attestations disabled for this agent (timeout or previous failure)
|
|
+ :statuscode 404: Agent not found
|
|
+ :statuscode 409: Concurrent attestation creation attempt
|
|
+ :statuscode 422: Invalid capabilities data
|
|
+ :statuscode 429: Rate limited (attestation interval not elapsed). Includes ``Retry-After`` header
|
|
+ :statuscode 503: Previous attestation still being verified. Includes ``Retry-After`` header
|
|
+
|
|
+
|
|
+.. http:patch:: /v3/agents/{agent_id}/attestations/latest
|
|
+
|
|
+ Phase 2: Submit attestation evidence for the latest attestation.
|
|
+
|
|
+ The agent sends the TPM quote, PCR values, and event logs generated using the
|
|
+ challenge nonce from Phase 1. The verifier accepts the evidence and verifies it
|
|
+ asynchronously.
|
|
+
|
|
+ :param agent_id: UUID of the agent
|
|
+ :type agent_id: string
|
|
+
|
|
+ **Authentication**: PoP bearer token (agent-only)
|
|
+
|
|
+ **Example request**:
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "attestation",
|
|
+ "attributes": {
|
|
+ "evidence_collected": [
|
|
+ {
|
|
+ "evidence_class": "certification",
|
|
+ "evidence_type": "tpm_quote",
|
|
+ "data": {
|
|
+ "subject_data": {
|
|
+ "0": "<PCR 0 value>",
|
|
+ "1": "<PCR 1 value>"
|
|
+ },
|
|
+ "message": "<base64-encoded TPM quote>",
|
|
+ "signature": "<base64-encoded quote signature>"
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "evidence_class": "log",
|
|
+ "evidence_type": "ima_log",
|
|
+ "data": {
|
|
+ "entry_count": 512,
|
|
+ "entries": "<base64-encoded or raw IMA log entries>"
|
|
+ }
|
|
+ }
|
|
+ ]
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ **Example response** (202 Accepted):
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "attestation",
|
|
+ "id": "0",
|
|
+ "attributes": {
|
|
+ "stage": "evaluating_evidence",
|
|
+ "evidence": [
|
|
+ {
|
|
+ "evidence_class": "certification",
|
|
+ "evidence_type": "tpm_quote",
|
|
+ "capabilities": {},
|
|
+ "chosen_parameters": {},
|
|
+ "data": {
|
|
+ "message": "<base64-encoded TPM quote>",
|
|
+ "signature": "<base64-encoded quote signature>",
|
|
+ "subject_data": {}
|
|
+ }
|
|
+ }
|
|
+ ],
|
|
+ "system_info": {
|
|
+ "boot_time": "2024-01-15T10:30:00Z"
|
|
+ },
|
|
+ "capabilities_received_at": "2024-01-15T10:30:00.123456Z",
|
|
+ "challenges_expire_at": "2024-01-15T10:35:00.123456Z",
|
|
+ "evidence_received_at": "2024-01-15T10:31:00.123456Z"
|
|
+ },
|
|
+ "links": {
|
|
+ "self": "/v3/agents/{agent_id}/attestations/0"
|
|
+ }
|
|
+ },
|
|
+ "meta": {
|
|
+ "seconds_to_next_attestation": 45
|
|
+ }
|
|
+ }
|
|
+
|
|
+ :<json string data.type: Must be ``"attestation"``
|
|
+ :<json array data.attributes.evidence_collected: List of evidence items
|
|
+ :<json string evidence_collected[].evidence_class: ``"certification"`` or ``"log"``
|
|
+ :<json string evidence_collected[].evidence_type: Type of evidence (must match what was requested)
|
|
+ :<json object evidence_collected[].data: Evidence data (format depends on evidence type)
|
|
+ :>json string data.attributes.stage: ``"evaluating_evidence"`` (verification in progress)
|
|
+ :>json array data.attributes.evidence: Evidence items with capabilities, parameters, and data
|
|
+ :>json string data.attributes.evidence_received_at: ISO 8601 timestamp when evidence was received
|
|
+ :>json int meta.seconds_to_next_attestation: Suggested wait before starting the next attestation cycle
|
|
+
|
|
+ :statuscode 202: Evidence accepted, verification in progress
|
|
+ :statuscode 400: Invalid evidence format
|
|
+ :statuscode 403: Evidence already submitted, attestation is not the latest, or challenges expired
|
|
+ :statuscode 404: Agent or attestation not found
|
|
+ :statuscode 410: Attestation no longer exists
|
|
+ :statuscode 503: No available worker processes. Includes ``Retry-After`` header
|
|
+
|
|
+
|
|
+.. http:patch:: /v3/agents/{agent_id}/attestations/{index}
|
|
+
|
|
+ Submit attestation evidence for a specific attestation by index.
|
|
+
|
|
+ Behaves identically to ``PATCH /v3/agents/{agent_id}/attestations/latest``
|
|
+ but targets a specific attestation index. Evidence can only be submitted for
|
|
+ the latest attestation.
|
|
+
|
|
+ :param agent_id: UUID of the agent
|
|
+ :type agent_id: string
|
|
+ :param index: Attestation index
|
|
+ :type index: integer
|
|
+
|
|
+ **Authentication**: PoP bearer token (agent-only)
|
|
+
|
|
+ :statuscode 202: Evidence accepted
|
|
+ :statuscode 403: Not the latest attestation, evidence already submitted, or challenges expired
|
|
+ :statuscode 404: Agent or attestation not found
|
|
+
|
|
+
|
|
+.. http:get:: /v3/agents/{agent_id}/attestations
|
|
+
|
|
+ List all attestations for an agent.
|
|
+
|
|
+ :param agent_id: UUID of the agent
|
|
+ :type agent_id: string
|
|
+
|
|
+ **Authentication**: mTLS (admin) or PoP bearer token (own agent only)
|
|
+
|
|
+ **Example response**:
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": [
|
|
+ {
|
|
+ "type": "attestation",
|
|
+ "id": "1",
|
|
+ "attributes": {
|
|
+ "stage": "verification_complete",
|
|
+ "evaluation": "pass",
|
|
+ "evidence": [],
|
|
+ "system_info": {
|
|
+ "boot_time": "2024-01-15T10:30:00Z"
|
|
+ },
|
|
+ "capabilities_received_at": "2024-01-15T10:30:00.123456Z",
|
|
+ "challenges_expire_at": "2024-01-15T10:35:00.123456Z",
|
|
+ "evidence_received_at": "2024-01-15T10:31:00.123456Z",
|
|
+ "verification_completed_at": "2024-01-15T10:32:00.123456Z"
|
|
+ },
|
|
+ "links": {
|
|
+ "self": "/v3/agents/{agent_id}/attestations/1"
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "type": "attestation",
|
|
+ "id": "0",
|
|
+ "attributes": {
|
|
+ "stage": "verification_complete",
|
|
+ "evaluation": "pass",
|
|
+ "evidence": [],
|
|
+ "system_info": {},
|
|
+ "capabilities_received_at": "2024-01-15T10:25:00.123456Z",
|
|
+ "challenges_expire_at": "2024-01-15T10:30:00.123456Z",
|
|
+ "evidence_received_at": "2024-01-15T10:26:00.123456Z",
|
|
+ "verification_completed_at": "2024-01-15T10:27:00.123456Z"
|
|
+ },
|
|
+ "links": {
|
|
+ "self": "/v3/agents/{agent_id}/attestations/0"
|
|
+ }
|
|
+ }
|
|
+ ]
|
|
+ }
|
|
+
|
|
+ :>json array data: List of attestation resources
|
|
+ :>json string data[].id: Attestation index
|
|
+ :>json string data[].attributes.stage: ``"awaiting_evidence"``, ``"evaluating_evidence"``, or ``"verification_complete"``
|
|
+ :>json string data[].attributes.evaluation: ``"pending"``, ``"pass"``, or ``"fail"``
|
|
+ :>json string data[].attributes.failure_reason: ``"broken_evidence_chain"`` or ``"policy_violation"`` (only when evaluation is ``"fail"``)
|
|
+
|
|
+ :statuscode 200: Success
|
|
+ :statuscode 404: Agent not found
|
|
+
|
|
+
|
|
+.. http:get:: /v3/agents/{agent_id}/attestations/latest
|
|
+
|
|
+ Get the latest attestation for an agent.
|
|
+
|
|
+ :param agent_id: UUID of the agent
|
|
+ :type agent_id: string
|
|
+
|
|
+ **Authentication**: mTLS (admin) or PoP bearer token (own agent only)
|
|
+
|
|
+ **Example response**:
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "attestation",
|
|
+ "id": "1",
|
|
+ "attributes": {
|
|
+ "stage": "verification_complete",
|
|
+ "evaluation": "pass",
|
|
+ "failure_reason": null,
|
|
+ "evidence": [
|
|
+ {
|
|
+ "evidence_class": "certification",
|
|
+ "evidence_type": "tpm_quote",
|
|
+ "capabilities": {},
|
|
+ "chosen_parameters": {},
|
|
+ "data": {
|
|
+ "message": "<base64-encoded TPM quote>",
|
|
+ "signature": "<base64-encoded signature>",
|
|
+ "subject_data": {}
|
|
+ }
|
|
+ }
|
|
+ ],
|
|
+ "system_info": {
|
|
+ "boot_time": "2024-01-15T10:30:00Z"
|
|
+ },
|
|
+ "capabilities_received_at": "2024-01-15T10:30:00.123456Z",
|
|
+ "challenges_expire_at": "2024-01-15T10:35:00.123456Z",
|
|
+ "evidence_received_at": "2024-01-15T10:31:00.123456Z",
|
|
+ "verification_completed_at": "2024-01-15T10:32:00.123456Z"
|
|
+ },
|
|
+ "links": {
|
|
+ "self": "/v3/agents/{agent_id}/attestations/1"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ :>json string data.attributes.stage: Current stage of the attestation
|
|
+ :>json string data.attributes.evaluation: ``"pending"``, ``"pass"``, or ``"fail"``
|
|
+ :>json string data.attributes.failure_reason: ``null``, ``"broken_evidence_chain"``, or ``"policy_violation"``
|
|
+ :>json array data.attributes.evidence: Evidence items with full data
|
|
+ :>json string data.attributes.capabilities_received_at: When capabilities were received
|
|
+ :>json string data.attributes.challenges_expire_at: When challenges expire
|
|
+ :>json string data.attributes.evidence_received_at: When evidence was received (``null`` if still awaiting)
|
|
+ :>json string data.attributes.verification_completed_at: When verification completed (``null`` if still in progress)
|
|
+
|
|
+ :statuscode 200: Success
|
|
+ :statuscode 404: Agent not found or no attestations exist
|
|
+
|
|
+
|
|
+.. http:get:: /v3/agents/{agent_id}/attestations/{index}
|
|
+
|
|
+ Get a specific attestation by index.
|
|
+
|
|
+ :param agent_id: UUID of the agent
|
|
+ :type agent_id: string
|
|
+ :param index: Attestation index
|
|
+ :type index: integer
|
|
+
|
|
+ **Authentication**: mTLS (admin) or PoP bearer token (own agent only)
|
|
+
|
|
+ Response format is identical to ``GET /v3/agents/{agent_id}/attestations/latest``.
|
|
+
|
|
+ :statuscode 200: Success
|
|
+ :statuscode 404: Agent or attestation not found
|
|
+
|
|
+
|
|
+Session Endpoints
|
|
+"""""""""""""""""
|
|
+
|
|
+These endpoints manage PoP (Proof of Possession) authentication sessions for
|
|
+push-model agents. Sessions are required before an agent can submit attestations.
|
|
+
|
|
+.. http:post:: /v3/sessions
|
|
+
|
|
+ Create a new authentication session.
|
|
+
|
|
+ The verifier generates a challenge nonce that the agent must sign using its
|
|
+ TPM attestation key to prove possession.
|
|
+
|
|
+ **Authentication**: None (public endpoint)
|
|
+
|
|
+ **Example request**:
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "session",
|
|
+ "attributes": {
|
|
+ "agent_id": "d432fbb3-d2f1-4a97-9ef7-75bd81c00000",
|
|
+ "authentication_supported": [
|
|
+ {
|
|
+ "authentication_class": "pop",
|
|
+ "authentication_type": "tpm_pop"
|
|
+ }
|
|
+ ]
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ **Example response** (200 OK):
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "session",
|
|
+ "id": "550e8400-e29b-41d4-a716-446655440000",
|
|
+ "attributes": {
|
|
+ "agent_id": "d432fbb3-d2f1-4a97-9ef7-75bd81c00000",
|
|
+ "authentication_requested": [
|
|
+ {
|
|
+ "authentication_class": "pop",
|
|
+ "authentication_type": "tpm_pop",
|
|
+ "chosen_parameters": {
|
|
+ "challenge": "<base64-encoded nonce>"
|
|
+ }
|
|
+ }
|
|
+ ],
|
|
+ "created_at": "2024-01-15T10:30:00.123456Z",
|
|
+ "challenges_expire_at": "2024-01-15T10:31:00.123456Z"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ :<json string data.attributes.agent_id: UUID of the agent requesting a session
|
|
+ :<json array data.attributes.authentication_supported: Supported authentication methods
|
|
+ :>json string data.id: Session UUID
|
|
+ :>json string data.attributes.challenges_expire_at: Deadline for submitting the PoP response
|
|
+
|
|
+ :statuscode 200: Session created
|
|
+ :statuscode 400: Missing or invalid agent_id
|
|
+ :statuscode 429: Rate limited. Includes ``Retry-After`` header
|
|
+
|
|
+
|
|
+.. http:patch:: /v3/sessions/{session_id}
|
|
+
|
|
+ Submit Proof of Possession response to complete authentication.
|
|
+
|
|
+ The agent signs the challenge nonce from the session creation response using
|
|
+ ``TPM2_Certify`` and submits the result. If valid, the verifier issues a bearer
|
|
+ token for subsequent API calls.
|
|
+
|
|
+ :param session_id: UUID of the session
|
|
+ :type session_id: string
|
|
+
|
|
+ **Authentication**: None (public endpoint; validates PoP internally)
|
|
+
|
|
+ **Example request**:
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "session",
|
|
+ "attributes": {
|
|
+ "agent_id": "d432fbb3-d2f1-4a97-9ef7-75bd81c00000",
|
|
+ "authentication_provided": [
|
|
+ {
|
|
+ "authentication_class": "pop",
|
|
+ "authentication_type": "tpm_pop",
|
|
+ "data": {
|
|
+ "message": "<base64-encoded AK attest structure>",
|
|
+ "signature": "<base64-encoded AK signature>"
|
|
+ }
|
|
+ }
|
|
+ ]
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ **Example response** (200 OK, authentication passed):
|
|
+
|
|
+ .. sourcecode:: json
|
|
+
|
|
+ {
|
|
+ "data": {
|
|
+ "type": "session",
|
|
+ "id": "550e8400-e29b-41d4-a716-446655440000",
|
|
+ "attributes": {
|
|
+ "agent_id": "d432fbb3-d2f1-4a97-9ef7-75bd81c00000",
|
|
+ "evaluation": "pass",
|
|
+ "token": "550e8400-e29b-41d4-a716-446655440000.<secret>",
|
|
+ "authentication": [
|
|
+ {
|
|
+ "authentication_class": "pop",
|
|
+ "authentication_type": "tpm_pop",
|
|
+ "chosen_parameters": {
|
|
+ "challenge": "<base64-encoded nonce>"
|
|
+ },
|
|
+ "data": {
|
|
+ "message": "<base64-encoded AK attest>",
|
|
+ "signature": "<base64-encoded AK signature>"
|
|
+ }
|
|
+ }
|
|
+ ],
|
|
+ "created_at": "2024-01-15T10:30:00.123456Z",
|
|
+ "challenges_expire_at": "2024-01-15T10:31:00.123456Z",
|
|
+ "response_received_at": "2024-01-15T10:30:30.123456Z",
|
|
+ "token_expires_at": "2024-01-15T11:30:00.123456Z"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ :>json string data.attributes.evaluation: ``"pass"`` or ``"fail"``
|
|
+ :>json string data.attributes.token: Bearer token for subsequent requests (only on ``"pass"``)
|
|
+ :>json string data.attributes.token_expires_at: Token expiration time (only on ``"pass"``)
|
|
+
|
|
+ :statuscode 200: PoP response processed (check ``evaluation`` field for result)
|
|
+ :statuscode 400: Missing or invalid request body
|
|
+ :statuscode 401: PoP verification failed
|
|
+ :statuscode 404: Session not found
|
|
+
|
|
+
|
|
+Attestation Stages and Evaluations
|
|
+"""""""""""""""""""""""""""""""""""
|
|
+
|
|
+Each attestation progresses through the following stages:
|
|
+
|
|
+.. list-table::
|
|
+ :header-rows: 1
|
|
+ :widths: 25 75
|
|
+
|
|
+ * - Stage
|
|
+ - Description
|
|
+ * - ``awaiting_evidence``
|
|
+ - Capabilities received, challenge issued, waiting for evidence
|
|
+ * - ``evaluating_evidence``
|
|
+ - Evidence received, verification in progress
|
|
+ * - ``verification_complete``
|
|
+ - Verification finished, see ``evaluation`` for result
|
|
+
|
|
+The ``evaluation`` field indicates the verification result:
|
|
+
|
|
+.. list-table::
|
|
+ :header-rows: 1
|
|
+ :widths: 20 80
|
|
+
|
|
+ * - Evaluation
|
|
+ - Description
|
|
+ * - ``pending``
|
|
+ - Verification not yet complete
|
|
+ * - ``pass``
|
|
+ - Evidence verified successfully
|
|
+ * - ``fail``
|
|
+ - Evidence verification failed (see ``failure_reason``)
|
|
+
|
|
+When an attestation fails, the ``failure_reason`` field provides the cause:
|
|
+
|
|
+.. list-table::
|
|
+ :header-rows: 1
|
|
+ :widths: 30 70
|
|
+
|
|
+ * - Failure Reason
|
|
+ - Description
|
|
+ * - ``broken_evidence_chain``
|
|
+ - TPM quote signature invalid or evidence integrity check failed
|
|
+ * - ``policy_violation``
|
|
+ - Evidence is valid but violates the configured attestation policy
|
|
diff --git a/docs/user_guide.rst b/docs/user_guide.rst
|
|
index 9bd44c512..ed052c175 100644
|
|
--- a/docs/user_guide.rst
|
|
+++ b/docs/user_guide.rst
|
|
@@ -8,6 +8,7 @@ User Guide
|
|
|
|
user_guide/authentication.rst
|
|
user_guide/configuration.rst
|
|
+ user_guide/push_model.rst
|
|
user_guide/runtime_ima.rst
|
|
user_guide/user_selected_pcr_monitoring.rst
|
|
user_guide/use_measured_boot.rst
|
|
diff --git a/docs/user_guide/configuration.rst b/docs/user_guide/configuration.rst
|
|
index 6d8f35c88..2e50757df 100644
|
|
--- a/docs/user_guide/configuration.rst
|
|
+++ b/docs/user_guide/configuration.rst
|
|
@@ -40,6 +40,13 @@ The following components can be configured:
|
|
- ``/etc/keylime/logging.conf``
|
|
- ``/etc/keylime/logging.conf.d``
|
|
|
|
+.. note::
|
|
+ For push-model attestation, the verifier must be configured with ``mode = push``
|
|
+ in the ``[verifier]`` section. The push-model agent uses the same
|
|
+ ``/etc/keylime/agent.conf`` file (TOML format) but with additional options such
|
|
+ as ``verifier_url`` and ``attestation_interval_seconds``. See
|
|
+ :doc:`push_model` for details.
|
|
+
|
|
The next sections contain details of the configuration files
|
|
|
|
Configuration file processing order
|
|
diff --git a/docs/user_guide/push_model.rst b/docs/user_guide/push_model.rst
|
|
new file mode 100644
|
|
index 000000000..773d2aaaa
|
|
--- /dev/null
|
|
+++ b/docs/user_guide/push_model.rst
|
|
@@ -0,0 +1,370 @@
|
|
+========================
|
|
+Push-Model Attestation
|
|
+========================
|
|
+
|
|
+.. warning::
|
|
+ Push-model attestation is currently experimental. The feature is functional
|
|
+ but the API and configuration options may change in future releases.
|
|
+
|
|
+Introduction
|
|
+------------
|
|
+
|
|
+In the default pull model, the Keylime verifier continuously polls agents for
|
|
+attestation data. This requires the verifier to reach the agent over the network.
|
|
+
|
|
+The push model reverses this: the agent initiates connections to the verifier and
|
|
+proactively sends attestation evidence. This is useful when the verifier cannot
|
|
+directly reach the agent, for example behind firewalls, NAT, or in edge/IoT
|
|
+deployments.
|
|
+
|
|
+For a detailed description of how push-model attestation works, see
|
|
+:doc:`../design/push_model`.
|
|
+
|
|
+Prerequisites
|
|
+-------------
|
|
+
|
|
+* Keylime verifier and registrar installed and running
|
|
+* The ``keylime-push-model-agent`` binary installed on the target machine
|
|
+* A TPM 2.0 device (hardware or emulated for development)
|
|
+* Network connectivity **from the agent to the verifier and registrar** (the
|
|
+ reverse is not required)
|
|
+* The verifier's CA certificate available on the agent machine
|
|
+
|
|
+Configuring the Verifier for Push Mode
|
|
+--------------------------------------
|
|
+
|
|
+Set the verifier's attestation mode to ``push`` in ``/etc/keylime/verifier.conf``:
|
|
+
|
|
+.. code-block:: ini
|
|
+
|
|
+ [verifier]
|
|
+ mode = push
|
|
+
|
|
+Or use a configuration snippet in ``/etc/keylime/verifier.conf.d/``:
|
|
+
|
|
+.. code-block:: ini
|
|
+
|
|
+ # /etc/keylime/verifier.conf.d/001-push-mode.conf
|
|
+ [verifier]
|
|
+ mode = push
|
|
+
|
|
+The verifier can also be configured via environment variable:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ export KEYLIME_VERIFIER_MODE=push
|
|
+
|
|
+.. note::
|
|
+ The ``mode`` setting affects all agents on this verifier. A verifier in push
|
|
+ mode expects agents to submit attestation data; it does not poll agents. A
|
|
+ single verifier cannot operate in both modes simultaneously.
|
|
+
|
|
+Additional verifier settings relevant to push mode:
|
|
+
|
|
+* ``quote_interval``: Used to calculate the agent timeout threshold
|
|
+ (``quote_interval * 5``). Default: ``2`` seconds.
|
|
+* ``challenge_lifetime``: How long a challenge nonce remains valid for evidence
|
|
+ submission.
|
|
+* ``verification_timeout``: Maximum time allowed for evidence verification.
|
|
+
|
|
+After changing the configuration, restart the verifier:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ sudo systemctl restart keylime_verifier
|
|
+
|
|
+Configuring the Push-Model Agent
|
|
+---------------------------------
|
|
+
|
|
+The push-model agent is a separate binary from the standard Keylime agent. It is
|
|
+installed as ``keylime_push_model_agent`` (or ``keylime-push-model-agent``).
|
|
+
|
|
+The agent is configured through ``/etc/keylime/agent.conf`` (TOML format), command-line
|
|
+arguments, or environment variables.
|
|
+
|
|
+Key Configuration Options
|
|
+"""""""""""""""""""""""""
|
|
+
|
|
+The following options are specific to or particularly important for push-model
|
|
+operation:
|
|
+
|
|
+.. list-table::
|
|
+ :header-rows: 1
|
|
+ :widths: 30 15 55
|
|
+
|
|
+ * - Option
|
|
+ - Default
|
|
+ - Description
|
|
+ * - ``verifier_url``
|
|
+ - ``https://localhost:8881``
|
|
+ - URL of the verifier. Must use HTTPS.
|
|
+ * - ``verifier_tls_ca_cert``
|
|
+ - ``cv_ca/cacert.crt``
|
|
+ - Path to the CA certificate for verifying the verifier's TLS certificate.
|
|
+ Relative paths are resolved from ``keylime_dir``.
|
|
+ * - ``attestation_interval_seconds``
|
|
+ - ``60``
|
|
+ - Interval in seconds between attestation cycles.
|
|
+ * - ``registrar_ip``
|
|
+ - ``127.0.0.1``
|
|
+ - IP address of the registrar.
|
|
+ * - ``registrar_port``
|
|
+ - ``8890``
|
|
+ - Port of the registrar.
|
|
+ * - ``registrar_tls_enabled``
|
|
+ - ``false``
|
|
+ - Enable TLS for registrar communication.
|
|
+ * - ``registrar_tls_ca_cert``
|
|
+ - ``cv_ca/cacert.crt``
|
|
+ - CA certificate for registrar TLS verification.
|
|
+ * - ``uuid``
|
|
+ - (generated)
|
|
+ - Agent UUID. Can be a specific UUID, ``generate`` (random), or
|
|
+ ``hash_ek`` (derived from the EK).
|
|
+ * - ``api_versions``
|
|
+ - ``3.0``
|
|
+ - API versions supported by the agent. Defaults to ``3.0`` for push model.
|
|
+ * - ``tpm_hash_alg``
|
|
+ - ``sha256``
|
|
+ - TPM hash algorithm (``sha256``, ``sha384``, ``sha512``).
|
|
+ * - ``tpm_signing_alg``
|
|
+ - ``rsassa``
|
|
+ - TPM signing algorithm (``rsassa``, ``ecdsa``).
|
|
+ * - ``keylime_dir``
|
|
+ - ``/var/lib/keylime``
|
|
+ - Working directory for certificates and data files.
|
|
+
|
|
+Example Minimal Configuration
|
|
+""""""""""""""""""""""""""""""
|
|
+
|
|
+.. code-block:: toml
|
|
+
|
|
+ # /etc/keylime/agent.conf (push-model agent)
|
|
+ [agent]
|
|
+ uuid = "d432fbb3-d2f1-4a97-9ef7-75bd81c00000"
|
|
+ verifier_url = "https://verifier.example.com:8881"
|
|
+ verifier_tls_ca_cert = "/var/lib/keylime/cv_ca/cacert.crt"
|
|
+ attestation_interval_seconds = 60
|
|
+ registrar_ip = "registrar.example.com"
|
|
+ registrar_port = 8890
|
|
+ tpm_hash_alg = "sha256"
|
|
+ tpm_signing_alg = "rsassa"
|
|
+
|
|
+Command-Line Arguments
|
|
+""""""""""""""""""""""
|
|
+
|
|
+The push-model agent accepts the following command-line arguments, which override
|
|
+configuration file values:
|
|
+
|
|
+.. code-block:: text
|
|
+
|
|
+ --verifier-url <URL> Verifier URL (required)
|
|
+ --registrar-url <URL> Registrar URL (default: http://127.0.0.1:8888)
|
|
+ --agent-identifier <ID> Agent UUID
|
|
+ --attestation-interval-seconds <SECS> Attestation interval (default: 60)
|
|
+ --ca-certificate <PATH> CA certificate for TLS verification
|
|
+ --api-version <VERSION> API version (default: v3.0)
|
|
+ --timeout <MS> Request timeout in milliseconds (default: 5000)
|
|
+ --insecure Accept invalid TLS certificates (testing only)
|
|
+ --avoid-tpm Use mock TPM (testing only)
|
|
+
|
|
+Exponential Backoff
|
|
+"""""""""""""""""""
|
|
+
|
|
+When the agent encounters errors (network failures, verifier unavailable), it uses
|
|
+exponential backoff for retries:
|
|
+
|
|
+.. list-table::
|
|
+ :header-rows: 1
|
|
+ :widths: 35 15 50
|
|
+
|
|
+ * - Option
|
|
+ - Default
|
|
+ - Description
|
|
+ * - ``exponential_backoff_initial_delay``
|
|
+ - ``10000``
|
|
+ - Initial delay in milliseconds (10 seconds)
|
|
+ * - ``exponential_backoff_max_retries``
|
|
+ - ``5``
|
|
+ - Maximum number of retry attempts
|
|
+ * - ``exponential_backoff_max_delay``
|
|
+ - ``300000``
|
|
+ - Maximum delay in milliseconds (5 minutes)
|
|
+
|
|
+Systemd Service Management
|
|
+---------------------------
|
|
+
|
|
+The push-model agent is managed as a systemd service:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ # Enable the service to start on boot
|
|
+ sudo systemctl enable keylime_push_model_agent
|
|
+
|
|
+ # Start the service
|
|
+ sudo systemctl start keylime_push_model_agent
|
|
+
|
|
+ # Check service status
|
|
+ sudo systemctl status keylime_push_model_agent
|
|
+
|
|
+ # View logs
|
|
+ sudo journalctl -u keylime_push_model_agent -f
|
|
+
|
|
+.. warning::
|
|
+ The push-model agent service (``keylime_push_model_agent.service``) conflicts
|
|
+ with the standard pull-model agent service (``keylime_agent.service``). Only one
|
|
+ can run at a time on the same machine. Starting one will stop the other.
|
|
+
|
|
+The service is configured to restart on failure with a 120-second delay between
|
|
+restart attempts.
|
|
+
|
|
+Enrolling an Agent for Push-Model Attestation
|
|
+---------------------------------------------
|
|
+
|
|
+Use the ``keylime_tenant`` tool with the ``--push-model`` flag to enroll an agent
|
|
+for push-model attestation:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ # Add an agent in push mode
|
|
+ sudo keylime_tenant -c add --push-model -u <agent-uuid>
|
|
+
|
|
+ # Add with a runtime IMA policy
|
|
+ sudo keylime_tenant -c add --push-model -u <agent-uuid> \
|
|
+ --runtime-policy-name <policy-name>
|
|
+
|
|
+ # Add with a measured boot policy
|
|
+ sudo keylime_tenant -c add --push-model -u <agent-uuid> \
|
|
+ --mb-policy-name <policy-name>
|
|
+
|
|
+.. note::
|
|
+ In push mode, the ``-t`` / ``--targethost`` option is not required because the
|
|
+ verifier does not need to connect to the agent. The agent's IP and port are set
|
|
+ to ``None`` in the verifier's database.
|
|
+
|
|
+To check the status of a push-model agent:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ sudo keylime_tenant -c cvstatus -u <agent-uuid>
|
|
+
|
|
+To remove an agent:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ sudo keylime_tenant -c delete -u <agent-uuid>
|
|
+
|
|
+TLS Configuration for Push Model
|
|
+---------------------------------
|
|
+
|
|
+The push model uses TLS differently from the pull model:
|
|
+
|
|
+**Agent-to-verifier connection:**
|
|
+
|
|
+* The agent connects to the verifier over HTTPS
|
|
+* The agent verifies the verifier's server certificate using the configured CA
|
|
+ certificate (``verifier_tls_ca_cert``)
|
|
+* The agent does **not** present a client certificate (no mTLS)
|
|
+* Authentication is done via PoP bearer tokens (see :doc:`authentication`)
|
|
+
|
|
+**Agent-to-registrar connection:**
|
|
+
|
|
+* The agent connects to the registrar to register itself
|
|
+* TLS can be enabled with ``registrar_tls_enabled = true``
|
|
+* The registrar CA certificate is configured with ``registrar_tls_ca_cert``
|
|
+
|
|
+**Firewall considerations:**
|
|
+
|
|
+* No inbound ports need to be opened on the agent machine
|
|
+* The agent needs outbound access to the verifier port (default: 8881)
|
|
+* The agent needs outbound access to the registrar port (default: 8890)
|
|
+
|
|
+To set up TLS, copy the verifier's CA certificate to the agent machine:
|
|
+
|
|
+.. code-block:: bash
|
|
+
|
|
+ # On the verifier machine, the CA cert is typically at:
|
|
+ # /var/lib/keylime/cv_ca/cacert.crt
|
|
+
|
|
+ # Copy to the agent machine:
|
|
+ scp verifier:/var/lib/keylime/cv_ca/cacert.crt /var/lib/keylime/cv_ca/cacert.crt
|
|
+
|
|
+Verifying the Deployment
|
|
+-------------------------
|
|
+
|
|
+After starting both the verifier (in push mode) and the push-model agent:
|
|
+
|
|
+1. **Check agent registration** in the registrar:
|
|
+
|
|
+ .. code-block:: bash
|
|
+
|
|
+ sudo keylime_tenant -c regstatus -u <agent-uuid>
|
|
+
|
|
+2. **Check attestation status** in the verifier:
|
|
+
|
|
+ .. code-block:: bash
|
|
+
|
|
+ sudo keylime_tenant -c cvstatus -u <agent-uuid>
|
|
+
|
|
+3. **View verifier logs** for attestation activity:
|
|
+
|
|
+ .. code-block:: bash
|
|
+
|
|
+ sudo journalctl -u keylime_verifier -f
|
|
+
|
|
+ Successful attestations will show evidence receipt and verification completion
|
|
+ messages.
|
|
+
|
|
+4. **View agent logs** for attestation cycles:
|
|
+
|
|
+ .. code-block:: bash
|
|
+
|
|
+ sudo journalctl -u keylime_push_model_agent -f
|
|
+
|
|
+ The agent logs will show transitions through the state machine:
|
|
+ registration, negotiation, and attestation phases.
|
|
+
|
|
+Troubleshooting
|
|
+----------------
|
|
+
|
|
+Agent cannot connect to verifier
|
|
+"""""""""""""""""""""""""""""""""
|
|
+
|
|
+* Verify the ``verifier_url`` is correct and uses HTTPS
|
|
+* Check that the verifier is running and listening on the configured port
|
|
+* Verify network connectivity from the agent to the verifier
|
|
+* Check that the CA certificate (``verifier_tls_ca_cert``) matches the verifier's
|
|
+ server certificate
|
|
+
|
|
+Agent shows timeout failures
|
|
+"""""""""""""""""""""""""""""
|
|
+
|
|
+The verifier marks an agent as failed if it does not receive an attestation within
|
|
+``quote_interval * 5`` seconds.
|
|
+
|
|
+* Verify the ``attestation_interval_seconds`` on the agent is less than the
|
|
+ verifier's timeout threshold
|
|
+* Check for network instability between agent and verifier
|
|
+* Review agent logs for errors during attestation cycles
|
|
+
|
|
+PoP authentication errors
|
|
+""""""""""""""""""""""""""
|
|
+
|
|
+* Ensure the agent is properly registered in the registrar (the AK must be known)
|
|
+* Check that the TPM is accessible and functioning
|
|
+* Verify the agent UUID matches between agent configuration and verifier enrollment
|
|
+
|
|
+Agent state stuck in Negotiating
|
|
+"""""""""""""""""""""""""""""""""
|
|
+
|
|
+* The verifier may be rejecting capabilities. Check verifier logs for error details
|
|
+* Ensure the TPM algorithms configured on the agent are accepted by the verifier
|
|
+* Check that the ``api_versions`` setting includes ``3.0``
|
|
+
|
|
+Service fails to start
|
|
+""""""""""""""""""""""
|
|
+
|
|
+* Check that the pull-model agent service is not running
|
|
+ (``systemctl status keylime_agent``)
|
|
+* Verify the configuration file syntax (TOML format)
|
|
+* Check file permissions on TLS certificates and TPM device
|