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.
Two policy artifacts
Section titled “Two policy artifacts”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.
The KMS policy document
Section titled “The KMS policy document”{ "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/attestquote, 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
0xprefix. 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 theRelease mero-kmsworkflow onrefs/heads/masterofcalimero-network/mero-tee) before trusting it. The profile-lesskms-attestation-policy.jsonis thelocked-read-onlyone.
Profiles and cohort separation
Section titled “Profiles and cohort separation”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.
Where the node allowlist comes from
Section titled “Where the node allowlist comes from”There is exactly one source, fixed at build time:
- The Release mero-tee workflow measures each profile’s node image and publishes
published-mrtds.json. - 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). - At startup the KMS reads
ALLOWED_TCB_STATUSES/ALLOWED_MRTD/ALLOWED_RTMR0..3from 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.
How a policy is matched
Section titled “How a policy is matched”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_rejectedmrtd ∈ node_allowed_mrtd \rtmr0 ∈ node_allowed_rtmr0 |rtmr1 ∈ node_allowed_rtmr1 | any miss => 403 measurement_policy_rejectedrtmr2 ∈ 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.
Versioning and the compatibility catalog
Section titled “Versioning and the compatibility catalog”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:
jq '.releases[] | select(.version == "2.3.50")' compatibility-catalog.jsonA separate CI guard keeps the KMS and merod/node versions from drifting apart when
either is bumped.
Promotion
Section titled “Promotion”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.