Skip to content

Policy Management

A policy is the answer to “which measurements do we trust?”. Mero TEE answers it twice per release, in both directions: the KMS image bakes in which node measurements get keys, and merod pins which KMS measurements it will ask. Both come from measuring the real images at release time, and both are published, signed, per profile. This page covers how those policies are produced, pinned to a profile, matched against a quote, and tracked in the compatibility catalog.

Each release publishes two related, per-profile artifacts:

Artifact Release family Consumed by
kms-attestation-policy.<profile>.json mero-kms-v<version> merod, to pin the KMS (kms_allowed_*); operators, to audit the node allowlist the KMS image bakes in (node_allowed_*)
published-mrtds.json mero-tee-v<version> the KMS release (baked into the KMS image), namespace admitters, operators and the verifier, as node measurement references

The KMS itself never reads either at runtime. Its node allowlist is part of its image (ALLOWED_* in /etc/mero-kms/kms.env), copied from published-mrtds.json when the image is built, so it is covered by the KMS’s own measurements.

{
"tag": "2.3.75",
"role": "kms",
"profile": "locked-read-only",
"kms": { "provider": "mero-kms" },
"merod_config_path": "tee.kms.attestation",
"policy": {
"kms_allowed_tcb_statuses": ["uptodate", "outofdate"],
"kms_allowed_mrtd": ["<96-hex MRTD>"],
"kms_allowed_rtmr0": ["<96-hex>"],
"kms_allowed_rtmr1": ["<96-hex>"],
"kms_allowed_rtmr2": ["<96-hex>"],
"kms_allowed_rtmr3": ["<96-hex>"],
"node_allowed_tcb_statuses": ["uptodate", "outofdate"],
"node_allowed_mrtd": ["<96-hex MRTD>"],
"node_allowed_rtmr0": ["<96-hex>"],
"node_allowed_rtmr1": ["<96-hex>"],
"node_allowed_rtmr2": ["<96-hex>"],
"node_allowed_rtmr3": ["<96-hex>"]
}
}
  • kms_allowed_* are the KMS image’s MRTD and RTMR0–3, measured by booting it as a cluster on real TDX VMs and verifying the quotes with Intel Trust Authority. merod requires them of the KMS’s /attest quote, and refuses a debug TD.
  • node_allowed_* are the node image’s measurements, the same values the KMS image bakes in.
  • Each measurement is a validated TDX register value — 48 bytes / 96 hex characters, normalized to lowercase without a 0x prefix. The TCB lists are declared release inputs, not readings (see the release pipeline).
  • merod verifies the document’s keyless Sigstore signature (.sig, .bundle.json; a Fulcio certificate for the Release mero-kms workflow on refs/heads/master of calimero-network/mero-tee) before trusting it. The profile-less kms-attestation-policy.json is the locked-read-only one.

There are exactly three profiles, forming distinct trust cohorts:

  • debug — development only; never production.
  • debug-read-only — staging / pre-production.
  • locked-read-only — hardened production.

A KMS serves exactly one profile: each profile is its own KMS image, with that profile’s node allowlist baked in, and so its own cluster. The profile is pinned by a file baked into the image at /etc/mero-kms/image-profile; if the MERO_KMS_PROFILE environment variable is also set it must match the pinned value, or the KMS refuses to start (the deprecated KMS_POLICY_PROFILE is a legacy alias for the same variable). At boot kms-init extends RTMR3 with the image’s role, profile and root hash, so the profile is reflected in the hardware measurement itself: a policy for one profile cannot silently accept a quote from another, and a replica of one profile can never join another profile’s cluster.

There is exactly one source, fixed at build time:

  1. The Release mero-tee workflow measures each profile’s node image and publishes published-mrtds.json.
  2. The Release mero-kms workflow reads it and bakes each profile’s measurements into that profile’s KMS image (kms_node_policy_file → /etc/mero-kms/kms.env).
  3. At startup the KMS reads ALLOWED_TCB_STATUSES / ALLOWED_MRTD / ALLOWED_RTMR0..3 from that baked environment and validates them. There is no runtime fetch, no signature to check, no degraded mode and no override: changing the allowlist means building a new KMS image, which the running cluster refuses to join.

See Config Reference for the full environment surface.

Matching happens on every /get-key request, after the quote is cryptographically verified (see Key Release). The KMS compares the quote’s reported values against its baked allowlists (published as node_allowed_*):

tcb_status ∈ allowed_tcb_statuses else 403 tcb_status_rejected
mrtd ∈ node_allowed_mrtd \
rtmr0 ∈ node_allowed_rtmr0 |
rtmr1 ∈ node_allowed_rtmr1 | any miss => 403 measurement_policy_rejected
rtmr2 ∈ node_allowed_rtmr2 |
rtmr3 ∈ node_allowed_rtmr3 /

All five registers plus the TCB status must match; any single mismatch is a policy violation. RTMR3 is the profile discriminator, so a production policy that only lists production RTMR3 values will never admit a debug node.

merod does the mirror image on the KMS’s /attest quote before every key fetch: not a debug TD, TCB status in kms_allowed_tcb_statuses, and MRTD and RTMR0–3 each in its kms_allowed_* list.

Because a KMS version change can change measurements, KMS and node-image versions move together. The repository’s compatibility-catalog.json records the released pairings:

{
"schema_version": 1,
"releases": [
{
"version": "2.3.50",
"kms_tag": "mero-kms-v2.3.50",
"node_image_tag": "mero-tee-v2.3.50",
"kms_policy_url": ".../mero-kms-v2.3.50/kms-attestation-policy.json",
"node_policy_url": ".../mero-tee-v2.3.50/published-mrtds.json"
}
]
}

Each entry pairs a KMS release with the node-image release of the same version and links directly to that pair’s policy and MRTD assets. The catalog is regenerated automatically whenever a release is published (the update-compatibility-catalog workflow enumerates mero-kms-v* releases and commits the rebuilt file to master), so it is a derived index rather than a hand-edited source. Look up a pairing by version:

Terminal window
jq '.releases[] | select(.version == "2.3.50")' compatibility-catalog.json

A separate CI guard keeps the KMS and merod/node versions from drifting apart when either is bumped.

Policy values are not hand-typed. The node release boots each node image on a TDX VM and records its measurements; the KMS release boots each KMS image as a two-replica cluster and records its. Both are verified by Intel Trust Authority, signed, and published with the release, so every accepted measurement is traceable to a signed release and the run that produced it. Before a change reaches a release, the manual KMS TDX image probe proves the image builds and forms a cluster. The operational details of that pipeline live in the release pipeline and runbooks.