Skip to content

Message Attestation: example with out-of-process policy server#600

Draft
liustanley wants to merge 15 commits into
open-telemetry:mainfrom
truthbk:message-attestation-example
Draft

Message Attestation: example with out-of-process policy server#600
liustanley wants to merge 15 commits into
open-telemetry:mainfrom
truthbk:message-attestation-example

Conversation

@liustanley

Copy link
Copy Markdown

Overview

Draft for early feedback, tracking the Message Attestation proposal in opamp-spec#333.

Extends the internal/examples/ agent and server to demonstrate Message Attestation end-to-end, following the isolated out-of-process signing architecture.

Depends on: #599


What's included

  • internal/examples/policysrv/ — new standalone HTTP signing service (the "policy server")
  • internal/examples/server/--policy-server flag wires a RemoteSigner into Settings.PayloadSigner
  • internal/examples/agent/--attestation-ca and --attestation-tofu-store flags

Architecture

┌─────────────┐   OpAMP (signed)   ┌─────────────┐   sign RPC   ┌──────────────┐
│    Agent    │ ◄────────────────► │ OpAMP Server│ ────────────► │ Policy Server│
│             │                    │  (no key)   │ ◄──────────── │  (holds key) │
└─────────────┘                    └─────────────┘  signature    └──────────────┘

The OpAMP server holds no private key material. Every outbound ServerToAgent is forwarded to the policy server for signing. The policy server can inspect the payload, enforce organisational policies, and delegate to an HSM before returning a signature.


Running the example

The examples live in their own Go module, so run all commands from the
internal/examples directory (each in its own terminal):

cd internal/examples

1. Start the policy server

go run ./policysrv/
# Policy server listening on :4322
# CA certificate → /tmp/opamp-policy-ca.pem

The policy server generates a fresh ephemeral CA on every start and writes it to
/tmp/opamp-policy-ca.pem — the agent loads this as its payload trust anchor.

2. Start the OpAMP server

go run ./server/ \
    --policy-server http://localhost:4322
# Server signs every outbound ServerToAgent via the policy server

3. Start the agent

Option A — pre-provisioned CA (recommended for production):

go run ./agent/ \
    --attestation-ca /tmp/opamp-policy-ca.pem

Option B — TOFU enrollment (first connection accepts and pins the CA):

go run ./agent/ \
    --attestation-tofu-store /tmp/my-trust-anchor.pem
# On first run: enrolls and saves the CA to the file.
# On subsequent runs: uses the saved CA as a pre-configured trust anchor.

--attestation-ca and --attestation-tofu-store are mutually exclusive.

What to observe

  • The agent logs Message Attestation enabled on startup.
  • The server logs each signing request forwarded to the policy server.
  • Stop the policy server mid-session and restart the agent — attestation fails, the agent applies exponential backoff, and reconnects once the policy server is back.
  • Replace the policy server with a new CA, delete the TOFU store file, and restart the agent — it re-enrolls against the new CA.
  • Rotate the signing leaf live while the agent stays connected — run the policy server with --rotate-interval 30s, or curl -X POST http://localhost:4322/v1/rotate. The root CA (trust anchor) is unchanged, so the agent re-validates the new chain and keeps verifying without reconnecting. (The example server uses a short chain-cache TTL so rotations are picked up promptly.)

Key files

File Purpose
internal/examples/policysrv/main.go HTTP policy server: /v1/sign, /v1/chain, /v1/ca, /v1/rotate (+ --rotate-interval)
internal/examples/server/main.go Adds --policy-server flag
internal/examples/server/opampsrv/opampsrv.go Wires signing.NewRemoteSigner into Settings.PayloadSigner
internal/examples/agent/main.go Adds --attestation-ca / --attestation-tofu-store flags
internal/examples/agent/agent/agent.go Passes PayloadVerifier / PayloadTOFUStore into StartSettings

liustanley added 15 commits July 2, 2026 17:30
Implements the Message Attestation feature from opamp-spec PR open-telemetry#333.
Every ServerToAgent is wrapped in a SignedServerToAgent envelope
carrying a detached X.509 signature. Agents that opt in can verify
that responses originate from an authorised distribution server and
have not been tampered with in transit.

The feature is fully opt-in and backward-compatible: agents and servers
that do not configure attestation continue to use the existing wire
format unchanged.

New signing/ package — pluggable interfaces (Signer, Verifier,
TrustAnchorProvider, TOFUStore) with in-process and remote-delegation
implementations. RemoteSigner supports out-of-process policy servers
as recommended in supplementary-guidelines.md.

Server: Settings.PayloadSigner enables signing. The server
auto-advertises OffersPayloadTrustVerification and wraps outbound
messages after capability negotiation with the agent.

Client: StartSettings.PayloadVerifier / PayloadTOFUStore enable
verification. Chain validation (RFC 5280), SAN check, and per-message
signature verification are enforced by attestationState. TOFU
enrollment bootstraps the verifier from the first TrustChainResponse
and persists it via TOFUStore.
Demonstrates the recommended production deployment pattern from
supplementary-guidelines.md: the OpAMP distribution server holds no
private key material. Signing is delegated to a separate policy server
that can inspect the payload, enforce organisational policies, and
delegate to an HSM before returning a signature.

policysrv: standalone HTTP signing service exposing /v1/sign,
/v1/chain, /v1/ca. Run it alongside the example server to see the
full attestation flow end-to-end.

Example server: --policy-server flag wires a RemoteSigner so the
OpAMP server signs every outbound ServerToAgent through the policy
server.

Example agent: --attestation-ca configures a pre-provisioned trust
anchor; --attestation-tofu-store enables TOFU enrollment for
environments where out-of-band CA provisioning is impractical.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant