From e4e9ecd835c0ac19495b094e58e1121dc2615295 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Mon, 20 Jul 2026 19:27:01 +0000 Subject: [PATCH 01/26] docs: integrate MIAF and WFM identity profile SUPs Replace the Management Interface security model with the MIAF and WFM Identity Profile SUPs: add the Identity and Authorization section, switch to mTLS with X.509-SVIDs, drop {clientId} from routes, and remove the onboarding, certificate, and RFC 9421 payload-signing paths. Signed-off-by: Matthias Lechner --- mkdocs.yml | 11 +- .../resources/index.md.jinja2 | 54 ++--- .../identity/identity-framework.md | 139 +++++++++++ .../identity/identity-lifecycle.md | 61 +++++ .../identity-security-considerations.md | 37 +++ system-design/specification/identity/svids.md | 41 ++++ .../identity/tls-requirements.md | 60 +++++ .../identity/trust-bundle-and-discovery.md | 137 +++++++++++ .../identity/trust-bundle-api-1.0.0.yaml | 145 ++++++++++++ .../identity/trust-bundle-api-swagger.md | 10 + .../identity/wfm-identity-profile.md | 124 ++++++++++ .../margo-devices/device-requirements.md | 2 +- .../api-requirements-and-security.md | 139 +++-------- .../certificate-api.md | 26 -- .../deployment-status.md | 10 +- .../device-capabilities.md | 28 +-- .../device-client-onboarding.md | 100 -------- .../workload-management-api-1.0.0.yaml | 222 ++++-------------- 18 files changed, 884 insertions(+), 462 deletions(-) create mode 100644 system-design/specification/identity/identity-framework.md create mode 100644 system-design/specification/identity/identity-lifecycle.md create mode 100644 system-design/specification/identity/identity-security-considerations.md create mode 100644 system-design/specification/identity/svids.md create mode 100644 system-design/specification/identity/tls-requirements.md create mode 100644 system-design/specification/identity/trust-bundle-and-discovery.md create mode 100644 system-design/specification/identity/trust-bundle-api-1.0.0.yaml create mode 100644 system-design/specification/identity/trust-bundle-api-swagger.md create mode 100644 system-design/specification/identity/wfm-identity-profile.md delete mode 100644 system-design/specification/margo-management-interface/certificate-api.md delete mode 100644 system-design/specification/margo-management-interface/device-client-onboarding.md diff --git a/mkdocs.yml b/mkdocs.yml index 8a2af410..370f568b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,10 +4,17 @@ docs_dir: system-design nav: - What is Margo?: index.md - Specification: + - Identity and Authorization: + - specification/identity/identity-framework.md + - specification/identity/svids.md + - specification/identity/tls-requirements.md + - specification/identity/trust-bundle-and-discovery.md + - specification/identity/identity-lifecycle.md + - specification/identity/wfm-identity-profile.md + - specification/identity/identity-security-considerations.md + - specification/identity/trust-bundle-api-swagger.md - Margo Management Interface: - specification/margo-management-interface/api-requirements-and-security.md - - specification/margo-management-interface/certificate-api.md - - specification/margo-management-interface/device-client-onboarding.md - specification/margo-management-interface/device-capabilities.md - specification/margo-management-interface/desired-state.md - specification/margo-management-interface/deployment-status.md diff --git a/src/specification/margo-management-interface/resources/index.md.jinja2 b/src/specification/margo-management-interface/resources/index.md.jinja2 index cc5260d4..3fc60ed2 100644 --- a/src/specification/margo-management-interface/resources/index.md.jinja2 +++ b/src/specification/margo-management-interface/resources/index.md.jinja2 @@ -21,7 +21,7 @@ This design allows the Workload Fleet Management Client to choose the optimal re | Individual YAMLs | Incremental updates, bandwidth-limited or metered links | Only changed workloads are downloaded | The Workload Fleet Management Client compares the manifest with its current state and reconciles any differences by deploying, updating, or removing workloads. -For every change in deployment state - including installation, updates, removals, and failures - the client MUST report the corresponding status to the WFM using the [Deployment Status API](../../specification/margo-management-interface/deployment-status.md). +For every change in deployment state - including installation, updates, removals, and failures - the client MUST report the corresponding status to the WFM using the [Deployment Status API](../margo-management-interface/deployment-status.md). ## Endpoints - State Manifest @@ -30,15 +30,9 @@ This section defines the API endpoint used by a client to retrieve the State Man ### Route and HTTP Methods ```https -GET /api/v1/clients/{clientId}/deployments +GET /api/v1/deployments ``` -### Route Parameters - -| Parameter | Type | Required? | Description | -| --------- | ---- | --------- | ----------- | -| `{clientId}` | string | Y | The unique identifier of the (device) client registered with the WFM during onboarding. | - ### Request Headers | Header | Description | @@ -52,6 +46,7 @@ GET /api/v1/clients/{clientId}/deployments | ----- | ---------- | | 200 OK | The response body contains the manifest. The server MUST include a valid `ETag` and `Content-Type: application/vnd.margo.manifest.v1+json`. | | 304 Not Modified | The response body is empty. Returned if the `If-None-Match` `ETag` matches, i.e. the cached response body has not changed since the last retrieved version. | +| 403 Forbidden | The request is not authorized by the WFM's local policy (for example, the client relationship has been retired; see [Authorization](../identity/wfm-identity-profile.md#authorization)). | | 406 Not Acceptable | The server cannot return a representation matching the `Accept` header. | ### Example State Manifest Response @@ -62,13 +57,13 @@ GET /api/v1/clients/{clientId}/deployments "bundle": { "mediaType": "application/vnd.margo.bundle.v1+tar+gzip", "digest": "sha256:b5c6d7e8f9...", - "url": "/api/v1/clients/1234/bundles/sha256:b5c6d7e8f9..." + "url": "/api/v1/bundles/sha256:b5c6d7e8f9..." }, "deployments": [ { "deploymentId": "a3e2f5dc-912e-494f-8395-52cf3769bc06", "digest": "sha256:a4e01b2c3d...", - "url": "/api/v1/clients/1234/deployments/a3e2f5dc-912e-494f-8395-52cf3769bc06/sha256:a4e01b2c3d..." + "url": "/api/v1/deployments/a3e2f5dc-912e-494f-8395-52cf3769bc06/sha256:a4e01b2c3d..." } ] } @@ -83,12 +78,12 @@ GET /api/v1/clients/{clientId}/deployments | `bundle.mediaType` | string | Y | MUST be `application/vnd.margo.bundle.v1+tar+gzip`, which denotes a gzip-compressed tar archive (commonly delivered as a .tar.gz) whose root contains one or more `ApplicationDeployment` YAML files. Servers MUST set the HTTP `Content-Type` to this media type. The archive MUST contain exactly the set of YAML files referenced by `deployments`. | | `bundle.digest` | string | Y | Digest of the bundle archive. MUST equal the digest computed over the exact sequence of bytes in the [bundle endpoint's](#endpoints-deployment-bundle) HTTP `200 OK` response body. See [Protocol - Digest](#protocol-digest) for further details. | | `bundle.sizeBytes` | number | N | Optional unsigned 64-bit advisory estimate of the decoded payload length in bytes for the bundle archive. Provided for bandwidth estimation and update planning. MUST NOT be used for integrity verification. | -| `bundle.url` | string | Y | Content-addressable retrieval endpoint for the bundle of the form `/api/v1/clients/{clientId}/bundles/{digest}` where `{digest}` equals `bundle.digest`. | +| `bundle.url` | string | Y | Content-addressable retrieval endpoint for the bundle of the form `/api/v1/bundles/{digest}` where `{digest}` equals `bundle.digest`. | | `deployments` | array | Y | List of deployment objects describing each workload. | | `deployments[].deploymentId` | string | Y | The UUID of the deployment. MUST equal top-level `id` attribute in the `ApplicationDeployment`. | | `deployments[].digest` | string | Y | Digest of the corresponding `ApplicationDeployment` YAML file. MUST equal the digest computed over the exact sequence of bytes in the [individual deployment endpoint's](#endpoints-individual-deployment-yaml) HTTP `200 OK` response body. See [Protocol - Digest](#protocol-digest) for further details. | | `deployments[].sizeBytes` | number | N | Optional unsigned 64-bit advisory estimate of the decoded payload length in bytes for the `ApplicationDeployment` YAML. Provided for bandwidth estimation and update planning. MUST NOT be used for integrity verification. | -| `deployments[].url` | string | Y | Content-addressable retrieval endpoint for the `ApplicationDeployment` YAML of the form `/api/v1/clients/{clientId}/deployments/{deploymentId}/{digest}` where `{digest}` equals `deployments[].digest`. | +| `deployments[].url` | string | Y | Content-addressable retrieval endpoint for the `ApplicationDeployment` YAML of the form `/api/v1/deployments/{deploymentId}/{digest}` where `{digest}` equals `deployments[].digest`. | > **Note:** The `ETag` returned from this endpoint is a digest of the entire JSON response body (after serialization). It is independent of `bundle.digest` and individual deployment digests (`deployments[].digest`). See [ETag and Caching](#protocol-etag-and-caching) for details. @@ -105,14 +100,13 @@ This section defines the API endpoint used by a client to retrieve a single `App ### Route and HTTP Methods ```https -GET /api/v1/clients/{clientId}/deployments/{deploymentId}/{digest} +GET /api/v1/deployments/{deploymentId}/{digest} ``` ### Route Parameters | Parameter | Type | Required? | Description | | --------- | ---- | --------- | ----------- | -| `{clientId}` | string | Y | The unique identifier of the (device) client registered with the WFM during onboarding. | | `{deploymentId}` | string | Y | The UUID of the served `ApplicationDeployment` YAML. This MUST equal to top-level `id` attribute in the ApplicationDeployment. | | `{digest}` | string | Y | Content-addressable digest of the served `ApplicationDeployment` YAML. See [Protocol - Digest](#protocol-digest) for further details. | @@ -120,7 +114,8 @@ GET /api/v1/clients/{clientId}/deployments/{deploymentId}/{digest} | Code | Description | | ---- | ----------- | -| 200 OK | The response body contains the raw `ApplicationDeployment` YAML (`Content-Type: application/yaml`). Server MUST set `ETag` to the quoted digest and SHOULD return `Cache-Control: public, max-age=31536000, immutable`. | +| 200 OK | The response body contains the raw `ApplicationDeployment` YAML (`Content-Type: application/yaml`). Server MUST set `ETag` to the quoted digest and MUST return `Cache-Control: private, max-age=31536000, immutable` (see [Protocol - ETag and Caching](#protocol-etag-and-caching)). | +| 403 Forbidden | The request is not authorized by the WFM's local policy (for example, the client relationship has been retired; see [Authorization](../identity/wfm-identity-profile.md#authorization)). | | 404 Not Found | The referenced digest does not exist on the server. `404 Not Found` indicates only that this specific digest is unavailable. It MUST NOT be interpreted as a deletion signal by a client; deletion of workloads is determined solely by absence from the state manifest. | > **Note:** Servers MAY apply HTTP `Content-Encoding` (e.g., gzip, br). The client advertises support via `Accept-Encoding`. Digests and ETags always refer to the decoded representation (i.e., the exact bytes of the response body after decompressing any HTTP `Content-Encoding` such as gzip). Servers SHOULD include `Vary: Accept-Encoding` if compression is used. @@ -132,21 +127,21 @@ This section defines the API endpoint used by a client to retrieve a compressed ### Route and HTTP Methods ```https -GET /api/v1/clients/{clientId}/bundles/{digest} +GET /api/v1/bundles/{digest} ``` ### Route Parameters | Parameter | Type | Required? | Description | | --------- | ---- | --------- | ----------- | -| `{clientId}` | string | Y | The unique identifier of the (device) client registered with the WFM during onboarding. | | `{digest}` | string | Y | Content-addressable digest of the served bundle archive. See [Protocol - Digest](#protocol-digest) for further details. | ### Response Codes | Code | Description | | ---- | ----------- | -| 200 OK | The bundle was successfully retrieved. The server MUST set `Content-Type` to the manifest-declared `bundle.mediaType`, `ETag` to the quoted digest, and SHOULD return `Cache-Control: public, max-age=31536000, immutable`. | +| 200 OK | The bundle was successfully retrieved. The server MUST set `Content-Type` to the manifest-declared `bundle.mediaType`, `ETag` to the quoted digest, and MUST return `Cache-Control: private, max-age=31536000, immutable` (see [Protocol - ETag and Caching](#protocol-etag-and-caching)). | +| 403 Forbidden | The request is not authorized by the WFM's local policy (for example, the client relationship has been retired; see [Authorization](../identity/wfm-identity-profile.md#authorization)). | | 404 Not Found | The referenced digest does not exist on the server. `404 Not Found` indicates only that this specific digest is unavailable. It MUST NOT be interpreted as a deletion signal by a client; deletion of workloads is determined solely by absence from the state manifest. | > **Note:** Servers MAY apply `Content-Encoding` (e.g., gzip, br) and SHOULD include `Vary: Accept-Encoding` if they do. @@ -163,7 +158,7 @@ A digest has the form `algorithm:encoded`, where both parts are lowercase. Clien The resulting digest value is used consistently across all API representations: - In JSON responses: `"digest": "sha256:a4e01b2c3d..."` -- In URLs: `/api/v1/clients/{clientId}/deployments/{deploymentId}/sha256:a4e01b2c3d...` +- In URLs: `/api/v1/deployments/{deploymentId}/sha256:a4e01b2c3d...` - In HTTP headers: `ETag: "sha256:a4e01b2c3d..."` Clients MUST verify that the digest they compute for every retrieved artifact matches the value provided in the manifest. Any mismatch MUST cause the client to abort the update and preserve the previous state. @@ -179,16 +174,17 @@ sha256:a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789abcdef > This API defines the digest format `sha256:` (lowercase) for JSON fields, URLs, and ETags. > This digest represents content identity, ensuring that each artifact (manifest, bundle, or `ApplicationDeployment` YAML) can be uniquely verified and referenced within the Desired State API. > -> By contrast, the mechanism described in the [API Requirements and Security Details](../../specification/margo-management-interface/api-requirements-and-security.md) document - which follows [RFC 9421](https://datatracker.ietf.org/doc/html/rfc9421) - provides HTTP message-level integrity through signed payloads. -> While both rely on the SHA-256 algorithm, they operate at different layers of the protocol: +> By contrast, transport-level authenticity and integrity are provided by mutual TLS as described in the [API Requirements and Security Details](../margo-management-interface/api-requirements-and-security.md) document. The two operate at different layers of the protocol: > -> - The digest in this API defines immutable content identity for artifacts. -> - The RFC 9421 mechanism ensures end-to-end message integrity and authenticity during HTTP transport. +> - The digest in this API defines immutable content identity for artifacts, verifiable independently of how they were transported. +> - Mutual TLS authenticates both parties and protects the messages that carry those artifacts in transit. ## Protocol - ETag and Caching All Desired State endpoints implement standard HTTP caching semantics to optimize synchronization between the Workload Fleet Manager (WFM) and the Workload Fleet Management Client. ETags are used to detect content changes and avoid redundant data transfers. Two caching models are defined: one for the mutable State Manifest, and one for immutable, content-addressable resources such as individual deployments and bundles. +Every response is scoped to the authenticated client, so all Desired State responses are marked `Cache-Control: private`, which keeps a response in that client's own cache rather than a shared one. Because mTLS carries no `Authorization` header (whose presence would normally keep a response out of shared caches per [RFC 9111 §3.5](https://datatracker.ietf.org/doc/html/rfc9111#section-3.5)), servers set this explicitly rather than rely on default cache behavior. Under end-to-end mTLS no shared cache sees these responses at all; the marking matters only where an operator terminates mTLS at a TLS-offloading proxy and cleartext reaches an internal hop (see the MIAF [traffic-inspecting proxies](../identity/tls-requirements.md#traffic-inspecting-proxies) rules), where `private` keeps a cache on that hop from serving one client's response to another. + ### State Manifest Endpoint For the [State Manifest](#endpoints-state-manifest) endpoint, servers use strong ETags as defined in [RFC 9110 § 8.8.3](https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3). @@ -198,7 +194,7 @@ For the [State Manifest](#endpoints-state-manifest) endpoint, servers use strong `":"`, for example: `"sha256:a4e01b2c3d..."`. - Servers SHOULD serialize JSON deterministically (for example, per [RFC 8785](https://datatracker.ietf.org/doc/html/rfc8785)) so that logically identical manifests yield identical bytes and therefore identical ETags. -- Manifest responses MUST NOT be marked immutable (e.g., by applying `Cache-Control: immutable` or excessively long `max-age` values). Freshness is controlled through periodic polling using `If-None-Match` revalidation requests. +- Manifest responses MUST be marked `Cache-Control: private` and MUST NOT be marked immutable (no `Cache-Control: immutable` or excessively long `max-age`). Freshness is controlled through periodic polling using `If-None-Match` revalidation requests. - When a client presents an `ETag` that matches the current manifest, the server MUST respond with `304 Not Modified`, omitting the response body. ### Content-Addressable Endpoints @@ -207,7 +203,7 @@ For [Individual Deployment YAML](#endpoints-individual-deployment-yaml) and [Dep - The `ETag` MUST equal the quoted digest embedded in the resource’s URL (e.g., `ETag: "sha256:a4e01b2c3d..."`). This constitutes a strong validator per [RFC 9110 § 8.8.3](https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3). -- Servers SHOULD include `Cache-Control: public, max-age=31536000, immutable` to enable long-term caching of immutable artifacts. +- Servers MUST include `Cache-Control: private, max-age=31536000, immutable`, enabling long-term caching of immutable artifacts in the client's own cache. - If compression is applied, servers SHOULD include `Vary: Accept-Encoding` to ensure cache correctness across encodings. - Clients MAY send `If-None-Match` when revalidating cached resources; servers MAY return `304 Not Modified` if the artifact has not changed. @@ -218,7 +214,7 @@ This section defines the end-to-end workflow followed by a client to retrieve, v - The client polls the WFM for the latest manifest using the last known `ETag` (if any): ```https - GET /api/v1/clients/{clientId}/deployments + GET /api/v1/deployments ``` - If the manifest is unchanged, the WFM responds with `304 Not Modified`. @@ -229,7 +225,7 @@ This section defines the end-to-end workflow followed by a client to retrieve, v - The client reconciles its local workloads: - Adds or updates workloads that appear in the new manifest. - Removes workloads no longer present in the new manifest. - - For each change in workload state, the client reports progress and results to the WFM using the [Deployment Status API](../../specification/margo-management-interface/deployment-status.md). + - For each change in workload state, the client reports progress and results to the WFM using the [Deployment Status API](../margo-management-interface/deployment-status.md). - Once reconciliation succeeds, the client MUST durably persist the new `manifestVersion` and associated `ETag` for use in the next poll cycle. ### Sequence Diagram @@ -241,7 +237,7 @@ sequenceDiagram participant WFM as Workload Fleet
Manager loop Poll for updates - Client->>+WFM: GET /api/v1/clients/{clientId}/deployments
Header: If-None-Match: "sha256:abc..." + Client->>+WFM: GET /api/v1/deployments
Header: If-None-Match: "sha256:abc..." alt State unchanged WFM-->>-Client: 304 Not Modified else State updated @@ -257,7 +253,7 @@ sequenceDiagram end Client->>Client: Verify digests and reconcile workloads - Client->>WFM: POST /api/v1/clients/{clientId}/deployment/{deploymentId}/status
Report progress + Client->>WFM: POST /api/v1/deployments/{deploymentId}/status
Report progress end end ``` diff --git a/system-design/specification/identity/identity-framework.md b/system-design/specification/identity/identity-framework.md new file mode 100644 index 00000000..5b9c754c --- /dev/null +++ b/system-design/specification/identity/identity-framework.md @@ -0,0 +1,139 @@ +# Margo Identity and Authorization Framework + +The Margo Identity and Authorization Framework (MIAF) is Margo's common foundation for identity, authentication, and authorization. It is built on cryptographically verifiable credentials aligned with open cloud-native identity standards, notably [SPIFFE](https://spiffe.io/). + +MIAF defines: + +- a **Trust Domain** model and the **SPIFFE ID** namespace for identifying Margo components; +- an **X.509-SVID** profile (an X.509 certificate carrying a SPIFFE ID in its URI SAN) as the credential a component presents; +- the **SPIFFE Bundle Map** as the canonical format for distributing trust anchors, located through an optional [discovery document](trust-bundle-and-discovery.md); +- the **Margo Identity Service (MIS)** as the identity-authority role within a Trust Domain; and +- a cryptographic and [TLS baseline](tls-requirements.md) shared by all Margo components, with authentication by mTLS using X.509-SVIDs validated against the Trust Bundle. + +The framework is generic: it does not define an enrollment protocol or a specific identity profile. Those are layered on top. The [Margo WFM Identity Profile](wfm-identity-profile.md) is the first such profile, naming WFMs and WFM Clients and applying MIAF authentication to the [Margo Management Interface](../margo-management-interface/api-requirements-and-security.md). + +Authentication is mTLS with an X.509-SVID. Authorization is performed locally by each verifier, based on the peer's verified SPIFFE ID. There is no central authorization server. + +## Framework Overview + +MIAF has four moving parts: + +1. **Trust Domain**: the security boundary within which MIAF identities are issued and validated. Each SPIFFE ID belongs to exactly one Trust Domain; a verifier MAY also accept identities from other Trust Domains through configuration or federation. +2. **Margo Identity Service (MIS)**: the identity-authority role. It issues SVIDs, publishes the discovery document and Trust Bundle, and enforces the cryptographic and SVID-profile rules. The role is not pinned to a specific service API. +3. **Margo components**: DFMs, WFMs, their clients, and infrastructure services. A component acts as an **SVID holder** when it authenticates and as a **verifier** when it validates a peer's SVID. +4. **Trust Bundles**: each Trust Domain publishes a Trust Bundle (a set of X.509 trust anchors) that verifiers validate SVIDs against. Bundles are distributed via the SPIFFE [Bundle Map](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md). + +Once a component holds an SVID: + +1. **Acquire trust material.** The component acquires its Trust Domain's Trust Bundle: it either locates the bundle through the discovery document and retrieves it over HTTPS, or receives the bundle and Trust Domain identifier through operator-provided configuration or out-of-band delivery (see [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap)). +2. **Authenticate to peers.** The component and peer complete an mTLS handshake: the component presents its X.509-SVID, and the peer validates the chain against the Trust Bundle. +3. **Authorize the call.** The peer applies its local policy to the now-verified SPIFFE ID. + +A component obtains its SVID through the [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook). + +> **Conceptual trust and identity architecture (informative)** +> +> The diagram below shows MIAF in its most general form: a Margo component holds an X.509-SVID within a governed Trust Domain, then authenticates to peers over mTLS. The Trust Domain publishes the Trust Bundle that participants use to validate identities. +> +> ```mermaid +> flowchart LR +> Client["`**Margo Client Component** +> (e.g., WFM Client, DFM Client, OTel Collector)`"] +> Server["`**Margo Server Component** +> (e.g., WFM, DFM, Observability Platform, Component Registry)`"] +> MIS["`**Margo Identity Service (MIS)** +> Issues SVIDs, publishes Trust Bundle & discovery`"] +> TD["`**Trust Domain** +> Defines trust anchors, policies, and namespace`"] +> X509["`**X.509 SVID** +> Certificate binding SPIFFE ID to key pair`"] +> TB["`**Trust Bundle** +> X.509 trust anchors`"] +> +> Client -->|"holds X.509 SVID"| X509 +> MIS -->|"issues X.509 SVID"| X509 +> Client -->|"authenticates using X.509 SVID (mTLS)"| Server +> Server -->|"verifies SVID using Trust Bundle of"| TD +> TD -->|"publishes"| TB +> +> classDef comp fill:#e8f1ff,stroke:#5b8def,stroke-width:1px,rx:8px,ry:8px,color:#0b3b8c; +> classDef ident fill:#e8f7ee,stroke:#2ca36b,stroke-width:1px,rx:8px,ry:8px,color:#0f5132; +> classDef trust fill:#f7f7f7,stroke:#bdbdbd,stroke-width:1px,rx:8px,ry:8px,color:#333; +> +> class Client,Server,MIS comp; +> class X509 ident; +> class TD,TB trust; +> ``` + +## Scope and Applicability + +MIAF is a general foundation: any Margo component MAY adopt it, and future identity profiles will extend it to new principal classes. In this release, MIAF governs: + +- the **MIS trust endpoints**: the discovery document and Trust Bundle retrieval described in [Trust Bundle and Discovery Endpoints](trust-bundle-and-discovery.md); and +- the **Workload Fleet Management interface**, through the [WFM Identity Profile](wfm-identity-profile.md), which is the only identity profile defined so far. + +Other Margo components (the Device Fleet Manager, observability collectors, or component registries, for example) MAY hold MIAF identities, but no identity profile is defined for their interfaces yet, so how they authenticate is not governed here until such a profile exists. For an interface into an external ecosystem that carries its own established authentication convention (such as an OCI registry), a MIAF identity is expected to serve as the root credential a component uses to obtain an ecosystem-native credential, rather than as the wire-level authentication mechanism itself. + +## Terminology + +The following terms form the common vocabulary for Margo's non-human identity and authorization model. Some are adopted directly from SPIFFE; others are Margo-specific. + +These identities belong to *non-human* **Margo components**: the logical units of the Margo system such as the Device Fleet Manager (DFM), Workload Fleet Manager (WFM), their clients, and infrastructure services such as registries or observability collectors. Which of their interfaces MIAF governs is defined in [Scope and Applicability](#scope-and-applicability). + +Terms adopted from SPIFFE, used here as SPIFFE defines them: + +- **Trust Domain**: the governed security boundary within which identities are issued and mutually recognized, a trust-root-backed identity namespace and policy boundary. A Trust Domain defines its authoritative trust anchors (the X.509 authority certificates published for the domain), the namespace for SPIFFE IDs, and the policies for identity lifecycle and authorization. +- **SPIFFE ID**: a URI of the form `spiffe:///` that names an identity within a Trust Domain. MIAF adopts [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md) syntax and validation rules by reference and defines Margo path conventions where needed (see [Identity model](#identity-model)). +- **SPIFFE Verifiable Identity Document (SVID)**: the verifiable credential representing an identity within a Trust Domain. An SVID binds a SPIFFE ID to a key pair. Its profile, cryptography, and validation are defined in [SVIDs](svids.md). +- **Trust Bundle**: the cryptographic material (X.509 trust anchors) used to validate SVIDs issued within a Trust Domain, distributed via the SPIFFE Bundle Map (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). + +Terms introduced by MIAF: + +- **Principal**: a non-human Margo component that holds, or is being provisioned with, a SPIFFE identity in a Trust Domain. Edge Compute Devices, WFMs, and WFM Clients are all principals. +- **Margo Identity Service (MIS)**: the identity-authority **role** within a Trust Domain. The MIS issues SVIDs, publishes the discovery document and Trust Bundle, and enforces MIAF's cryptographic and SVID-profile rules. The MIS is defined by its responsibilities, not by a specific API (see [The MIS role](#the-mis-role)). +- **Policy-based authorization**: each verifier makes authorization decisions locally, based on the peer's verified SPIFFE ID. MIAF does not use OAuth-style token scopes or a central authorization server. + +## Relationship to SPIFFE + +MIAF reuses SPIFFE identity primitives rather than inventing Margo-specific credential formats or trust semantics. This framework: + +- adopts by reference the SPIFFE concepts of **Trust Domain**, **SPIFFE ID**, **X.509-SVID**, and **Trust Bundle / Bundle Map**; +- profiles or constrains those standards where Margo needs additional rules; and +- defines Margo-specific behavior for discovery and the MIS role, and constrains the SPIFFE ID path namespace to paths beginning with `/margo/`. + +MIAF references the current published text of each SPIFFE specification instead of a pinned revision: SPIFFE versions its specifications by [stability level](https://github.com/spiffe/spiffe/blob/main/standards/STABILITY.md), not release tag, and every document MIAF adopts is at **Stable**, where breaking changes are reserved for critical security fixes. + +| Topic | Source | Notes | +| :---- | :----- | :---- | +| SPIFFE ID syntax and validation rules | [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md), adopted by reference | Margo defines only path conventions where needed. | +| X.509-SVID baseline semantics | [SPIFFE X.509-SVID](https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md), adopted by reference and constrained | Margo adds the profile constraints in [SVIDs](svids.md#x509-svid-profile). | +| Trust Bundle / Bundle Map | [SPIFFE Trust Domain and Bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), adopted by reference | Margo defines discovery and retrieval conventions around it. | +| Discovery document | Margo | Not part of SPIFFE; defined in [Trust Bundle and Discovery Endpoints](trust-bundle-and-discovery.md#discovery-document-endpoint). | + +## Identity Model + +- **Identity representation.** An identity is named by a **SPIFFE ID** and represented by an **SVID** issued under the Trust Domain's MIS. +- **Path namespace.** A SPIFFE ID issued under a MIAF identity profile MUST have a path beginning with `/margo/`. Each identity profile claims a non-conflicting sub-prefix and defines its structure (the path conventions for WFMs and WFM Clients are in the [WFM Identity Profile](wfm-identity-profile.md)). So that `/margo/` remains a reliable signal of MIAF provenance, a non-MIAF SVID in the same Trust Domain MUST NOT use it. +- **Uniqueness.** Each SPIFFE ID names a single identity within its Trust Domain. +- **Lifecycle.** All identities follow the [lifecycle vocabulary](identity-lifecycle.md#lifecycle-vocabulary). +- **Extensibility.** The MIS, Trust Domain, SVID, and Trust Bundle concepts are generic; further profiles may be added for new principal classes without redefining the framework. + +## The MIS Role + +The **Margo Identity Service (MIS)** is a role, not a specific service. Within a Trust Domain, the MIS is responsible for: + +- issuing X.509-SVIDs to principals; +- serving the [Trust Bundle retrieval endpoint](trust-bundle-and-discovery.md#trust-bundle-retrieval-endpoint) and, when used, the [discovery document endpoint](trust-bundle-and-discovery.md#discovery-document-endpoint) over HTTPS; and +- enforcing MIAF's cryptographic and SVID-profile requirements. + +Anything that meets these responsibilities can fill the role: [SPIRE](https://spiffe.io/docs/latest/spire-about/), a CA configured for a MIAF profile, an operator's provisioning workflow, or something else. The only wire contract MIAF fixes for the MIS is the two HTTPS trust endpoints above; it does not standardize how the MIS issues SVIDs, and beyond those endpoints conformance is judged by behavior rather than by API surface. + +### Deployment Patterns (informative) + +Three common ways to fulfil the MIS role. The framework requirements above apply equally to all of them. + +| Pattern | Description | Typical use case | +| :--- | :---------- | :--------------- | +| **Self-signed root CA** | A CA operating as a self-signed root, issuing SVIDs directly. | Self-contained or air-gapped environments. | +| **Intermediate CA under enterprise PKI** | A CA operating as an intermediate, chaining SVIDs to an enterprise or offline root. | Enterprise environments aligned with corporate PKI. | +| **SPIFFE-conformant identity service** | A SPIFFE-conformant service such as SPIRE, configured with the Margo path conventions and Trust Bundle distribution. | Cloud-native or service-mesh environments. | diff --git a/system-design/specification/identity/identity-lifecycle.md b/system-design/specification/identity/identity-lifecycle.md new file mode 100644 index 00000000..f73bebd6 --- /dev/null +++ b/system-design/specification/identity/identity-lifecycle.md @@ -0,0 +1,61 @@ +# Identity Lifecycle and Operator Playbooks + +## Lifecycle Vocabulary + +A MIAF identity moves through five lifecycle phases: + +- **Enrollment**: initial issuance of an SVID for a principal. +- **Active**: the principal holds a valid SVID and authenticates over mTLS, presenting its own SVID and validating each peer's SVID against the Trust Bundle, and is recognized by relying parties. +- **Renewal**: refresh of an SVID before expiry. +- **Revocation**: declaration that an issued SVID is no longer valid before its natural expiry. +- **Re-issuance**: issuance of an SVID to a replacement principal (for example, after device replacement), typically reusing the original SPIFFE ID, though an operator MAY assign a fresh one. + +The **Active** phase has a fully normative protocol surface. The other phases are operator-driven and follow the playbooks below. + +> **Future work (informative)** +> +> Automated mechanisms for the non-active phases (enrollment, renewal, revocation, and re-issuance protocols) are not yet specified. Operator provisioning as described here is the current path and remains valid once automation is added. + +## Operator Provisioning Playbook + +The Trust Bundle and discovery document are the only runtime endpoints MIAF defines; everything else flows through the operator's existing provisioning channel. + +For **enrollment**, the operator: + +1. accepts a CSR from the principal (the preferred path, since it keeps the private key on the principal and supports hardware-bound keys such as a TPM, secure element, or HSM). Where the principal cannot generate its own key pair, the operator generates one centrally and accepts the resulting concentration of key custody; +2. mints an X.509-SVID for the chosen SPIFFE ID under the Trust Domain's issuing authority; +3. installs the SVID on the principal over a channel that protects its integrity and authenticity, and that additionally protects confidentiality on the path where it also carries the centrally generated private key; and +4. ensures every relying party the principal will authenticate to has the Trust Bundle and any local-policy entries needed to recognize the new SPIFFE ID. + +For **renewal**, the operator repeats steps 1-3 before the current SVID expires, replacing the prior SVID in place. + +For **re-issuance** after a principal is replaced, the operator follows the same workflow on the replacement principal. Whether to reuse the original SPIFFE ID or assign a fresh one is an operator-policy choice. + +The provisioning channel itself is deployment-specific and out of scope. Typical options include device-management tooling, configuration management, HSM workflows, and out-of-band installer media. + +## Operator Revocation Playbook + +Without an automated revocation protocol, a deployment revokes an SVID through one of: + +1. **Relying-party allowlist removal**: where a relying party keeps an allowlist of the identities it accepts, the operator removes the SPIFFE ID from it. This is the most precise option - it revokes one principal's access without affecting any other - and is recommended for routine revocation where such a list exists. +2. **Trust Bundle rotation**: the operator removes the compromised trust anchor from the Trust Bundle, invalidating every SVID that chains to it. This is heavy-handed but effective when an entire issuance authority is compromised. See the [Trust Anchor Rotation Playbook](#trust-anchor-rotation-playbook) below, which also covers the case where the issuer is an intermediate CA whose anchor is the root above it. +3. **Expiry**: wait for the SVID to expire. This is viable only with shorter SVID lifetimes. + +None of these options is instantaneous. Allowlist removal takes effect promptly only where the relying party re-evaluates its authorization policy per request; a Trust Bundle rotation propagates no faster than the fleet's refresh interval. In either case a long-lived mTLS connection can keep a revoked peer authenticated until the connection is re-established (see [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation)). + +## Trust Anchor Rotation Playbook + +Rotating a Trust Domain's trust anchor is routine CA lifecycle. This playbook is informative operator guidance; the normative rule it relies on is the [X.509-SVID validation](svids.md#x509-svid-validation) rule that a verifier accepts an SVID chaining to any anchor in the current Trust Bundle. That is what lets a bundle carry the old and new anchors together during an overlap. Because a verifier only learns about anchor changes when it refreshes the Trust Bundle (see [Trust Bundle retrieval endpoint](trust-bundle-and-discovery.md#trust-bundle-retrieval-endpoint)), sequence a rotation so that no principal is asked to validate, or authenticate with, material its peers have not yet learned to trust: + +1. **Publish both anchors.** Add the new trust anchor to the Trust Bundle alongside the old one and publish the updated bundle. +2. **Wait for propagation.** Allow every verifier to refresh the bundle before proceeding: at least the longest refresh interval in use across the fleet, extended to cover realistic offline windows for intermittently connected principals. Setting the bundle's `spiffe_refresh_hint` (see [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle)) puts every principal on the same refresh interval, so this wait has one known value to measure against. +3. **Switch issuance.** Issue new SVIDs (and renewals) so they chain to the new anchor. SVIDs that chain to the old anchor remain valid and keep validating, because both anchors are in the bundle. +4. **Retire the old anchor.** Remove the old anchor from the bundle only once no SVID still chains to it: after all principals have renewed, or after the last old SVID has expired. Removing it earlier revokes every remaining SVID that chains to it (which is exactly the intent when rotation is used for compromise response, and an outage otherwise). + +Where the MIS operates as an intermediate CA beneath an enterprise or offline root (see the [MIS deployment patterns](identity-framework.md#deployment-patterns-informative)), the trust anchor in the bundle is that root, not the issuing intermediate. Rotation and bundle-level revocation then act at the root: replacing an intermediate issuer under an unchanged root needs no bundle change and does not follow this playbook, while removing the root anchor invalidates every SVID chaining through it, not only those from one intermediate. + +## SVID Lifetime Guidance + +MIAF favours short SVID lifetimes: short lifetimes keep the blast radius low and let expiry double as revocation. The right value depends on the principal. A workload identity in a connected service can renew hourly; a device with intermittent or air-gapped connectivity needs enough margin to renew before a realistic offline window ends. As a rough orientation, automated SPIRE-style workload SVIDs are commonly 1-24 hours, while long-lived device SVIDs are often weeks to a few months. + +Manual provisioning makes short lifetimes operationally expensive, so an operator relying on it MAY use longer lifetimes than automated renewal would allow. Treat any such extension as a bridge: once automated renewal is available, an operator SHOULD reduce lifetimes to the shortest value compatible with their principals' connectivity. diff --git a/system-design/specification/identity/identity-security-considerations.md b/system-design/specification/identity/identity-security-considerations.md new file mode 100644 index 00000000..d134e899 --- /dev/null +++ b/system-design/specification/identity/identity-security-considerations.md @@ -0,0 +1,37 @@ +# Identity Security Considerations + +MIAF assumes an adversarial network and the possibility of a compromised individual device or service. Its goals are to preserve identity integrity and minimize blast radius; protecting the private keys those identities rest on is assumed of the deployment rather than provided by the framework. + +This section is scoped to identity. For each threat it states the mitigation MIAF relies on, with the concrete rules in the linked sections. Some mitigations are normative framework requirements; others are residual risks carried by the operator's deployment. + +## Framework Threats + +| Threat | Description | Mitigation | +| :----- | :---------- | :---------- | +| **Theft and misuse of a credential** | An attacker who obtains a principal's SVID together with its private key authenticates as that principal anywhere its SPIFFE ID is accepted; an X.509-SVID carries no audience restriction confining it to one relying party. | A principal MUST protect its private key (see the private-key-compromise row below); SVIDs SHOULD be short-lived per [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance) to bound the exposure window; and each verifier MUST validate the SVID against the Trust Bundle and authorize the specific SPIFFE ID locally, so a stolen credential reaches only what that identity is granted (see [X.509-SVID validation](svids.md#x509-svid-validation)). | +| **Private key compromise** | An attacker exfiltrates a principal's private key and can then authenticate as that principal until the credential is withdrawn. | A principal protects its private key per deployment policy; MIAF specifies no key-protection mechanism of its own. The [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)'s preferred path issues by CSR, which keeps the key on the principal and can bind it to hardware (TPM, secure element, or HSM) where available. Once compromise is known, access is withdrawn via the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook), bounded by short [SVID lifetimes](identity-lifecycle.md#svid-lifetime-guidance). | +| **Central key-custody concentration** | Where a principal cannot generate its own key pair, the operator generates it centrally (see [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)), concentrating many principals' private keys in one store whose breach exposes all of them at once. | The provisioning playbook's preferred path generates the key on the principal (issuance by CSR), leaving no central copy; where a principal cannot generate its own key pair, the operator generates it centrally and accepts the resulting key-custody concentration risk, protecting the store per deployment policy and treating its breach as private-key compromise (row above). | +| **Compromised issuance authority (MIS)** | A compromised [MIS](identity-framework.md#the-mis-role) can mint a valid SVID for any SPIFFE ID in the Trust Domain, impersonating any WFM or client, or issue under a WFM namespace at will. | MIAF concentrates issuance in the MIS by design, so protecting it is an operator PKI-governance responsibility: scope the issuing authority (for example an intermediate CA per the [deployment patterns](identity-framework.md#deployment-patterns-informative)) and protect its signing key per deployment policy. Recovery is [Trust Bundle rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) to retire the compromised authority. | +| **Initial trust anchor confusion** | An attacker causes a principal to trust the wrong HTTPS authority and serves a malicious discovery document or Bundle Map. | A principal MUST authenticate the first HTTPS retrieval of the discovery document and `trustBundleUri` per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). | +| **Trust Bundle substitution or anchor injection** | An attacker who compromises a Trust Bundle origin (or its server certificate) injects a trust anchor. Because the SPIFFE Bundle Map is authenticated only by the transport and is not itself signed, every SVID chaining to the injected anchor is then accepted. This applies on every refresh, not only at bootstrap. | A client retrieves the Bundle Map only over HTTPS validated against its configured initial-trust anchors or pins, on each refresh, per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap) and [certificate validation](tls-requirements.md#certificate-validation), and MUST fail closed on an empty or unusable bundle per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | +| **Trust Bundle rollback** | A stale, cached, or replayed Bundle Map carrying an older `spiffe_sequence` is served to reverse a completed rotation, re-admitting a trust anchor that was removed to revoke a compromise. | A client SHOULD track the highest `spiffe_sequence` it has accepted for the local Trust Domain and reject a Bundle Map whose sequence has regressed, per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | +| **Certificate revocation lag** | A revocation event is not propagated promptly. | A deployment relies on the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook) and short SVID lifetimes; a verifier SHOULD bound connection lifetime so that a long-lived mTLS session does not extend the lag indefinitely (see [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation)). | +| **Service impersonation / MITM** | An adversary attempts to impersonate the MIS or another service. | All endpoints MUST use HTTPS with strict certificate validation per the [TLS requirements](tls-requirements.md); a peer MUST verify a presented SVID against the configured Trust Domain and Trust Bundle. | +| **Cross-domain trust confusion** | A component accepts identities from an unintended Trust Domain. | A verifier MUST determine the Trust Domain from the SPIFFE ID and MUST NOT trust an SVID unless the domain is explicitly configured or federated. | +| **Inadvertent inspection-proxy MITM** | An operator-deployed traffic-inspecting proxy intercepts and re-signs Margo mTLS traffic, presenting a substitute certificate to either endpoint. | An operator MUST exempt Margo mTLS endpoints from inspection per [traffic-inspecting proxies](tls-requirements.md#traffic-inspecting-proxies). Both endpoints MUST validate peer SVIDs against the Trust Bundle and reject a substituted certificate. | +| **Forwarded-identity header injection** | In a TLS-offload topology, an attacker injects a forged `Client-Cert` header to impersonate an authenticated caller at the backend. | The proxy MUST remove or overwrite any `Client-Cert` or `Client-Cert-Chain` header on incoming requests, and the backend MUST accept a forwarded identity only over the trusted proxy boundary, per [traffic-inspecting proxies](tls-requirements.md#traffic-inspecting-proxies). | +| **Unreliable or manipulated validator clock** | A principal mis-evaluates certificate validity periods because its clock is skewed or unset (a dead RTC battery, no time source at first boot) or because an attacker rewinds it, for example by spoofing an unauthenticated time source on the adversarial network. Rewinding the clock extends the validity window of an expired SVID, defeating the use of expiry as revocation. | SVID validation depends on a trustworthy local time source. A principal SHOULD synchronize time from a trustworthy source before validating certificates, and operators SHOULD account for devices with weak clocks when choosing SVID lifetimes (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). A deployment that cannot assure clock integrity SHOULD NOT rely on expiry alone for revocation. | +| **Handshake identity disclosure on TLS 1.2 fallback** | Where an operator enables the optional TLS 1.2 fallback, the mTLS handshake sends the client X.509-SVID in cleartext, so a passive on-path observer can read the peer's SPIFFE ID, and with it its Trust Domain and client relationship. | Default to TLS 1.3, which encrypts the certificate messages, per the [TLS requirements](tls-requirements.md#minimum-tls-baseline). Enable TLS 1.2 only where a deployment requires it, accepting this passive-disclosure risk; when used, TLS 1.2 MUST follow modern security guidance ([RFC 9325](https://datatracker.ietf.org/doc/html/rfc9325)). | + +## WFM Identity Profile Threats + +WFM and WFM Client identities inherit the framework threats above. The threats below are specific to this profile. + +| Threat | Description | Mitigation | +| :--- | :--- | :--- | +| **Wrong-WFM impersonation** | A WFM Client connects to a WFM whose SVID is validly issued by the Trust Domain but whose `wfm-id` is not the one that issued the client, so the client authenticates to the wrong WFM. | The WFM Client MUST verify the `wfm-id` in the WFM's SPIFFE path against the `wfm-id` in its own SVID before authenticating (see [Recognition by the WFM Client](wfm-identity-profile.md#recognition-by-the-wfm-client)). | +| **Wrong-WFM-Client acceptance** | A WFM accepts an SVID whose `wfm-id` belongs to a different WFM's namespace, treating the bearer as a client of itself. | The WFM MUST verify the `wfm-id` in the SPIFFE path against its own namespace before authorizing (see [Recognition by the WFM](wfm-identity-profile.md#recognition-by-the-wfm)). | +| **Unauthorized client within the namespace** | A validly issued SVID under a WFM's own namespace (`.../wfm//client/...`) is presented by a party the WFM's operator never authorized; for instance, the MIS issues under the namespace to the wrong principal. | Namespace match alone does not grant access: a WFM MUST admit a caller only if its identity is accepted by the WFM's accepted-client policy (see [Authorization](wfm-identity-profile.md#authorization)). Operators SHOULD also constrain the MIS to issue under a WFM's namespace only to that WFM's authorized clients. | +| **Stale accepted-client policy** | An entry remains in a WFM's accepted-client policy after the relationship is retired, letting a still-valid credential keep accessing the API. | An operator removes `wfm-client-id` entries promptly per the revocation row of the [lifecycle table](wfm-identity-profile.md#lifecycle). The WFM MAY also deny the request by local policy, surfaced as described in [API Requirements and Security](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication), per [Authorization](wfm-identity-profile.md#authorization). | +| **No precise revocation of a compromised WFM** | A WFM has no client-side accepted-server allowlist comparable to the WFM's accepted-client policy, so a compromised WFM SVID cannot be revoked precisely: only fleet-wide Trust Bundle rotation or expiry withdraws it, so both the blast radius and the revocation lag are large. | Keep WFM SVIDs short-lived and reissue with the same SPIFFE ID before expiry; use [Trust Bundle rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) as the cryptographically enforced revocation path, accepting its fleet-wide scope (see the WFM [lifecycle](wfm-identity-profile.md#lifecycle)). | +| **Non-conformant authentication paths** | A WFM exposes an authentication path outside the mTLS model (an unauthenticated onboarding endpoint or an application-layer signature scheme) that an attacker uses to bypass SVID validation. | A WFM MUST authenticate every Management Interface request with mTLS and an X.509-SVID, and MUST reject requests that do not conform per [API Requirements and Security](../margo-management-interface/api-requirements-and-security.md). | diff --git a/system-design/specification/identity/svids.md b/system-design/specification/identity/svids.md new file mode 100644 index 00000000..0899b35d --- /dev/null +++ b/system-design/specification/identity/svids.md @@ -0,0 +1,41 @@ +# SVIDs + +A MIAF identity is represented by an **X.509-SVID**: an X.509 certificate that binds a SPIFFE ID to a key pair, with the SPIFFE ID in the certificate's URI SAN. This topic defines the SVID profile every MIAF principal presents, the cryptographic algorithms those credentials use, and how a verifier validates a presented SVID. It applies to every interface authenticated under a MIAF identity profile. + +## X.509-SVID Profile + +MIAF adopts the [SPIFFE X.509-SVID specification](https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md) by reference. X.509-SVID is the SVID representation used throughout MIAF. + +An identity profile MAY further constrain validity periods, key-protection rules, or path conventions for its own principal classes. + +The SPIFFE X.509-SVID specification defines the certificate profile and RFC 5280 path validation but leaves how the chain is conveyed out of scope, so MIAF specifies chain delivery here. When presenting an X.509-SVID, the presenter MUST include the leaf SVID and every intermediate CA certificate needed to build a path to a trust anchor; a certificate the Trust Bundle already carries as a trust anchor (typically the self-signed root) MAY be omitted. This presented chain travels inline wherever an X.509-SVID is conveyed, including the TLS `Certificate` message during mTLS. Because the [Trust Bundle](trust-bundle-and-discovery.md) holds only trust anchors, the presented chain is the sole carrier of the intermediates. + +## Cryptographic Requirements + +This section constrains the signature algorithms and key parameters used for SVIDs, CSRs, and the keys that back them. The signatures below are those approved in [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf), over the elliptic curves specified in [NIST SP 800-186](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf); RSA and ECDSA key lengths additionally meet the minimum strengths in [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf). The `ES256`-style codes in the table are [JWA](https://datatracker.ietf.org/doc/html/rfc7518) identifiers, used here as familiar shorthand; the certificate itself carries the equivalent PKIX signature-algorithm OID. Transport-layer cryptography is governed separately by the [TLS requirements](tls-requirements.md). + +| Algorithm | Requirements | +| :-------- | :----------- | +| **ECDSA (P-256 or P-384)** | Keys MUST use curve P-256 (`prime256v1`) or P-384 (`secp384r1`); P-256 with SHA-256 (`ES256`) is the interoperable default, and P-384 with SHA-384 (`ES384`) MAY be used where a deployment requires a higher-assurance curve. | +| **EdDSA (Ed25519)** | Keys MUST use the Ed25519 curve; signatures follow [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032) (`EdDSA`). | +| **RSA (≥3072 + SHA-256)** | Modulus MUST be at least 3072 bits; signatures MUST use SHA-256. RSASSA-PSS (`PS256`, [RFC 8017](https://datatracker.ietf.org/doc/html/rfc8017)) is RECOMMENDED. RSASSA-PKCS#1 v1.5 (`RS256`) MAY be used only for X.509 certificate and CSR signatures (today the only RSA signatures in MIAF), for compatibility with issuing CAs that cannot produce PSS signatures (its use there remains sanctioned by [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf)). Any other RSA signature MIAF defines later MUST use PSS. | + +- **ECDSA P-256 with SHA-256 is mandatory to implement.** Every MIAF component MUST implement it, for both presenting and validating SVIDs, so that any two components share at least one algorithm. +- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. A component that enables a non-mandatory algorithm for the SVID it presents is responsible for confirming that its peers accept it, since only ECDSA P-256 is guaranteed everywhere. +- A component that validates SVIDs MUST validate every algorithm it accepts from peers. For RSA, this includes accepting both PSS and PKCS#1 v1.5 signatures on certificates. + +> **Crypto-agility (informative):** MIAF names its algorithms explicitly so the permitted set can evolve. The set above is classical; post-quantum signature suites are expected to arrive as additional permitted algorithms rather than a redesign of the framework. Defaulting the transport to TLS 1.3 (see [TLS requirements](tls-requirements.md)) supports this: it is the version track on which post-quantum key exchange and authentication are being standardized. + +These requirements apply to MIAF-generated artifacts and to the keys used in SVIDs and CSRs. They do not constrain an external bootstrap ecosystem (for example, a manufacturer PKI used as a bootstrap input), which MAY use algorithms permitted by its governing standards, subject to Trust Domain policy. + +## X.509-SVID Validation + +A verifier authenticates a peer by validating the presented X.509-SVID against the peer's Trust Domain and, on success, treating the SPIFFE ID it carries as the peer's identity. A verifier MUST, in order: + +- read the SPIFFE ID from the leaf certificate's **URI SAN** to determine the peer's Trust Domain, and reject the SVID unless that Trust Domain is explicitly configured or federated. DNS hostname matching does not apply to SVID identity and MUST NOT override the SPIFFE ID. +- validate the presented chain against that Trust Domain's [Trust Bundle](trust-bundle-and-discovery.md), accepting an SVID that chains to any anchor in the current bundle. A bundle MAY contain more than one anchor (for example, during a [trust anchor rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) overlap), and every anchor in it is equally authoritative. Reject any certificate outside its validity period. A verifier MUST NOT rely on AIA fetching or other out-of-band intermediate retrieval; the presenter supplies the intermediates the chain needs. +- enforce the SPIFFE X.509-SVID leaf constraints and reject any SVID that violates them: basic-constraints `cA` MUST be `false`; `keyCertSign` and `cRLSign` MUST NOT be set in key usage; the SPIFFE ID MUST use the `spiffe` scheme with a non-root path; and the certificate MUST carry exactly one URI SAN. + +These leaf constraints, and the other structural rules of the SPIFFE X.509-SVID specification, apply during validation as well as issuance. + +On success, the verifier applies its local authorization policy to the verified SPIFFE ID (see [Identity model](identity-framework.md#identity-model)); MIAF has no central authorization server. diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md new file mode 100644 index 00000000..8f2c21f0 --- /dev/null +++ b/system-design/specification/identity/tls-requirements.md @@ -0,0 +1,60 @@ +# Transport Layer Security Requirements + +These requirements are the TLS baseline for every interface authenticated under a MIAF identity profile. The baseline defines *how* TLS and mTLS behave; each identity profile defines *where* mTLS is required (for example, the [Margo Management Interface](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication) requires it for every call). Traffic that carries a MIAF identity MUST run over TLS: the mTLS connections between principals, and the HTTPS connections a client uses to retrieve the discovery document or Trust Bundle. When a peer authenticates with mTLS, its client certificate MUST be a valid X.509-SVID issued under the applicable Trust Domain. The X.509-SVID presented at the mTLS layer is the only authenticated transport credential in scope here; JWT-SVIDs are out of scope. + +## Minimum TLS Baseline + +MIAF follows [RFC 9852](https://datatracker.ietf.org/doc/html/rfc9852), which requires TLS 1.3 as the default and permits TLS 1.2 only as a non-default fallback. + +| Requirement | Normative directive | Reference | +| :---------- | :------------------ | :-------- | +| **Default protocol version** | An implementation MUST use **TLS 1.3** as its default. | [RFC 8446](https://datatracker.ietf.org/doc/html/rfc8446), [RFC 9852](https://datatracker.ietf.org/doc/html/rfc9852) | +| **TLS 1.2 fallback** | TLS 1.2 MAY be supported as a non-default fallback where a deployment requires it. When supported, it MUST conform to [RFC 9325](https://datatracker.ietf.org/doc/html/rfc9325). | [RFC 9852](https://datatracker.ietf.org/doc/html/rfc9852), [RFC 9325](https://datatracker.ietf.org/doc/html/rfc9325) | +| **Deprecated versions** | SSL v2, SSL v3, TLS 1.0, and TLS 1.1 MUST NOT be used. | [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996) | + +The TLS 1.2 fallback carries a confidentiality cost specific to MIAF. A MIAF client certificate is an X.509-SVID, and TLS 1.2 sends the certificate messages in cleartext during the handshake (TLS 1.3 encrypts them), so a passive on-path observer can read the peer's SPIFFE ID, and with it the peer's Trust Domain and client relationship, from any TLS 1.2 mTLS handshake. Defaulting to TLS 1.3 avoids this. An operator that enables the fallback accepts the exposure (see [handshake identity disclosure on TLS 1.2 fallback](identity-security-considerations.md#framework-threats)). + +## Initial Trust Bootstrap + +A client cannot validate MIAF-issued SVIDs against the Trust Bundle until it holds that bundle, and it must acquire the first bundle before it has any MIAF credential of its own to authenticate the exchange. A client acquires the trust material (the discovery document, if used, and the Bundle Map) by one of two paths. + +**Authenticated HTTPS retrieval.** The client fetches the discovery document and the Bundle Map over HTTPS (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). Because the client holds no MIAF credential yet, these connections rely on an initial trust mechanism established outside MIAF. The client MUST authenticate both connections using at least one of: + +1. **PKI-anchored validation**: validate the [MIS](identity-framework.md#the-mis-role) server certificate chain to a configured set of trust anchors (web PKI, enterprise PKI, or an operator-configured private CA), with DNS name validation per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). +2. **Pinned trust**: validate the MIS server certificate chain or public key against operator-provisioned pins (for example, a pinned CA certificate). + +An operator MAY deliver the trust material for either option (the configured anchors for the first, or the pins for the second) through the same channel used to provision the principal's SVID. A client that cannot authenticate a connection by one of these mechanisms MUST abort. + +**Out-of-band delivery.** The operator delivers the Bundle Map, and the Trust Domain identifier and Trust Bundle URI, directly through the provisioning or deployment channel (see [Bundle Map contents and distribution](trust-bundle-and-discovery.md#bundle-map-contents-and-distribution) and the [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)). No HTTPS retrieval takes place, so there is no bootstrap connection to authenticate; the integrity and authenticity of the delivered material rest on that channel. + +Whichever path is used, a client MUST NOT accept trust material from an unauthenticated source, and MUST NOT treat the first acquisition as "trust on first use". Once acquired, the discovery document (if used) and the Bundle Map are MIAF's authoritative sources; the bundle they select then validates SVIDs within the Trust Domain. + +## Certificate Validation + +Each endpoint MUST validate the peer's TLS certificate chain and identity in accordance with [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280). The validating party MUST verify that the presented certificate chain is within its validity period and MUST reject an expired certificate. MIAF does not use RFC 5280 online revocation checking (CRL or OCSP); a compromised credential is withdrawn by removing its trust anchor from the Trust Bundle and by short SVID lifetimes (see [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook) and [Session Lifetime and Re-validation](#session-lifetime-and-re-validation)). + +Validity-period evaluation depends on a trustworthy local clock (see [unreliable validator clock](identity-security-considerations.md#framework-threats)). A verifier MAY apply a small, bounded clock-skew tolerance consistent with its time-synchronization assumptions. + +**Server identity for MIAF HTTPS endpoints (discovery and Trust Bundle retrieval):** + +- The client MUST validate the server certificate chain to its configured initial trust anchors (see [Initial Trust Bootstrap](#initial-trust-bootstrap)). +- Under PKI-anchored validation, the client MUST validate the expected DNS name per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). Under pinned trust, the pin itself establishes server identity, so RFC 6125 DNS-name validation applies only where the client connects by a DNS name; a client connecting to a pinned endpoint by IP address is not required to perform it. +- The Trust Bundle selected from `trustBundleUri` MUST NOT replace these TLS server-validation checks for MIAF HTTPS endpoints; it is used to validate SVIDs within the Trust Domain. + +**SVID identity for MIAF mTLS:** when a peer presents an X.509-SVID at the mTLS layer, the verifier validates it and derives the peer's identity per the [X.509-SVID validation](svids.md#x509-svid-validation) rules. SVID identity is established by the SPIFFE ID in the URI SAN, not by a DNS name. + +A verifier revokes access to a compromised SVID through the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook). + +## Session Lifetime and Re-validation + +mTLS authenticates a peer only at the handshake. Because MIAF revokes through short SVID lifetimes and Trust Bundle changes rather than an online status mechanism, a long-lived or pooled connection that outlives its peer's SVID, or that survives a Trust Bundle rotation, extends the revocation lag for as long as it stays open. + +A verifier therefore SHOULD bound how long an authenticated connection stays in service after the SVID that established it has expired or has ceased to validate against the current Trust Bundle, and that bound SHOULD be short relative to the SVID lifetimes in use (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). Capping the maximum age of a connection satisfies this, since the re-establishing handshake re-validates the peer's current SVID against the current Trust Bundle; a verifier MAY instead re-validate the SVID on the open connection and close it when the SVID is no longer valid, and MAY tighten the bound to the SVID's own `notAfter`. Separately, a verifier SHOULD re-evaluate its local authorization policy for the peer's SPIFFE ID on each request, so that an allowlist removal takes effect without waiting for the connection to close. + +These bounds are measured from the full TLS handshake that validated the peer's SVID, not from any later resumption of the session. A resumed session inherits the authentication time of that original handshake, so resumption MUST NOT keep a peer authenticated beyond those bounds, and a verifier that issues session tickets SHOULD limit their lifetime accordingly. A client SHOULD proactively re-establish affected connections after renewing its own SVID. + +## Traffic-Inspecting Proxies + +MIAF specifies end-to-end mTLS between a principal and a relying party. Where the relying party terminates that mTLS, in the application process or at a proxy operated within the relying party's trust boundary, is a deployment choice. A proxy that terminates the mTLS, validates the peer's SVID, and forwards the authenticated identity to a backend is treated as part of the relying party for the MIAF authentication requirement. Where such a proxy forwards the identity over an application-layer header (for example, the [RFC 9440](https://datatracker.ietf.org/doc/html/rfc9440) `Client-Cert` header), that header is the caller's identity and MUST be spoof-proof: the proxy MUST remove or overwrite any `Client-Cert` or `Client-Cert-Chain` header present on an incoming request, and the backend MUST accept a forwarded identity header only on requests arriving over the trusted proxy boundary (see [RFC 9440 §4](https://datatracker.ietf.org/doc/html/rfc9440#section-4)). + +**Traffic-inspecting proxies** (NGFW, SWG, SASE products) in the inline path between a principal and a relying party are not supported. Traffic inspection requires the proxy to MITM TLS for content inspection, which is architecturally incompatible with mTLS. An operator MUST exempt Margo mTLS endpoints from inspection. Other B2B mTLS APIs follow the same pattern, and inspection-proxy products commonly expose it as an explicit configuration option. diff --git a/system-design/specification/identity/trust-bundle-and-discovery.md b/system-design/specification/identity/trust-bundle-and-discovery.md new file mode 100644 index 00000000..8d25deae --- /dev/null +++ b/system-design/specification/identity/trust-bundle-and-discovery.md @@ -0,0 +1,137 @@ +# Trust Bundle and Discovery + +The MIS role serves two read-only HTTPS endpoints: an optional **discovery document** that points a client to the Trust Bundle, and the **Trust Bundle retrieval** endpoint itself. Because the MIS is a role rather than a fixed service (see [The MIS role](identity-framework.md#the-mis-role)), the origin hosting these endpoints is chosen by the MIS implementation; this section constrains only the path convention (when discovery is used) and the response payloads. + +Both endpoints MUST be served over HTTPS authenticated per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap), and a client MUST tolerate unknown response fields so that future revisions can add fields without breaking existing implementations. + +A machine-readable description of both endpoints is available as the [Trust Bundle API OpenAPI definition](trust-bundle-api-swagger.md). + +## Discovery Document Endpoint + +The discovery document is an optional entry point to a Trust Domain that points a client to the Trust Bundle URI. Each document describes exactly one Trust Domain. + +When discovery is used, an origin serving exactly one Trust Domain SHOULD expose the document at `GET /.well-known/margo` per [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615); an origin serving several Trust Domains MAY use other absolute HTTPS URLs. When discovery is not used, the Trust Domain identifier and Trust Bundle URI are supplied by operator-provided configuration. + +The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). + +### Route and HTTP Methods + +```https +GET /.well-known/margo +``` + +The path above is the default convention; an origin serving several Trust Domains MAY serve the document at another absolute HTTPS URL. + +### Request Headers + +| Header | Description | +| ------ | ----------- | +| `Accept` *(optional)* | The client SHOULD request the document in `application/json`. | +| `If-None-Match` *(optional)* | The `ETag` from the last successfully retrieved document, used to revalidate a cached copy. | + +### Response Codes + +| Code | Description | +| ---- | ----------- | +| 200 OK | The response body contains the discovery document. The server SHOULD include an `ETag` for cache revalidation. | +| 304 Not Modified | The cached copy is still valid; returned when the `If-None-Match` `ETag` matches. The response body is empty. | +| 404 Not Found | No discovery document is available at this origin. | + +### Response Body Attributes + +| Field | Type | Required? | Description | +| :---- | :--- | :-------- | :---------- | +| `trustDomain` | string | Y | Identifier of the Trust Domain (for example, `factory.example`). Every SPIFFE ID issued by the MIS MUST belong to this Trust Domain. | +| `trustBundleUri` | string | Y | Absolute HTTPS URL to the **SPIFFE Bundle Map** resource for this Trust Domain. The resource MUST conform to the [SPIFFE Bundle Map specification](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#5-spiffe-bundle-map) and MUST contain an entry for the domain named by `trustDomain`; that entry is the authoritative local Trust Bundle. The resource SHOULD expose an `ETag` for cache revalidation. | + +A client MUST ignore unknown fields in the discovery document. + +### Example Discovery Document Response + +Request: + +```http +GET /.well-known/margo +Accept: application/json +``` + +Response (`200 OK`): + +```json +{ + "trustDomain": "factory.example", + "trustBundleUri": "https://mis.factory.example/.well-known/spiffe/bundle.json" +} +``` + +## Trust Bundle Retrieval Endpoint + +The resource identified by `trustBundleUri` returns a SPIFFE Bundle Map. The entry keyed by the local `trustDomain` holds the authoritative set of public trust anchors for that Trust Domain. + +The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). A client cannot yet validate MIAF-issued SVIDs when it first retrieves trust material, so this connection relies on an initial trust mechanism established outside MIAF, not on a MIAF SVID. When `trustBundleUri` names a different origin than the discovery document, the client's initial-trust material (configured PKI anchors or operator-provisioned pins) MUST cover that origin. + +### Route and HTTP Methods + +```https +GET +``` + +`trustBundleUri` comes from the discovery document or from operator configuration and is an absolute HTTPS URL (for example, `https://mis.example.com/.well-known/spiffe/bundle.json`). A client MUST reject a `trustBundleUri` whose scheme is not `https`. + +### Request Headers + +| Header | Description | +| ------ | ----------- | +| `Accept` *(optional)* | The client SHOULD request the Bundle Map in `application/json`. | +| `If-None-Match` *(optional)* | The `ETag` from the last retrieved Bundle Map, used to revalidate a cached copy. | + +### Response Codes + +| Code | Description | +| ---- | ----------- | +| 200 OK | The response body is a SPIFFE Bundle Map conforming to the [SPIFFE Bundle Map format](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#5-spiffe-bundle-map). The server SHOULD include an `ETag` for cache revalidation. | +| 304 Not Modified | The cached copy is still valid; returned when the `If-None-Match` `ETag` matches. The response body is empty. | +| 404 Not Found | The bundle is unavailable. | + +### Example Bundle Map Response + +The Bundle Map is keyed by Trust Domain under `trust_domains`; the entry for the local `trustDomain` carries that domain's X.509 trust anchors as JWK entries with `"use": "x509-svid"`. Each authority's certificate travels in `x5c` (base64-encoded DER). During a [trust anchor rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) overlap the `keys` array carries more than one `x509-svid` entry. + +Response (`200 OK`): + +```json +{ + "trust_domains": { + "factory.example": { + "spiffe_sequence": 12, + "spiffe_refresh_hint": 86400, + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "x": "", + "y": "", + "use": "x509-svid", + "x5c": [""] + } + ] + } + } +} +``` + +### Selecting and Refreshing the Bundle + +A client selects the Trust Bundle for `trustDomain` from the retrieved Bundle Map and uses it as the authoritative source when validating SVIDs issued within the Trust Domain. A client that finds no entry for its `trustDomain`, or finds an entry that carries no X.509 trust anchors, MUST reject the Bundle Map and MUST NOT validate SVIDs against it, failing closed rather than proceeding with an empty anchor set. To resist rollback, where the Bundle Map carries `spiffe_sequence` a client SHOULD track the highest value it has accepted for the local Trust Domain and SHOULD reject a retrieved Bundle Map whose `spiffe_sequence` has regressed, since a lower value signals a stale, cached, or replayed bundle that could re-admit a trust anchor that was retired to revoke a compromise. + +A client SHOULD refresh its cached bundle at the interval given by the bundle's `spiffe_refresh_hint`, when present, and otherwise at an operator-configured interval. This refresh cadence is authoritative: HTTP cache revalidation (`If-None-Match`/`304`, and any `Cache-Control` freshness) is an efficiency optimization within it and MUST NOT defer a refresh the interval requires. The refresh interval bounds how quickly a Trust Bundle rotation reaches the fleet; the [trust anchor rotation playbook](identity-lifecycle.md#trust-anchor-rotation-playbook) depends on it. + +## Bundle Map Contents and Distribution + +A Trust Bundle is distributed via the SPIFFE [Trust Domain and Bundle Map](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), MAY additionally be delivered through deployment tooling or provisioning flows, and SHOULD be cached locally by a client to support offline validation. + +A Trust Domain's bundle entry contains that domain's X.509 trust anchors only; intermediate CA certificates travel with the presented SVID chain, not in the bundle (see [chain delivery](svids.md#x509-svid-profile)). + +The SPIFFE Bundle Map format reserves a slot for JWKS material. MIAF does not populate it, and an implementation MUST ignore any JWKS material found on retrieval. + +A Bundle Map can carry the bundles of several Trust Domains, but from a given `trustBundleUri` only the entry keyed by that resource's local `trustDomain` is authoritative: a client MUST use that entry, and only that entry, as the trust anchors for the local Trust Domain. A client MUST NOT treat an entry for some other Trust Domain that happens to appear in the same map as authoritative for that domain; the trust anchors for another Trust Domain MUST be obtained from that domain's own authoritative source (its `trustBundleUri` or operator configuration), so that the operator of one domain's MIS cannot vouch for another domain. Where an operator configures cross-domain trust, packing the additional domain's bundle into one map MAY serve as a delivery convenience, but each domain's anchors remain bound to that domain's authoritative source. Full [SPIFFE Federation](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md) lifecycle semantics are out of scope. diff --git a/system-design/specification/identity/trust-bundle-api-1.0.0.yaml b/system-design/specification/identity/trust-bundle-api-1.0.0.yaml new file mode 100644 index 00000000..01f86204 --- /dev/null +++ b/system-design/specification/identity/trust-bundle-api-1.0.0.yaml @@ -0,0 +1,145 @@ +openapi: 3.1.0 +info: + title: Margo Trust Bundle API + version: 1.0.0 + description: > + Read-only HTTPS endpoints served by the Margo Identity Service (MIS) role: + an optional discovery document that points a client to the Trust Bundle, + and the SPIFFE Bundle Map retrieval endpoint. + + + Both endpoints are served over HTTPS. Because a client cannot yet validate + MIAF-issued SVIDs when it first retrieves trust material, these connections + are authenticated by an initial trust mechanism established outside MIAF + (PKI-anchored validation or operator-provisioned pins), not by a MIAF SVID. + There is therefore no application-layer security scheme on these endpoints. + + + A client MUST tolerate unknown response fields so that future revisions can + add fields without breaking existing implementations. + +servers: + - url: https://mis.example.com + description: Margo Identity Service (illustrative origin) + +security: [] + +paths: + /.well-known/margo: + get: + summary: Retrieve the Trust Domain discovery document + description: > + Optional entry point to a Trust Domain that points a client to the + Trust Bundle URI. Each document describes exactly one Trust Domain. + An origin serving exactly one Trust Domain SHOULD expose the document + at this well-known path per RFC 8615; an origin serving several Trust + Domains MAY use other absolute HTTPS URLs. When discovery is not used, + the Trust Domain identifier and Trust Bundle URI are supplied by + operator-provided configuration. + parameters: + - name: If-None-Match + in: header + required: false + schema: + type: string + description: Previously returned ETag; used to revalidate a cached copy. + responses: + '200': + description: Discovery document + headers: + ETag: + schema: + type: string + description: Entity tag for cache revalidation. + content: + application/json: + schema: + $ref: '#/components/schemas/DiscoveryDocument' + '304': + description: Cached copy still valid + '404': + description: Discovery document not available + + /.well-known/spiffe/bundle.json: + get: + summary: Retrieve the SPIFFE Bundle Map + description: > + Returns the SPIFFE Bundle Map for the Trust Domain. The path shown here + is illustrative; the authoritative location is the trustBundleUri value + from the discovery document (or operator configuration). The entry keyed + by the local trustDomain holds the authoritative set of public trust + anchors for that Trust Domain. The response MUST conform to the SPIFFE + Bundle Map format + (https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#5-spiffe-bundle-map). + parameters: + - name: If-None-Match + in: header + required: false + schema: + type: string + description: Previously returned ETag; used to revalidate a cached copy. + responses: + '200': + description: SPIFFE Bundle Map + headers: + ETag: + schema: + type: string + description: Entity tag for cache revalidation. + content: + application/json: + schema: + $ref: '#/components/schemas/SpiffeBundleMap' + '304': + description: Cached copy still valid + '404': + description: Bundle unavailable + +components: + schemas: + DiscoveryDocument: + type: object + required: [trustDomain, trustBundleUri] + description: > + Describes exactly one Trust Domain. Clients MUST ignore unknown fields. + properties: + trustDomain: + type: string + description: > + Identifier of the Trust Domain (for example, factory.example). + Every SPIFFE ID issued by the MIS MUST belong to this Trust Domain. + examples: + - factory.example + trustBundleUri: + type: string + format: uri + description: > + Absolute HTTPS URL to the SPIFFE Bundle Map resource for this Trust + Domain. A client MUST reject a value whose scheme is not https. The + resource MUST contain an entry for the domain named by trustDomain; + that entry is the authoritative local Trust Bundle. + examples: + - https://mis.factory.example/.well-known/spiffe/bundle.json + SpiffeBundleMap: + type: object + description: > + SPIFFE Bundle Map. The concrete schema is defined by the SPIFFE Trust + Domain and Bundle Map specification; the map is keyed by Trust Domain + identifier. A client MUST reject the map, and MUST NOT validate SVIDs + against it, if it contains no entry for the local trustDomain or that + entry carries no X.509 trust anchors (fail closed). A JWKS slot MAY be + present but is not populated by MIAF and MUST be ignored on retrieval. + additionalProperties: true + examples: + - trust_domains: + factory.example: + spiffe_sequence: 12 + spiffe_refresh_hint: 86400 + keys: + - kty: EC + crv: P-256 + x: + y: + use: x509-svid + x5c: + - diff --git a/system-design/specification/identity/trust-bundle-api-swagger.md b/system-design/specification/identity/trust-bundle-api-swagger.md new file mode 100644 index 00000000..523039a1 --- /dev/null +++ b/system-design/specification/identity/trust-bundle-api-swagger.md @@ -0,0 +1,10 @@ +# Trust Bundle API - Swagger UI +
+ diff --git a/system-design/specification/identity/wfm-identity-profile.md b/system-design/specification/identity/wfm-identity-profile.md new file mode 100644 index 00000000..6d62bfb9 --- /dev/null +++ b/system-design/specification/identity/wfm-identity-profile.md @@ -0,0 +1,124 @@ +# WFM Identity Profile + +The WFM Identity Profile is the first identity profile under the [Margo Identity and Authorization Framework](identity-framework.md). It covers both the **WFM identity** and the **WFM Client identity**: how each is named, how each is recognized, how each is provisioned, and how the WFM authenticates and authorizes a caller at its API. + +A WFM holds an identity within the Trust Domain that anchors its namespace. A WFM Client holds an identity within that same Trust Domain, named under the WFM that issues it. Authentication is mutual: a WFM Client presents its X.509-SVID and validates the WFM's SVID, and the caller identity at the WFM API is the authenticated WFM Client SPIFFE ID carried over mTLS. + +All MIAF terminology is reused by reference unless specialized here. + +## Identity Terminology + +**WFM Identity (`wfm-id`)** is the identity of a WFM within its Trust Domain, expressed as a SPIFFE URI of the form `spiffe:///margo/wfm/` and represented by an X.509-SVID. It anchors the namespace under which WFM Client identities are issued. The `wfm-id` segment: + +- MUST be unique within the Trust Domain; +- MUST consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters; +- SHOULD be assigned by the operator deploying the WFM into the Trust Domain, not unilaterally by the WFM vendor, so that operators can prevent namespace collisions in multi-vendor deployments; and +- MUST be stable for the life of the WFM identity it names. Rebinding a `wfm-id` to a different WFM identity is not defined by this profile and MUST NOT be performed silently; replacement requires a new `wfm-id`. + +An operator MAY assign one shared `wfm-id` across several WFM instances to present a single logical identity, or distinct `wfm-id`s for each instance for finer-grained lifecycle management. + +**WFM Client Identity (`wfm-client-id`)** is the stable, verifiable identity of a WFM Client relationship within a Trust Domain, expressed as a SPIFFE URI of the form `spiffe:///margo/wfm//client/` and represented by an X.509-SVID. The `wfm-client-id` segment: + +- MUST be stable for the lifetime of the relationship; +- MUST be unique within the issuing WFM's namespace; and +- MUST consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters. + +## Identity Model + +### WFM Identity + +A WFM identity is a SPIFFE ID of the form: + +```text +spiffe:///margo/wfm/ +``` + +A WFM participating in this profile: + +- MUST hold a valid WFM X.509-SVID; and +- MUST use the same `wfm-id` in its own SPIFFE ID and in the SPIFFE IDs of the WFM Clients it accepts. + +The WFM is a principal under MIAF and obtains its SVID through the operator's provisioning channel (see [Provisioning](#provisioning)). + +### WFM Client Identity + +A WFM Client identity is a SPIFFE ID of the form: + +```text +spiffe:///margo/wfm//client/ +``` + +The `wfm-id` and `wfm-client-id` segments: + +- MUST each be non-empty, consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters, and MUST NOT be `.` or `..`; and +- carry no meaning beyond naming the WFM and the client relationship: apart from the recognition checks defined in this profile, a WFM Client MUST NOT infer structure or attributes from their content. + +All comparisons of these segments are exact and case-sensitive, following SPIFFE path semantics. This SPIFFE ID is the canonical WFM Client identity within the Trust Domain. + +### Identity Representation + +X.509-SVID is the representation used for WFM and WFM Client authentication, per the MIAF [X.509-SVID profile](svids.md#x509-svid-profile). + +### Recognition by the WFM + +A WFM MUST recognize a WFM Client from the authenticated SPIFFE ID alone. When a WFM Client connection is established, the WFM MUST: + +1. validate the presented SVID against the Trust Domain's Trust Bundle; +2. extract the SPIFFE ID from the URI SAN and verify that it has the exact form `spiffe:///margo/wfm//client/`, where `` and `` are those of the WFM's own identity; and +3. reject the connection if the SPIFFE ID does not have this form, or if its `` or `` does not match the WFM's own. + +A WFM MUST NOT treat a peer as one of its clients when the peer's SPIFFE ID does not match this shape, even if that SVID is validly issued within the Trust Domain. + +Over the life of the connection, the WFM SHOULD bound connection lifetime per the MIAF [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation) rules, and MUST authorize each request using local policy keyed on the WFM Client identity, per [Authorization](#authorization). + +### Recognition by the WFM Client + +A WFM Client MUST recognize the WFM it connects to from the authenticated SPIFFE ID alone. The WFM a client may talk to is fully determined by the client's own SVID: a client named `spiffe:///margo/wfm//client/` belongs to the WFM `spiffe:///margo/wfm/` in the same Trust Domain. The client takes the expected `` and `` from its own SVID rather than from separate configuration. For each connection, the WFM Client MUST: + +1. validate the presented WFM SVID against the Trust Domain's Trust Bundle; +2. extract the SPIFFE ID from the URI SAN; +3. verify that the SPIFFE ID is exactly `spiffe:///margo/wfm/`, using the `` and `` of the client's own SVID; and +4. abort the connection if any of these checks fails. + +A WFM Client holding a long-lived connection SHOULD bound the connection's lifetime, or otherwise re-validate the WFM SVID, per the MIAF [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation) rules, rather than relying solely on the connection-time check above. + +## Provisioning + +WFM and WFM Client SVIDs are both provisioned by the operator. The MIAF [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook) applies; the SPIFFE path and acceptance policy for each principal type are below. + +**For each WFM, the operator:** + +1. chooses a `wfm-id` for the WFM namespace; +2. mints an X.509-SVID with URI SAN `spiffe:///margo/wfm/`, conforming to the MIAF [X.509-SVID profile](svids.md#x509-svid-profile) and [cryptographic requirements](svids.md#cryptographic-requirements); and +3. installs the SVID (and private key material, if generated centrally) on the WFM. + +**For each WFM Client, the operator:** + +1. chooses a `wfm-id` for the target WFM (matching the WFM's `wfm-id`) and a `wfm-client-id` for this client relationship; +2. mints an X.509-SVID with URI SAN `spiffe:///margo/wfm//client/`, conforming to the MIAF [X.509-SVID profile](svids.md#x509-svid-profile) and [cryptographic requirements](svids.md#cryptographic-requirements); +3. installs the SVID (and private key material, if generated centrally) on the principal; +4. configures the client with the WFM's endpoint URL. The URL is routing information only: the client authenticates the WFM by its SVID, matching it against the `` and `` carried in the client's own SVID per [Recognition by the WFM Client](#recognition-by-the-wfm-client), not by the URL; and +5. adds the new `wfm-client-id` (or full SPIFFE ID) to the target WFM's accepted-client policy, so that the WFM will authorize requests from this client per [Authorization](#authorization). + +## Lifecycle + +The MIAF [lifecycle vocabulary](identity-lifecycle.md#lifecycle-vocabulary) applies to both WFM and WFM Client identities. The **Active** phase has a fully normative protocol surface: a client authenticates to a WFM over mTLS using its X.509-SVID per the Management Interface [identity and authentication](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication) rules, and validates the WFM SVID per [Recognition by the WFM Client](#recognition-by-the-wfm-client). The other phases are operator-driven: + +| Phase | WFM | WFM Client | +| :---- | :--------- | :--------- | +| Enrollment | Mint SVID with URI SAN `spiffe:///margo/wfm/`; install on the WFM. | Mint SVID with URI SAN `spiffe:///margo/wfm//client/`; install on the principal; add `wfm-client-id` to the WFM's accepted-client policy. | +| Renewal | Mint a replacement SVID (same SPIFFE ID) before expiry; install on the WFM. | Mint a replacement SVID (same SPIFFE ID) before expiry; install on the principal. | +| Revocation | Rotate the Trust Bundle to invalidate the issuing CA (this also invalidates the WFM Clients issued under that CA). See the MIAF [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook). | Remove `wfm-client-id` from the WFM's accepted-client policy. For mass revocation, rotate the Trust Bundle. | +| Re-issuance | Mint a new SVID with the same SPIFFE ID; install on the replacement WFM. | Mint a new SVID (same or new `wfm-client-id`, per operator policy); install on the replacement principal; update the WFM's accepted-client policy if the identifier changed. | + +WFM revocation is heavier-handed than WFM Client revocation because there is no client-side accepted-server allowlist comparable to the WFM's accepted-client policy. An operator reissues the WFM SVID (keeping the same SPIFFE ID) in most cases; Trust Bundle rotation is the cryptographically enforced revocation path. + +Removing a `wfm-client-id` revokes one client only where the accepted-client policy lists clients individually. Where a WFM instead accepts any client within its namespace (see [Authorization](#authorization)), there is no per-client entry to remove, so revoking a single client requires narrowing the policy to explicit entries or rotating the Trust Bundle. + +## Authorization + +A WFM MUST authorize each request using local policy keyed on the authenticated WFM Client identity. Recognizing the SPIFFE ID (see [Recognition by the WFM](#recognition-by-the-wfm)) establishes only that the caller is a validly issued client within this WFM's namespace; it does not by itself grant access. + +A WFM MUST maintain an accepted-client policy and admit a caller only when its identity is accepted by that policy; a matching `wfm-id` namespace is necessary but not sufficient. The policy MAY accept named `wfm-client-id`s (or full SPIFFE IDs) individually, and MAY accept any client within this WFM's namespace where the operator trusts the [MIS](identity-framework.md#the-mis-role) to issue identities under `spiffe:///margo/wfm//client/` only to authorized clients. How the policy is expressed is implementation-specific; the requirement is that acceptance is an explicit local decision, not an automatic consequence of holding a valid SVID. Policy MAY further consider deployment-specific `wfm-client-id` metadata, and a WFM MAY deny a request from a still-valid credential; for example, once a client relationship has been retired. + +How this profile applies to the Margo Management Interface is specified in [API Requirements and Security](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication): the endpoints served, the mTLS authentication of each call, the handling of caller identity, and how an authorization denial is surfaced (HTTP status and response body). diff --git a/system-design/specification/margo-devices/device-requirements.md b/system-design/specification/margo-devices/device-requirements.md index 607a9fac..9b9a4d6a 100644 --- a/system-design/specification/margo-devices/device-requirements.md +++ b/system-design/specification/margo-devices/device-requirements.md @@ -1,6 +1,6 @@ # Device Requirements -All current device roles MUST meet the following requirements to be considered Margo compliant. These requirements ensure devices can effectively host and manage Margo compliant workloads. A device MUST support onboarding and management from only a single workload fleet manager. +All current device roles MUST meet the following requirements to be considered Margo compliant. These requirements ensure devices can effectively host and manage Margo compliant workloads. A device MUST be managed by only a single workload fleet manager. All devices MUST provide the following components: diff --git a/system-design/specification/margo-management-interface/api-requirements-and-security.md b/system-design/specification/margo-management-interface/api-requirements-and-security.md index b52927b1..9f3e3a01 100644 --- a/system-design/specification/margo-management-interface/api-requirements-and-security.md +++ b/system-design/specification/margo-management-interface/api-requirements-and-security.md @@ -1,135 +1,56 @@ # API Requirements and Security Details ## General Requirements - The Workload Fleet Management supplier MUST implement the server side of the API specification contract. -- The Device supplier, via it's WFM Client, MUST implement the client side of the API specification contract. +- The Device supplier, via its WFM Client, MUST implement the client side of the API specification contract. -Below is a breakdown of the three major categories these requirements fall under: +Below is a breakdown of the two major categories these requirements fall under: -1. Basic functions for supporting the Management Interface - - Onboarding of the management interface client -2. Workload management functions +1. Workload management functions - Set Desired State(s) assigned to particular device clients -3. Device client specific functions - - Client Onboarding +2. Device client specific functions - Device Capability Reporting - Workload Status deployment reporting -### API Technical Overview -#### REST API Definition -The REST API is defined via the Open API Specification. +Identity and authentication for the Management Interface are provided by the [Margo Identity and Authorization Framework](../identity/identity-framework.md) and the [WFM Identity Profile](../identity/wfm-identity-profile.md). A WFM Client and a WFM are each provisioned with an X.509-SVID before any Management Interface call is made. -- Follow the link below to see the specification: - - [OpenAPI Specification](https://github.com/margo/specification/blob/pre-draft/system-design/specification/margo-management-interface/workload-management-api-1.0.0.yaml) - - [Swagger UI](../margo-management-interface/management-interface-swagger.md) -#### General Rest API information -Server-side TLS REST API MUST be utilized operating over HTTP1.1. +## API Definition +The REST API is defined via the OpenAPI Specification: -- The motivation to utilize HTTP1.1 is to ensure maximum support for existing infrastructure within our install base. -- Server-side tls is utilized instead of mTLS due to possible issues with TLS terminating HTTPS load-balancer or a HTTPS proxy doing lawful inspection. See [Security and Integrity](#security-and-integrity-information) section for more details. -- The client MUST trust the WFM's server certificate through a pre-provisioned or securely obtained root CA. - - The Certificate API MAY be used to retrieve this CA over an already trusted channel. +- [OpenAPI Specification](https://github.com/margo/specification/blob/pre-draft/system-design/specification/margo-management-interface/workload-management-api-1.0.0.yaml) +- [Swagger UI](../margo-management-interface/management-interface-swagger.md) -#### Authentication Mechanism -Initial trust is accomplished via TLS version 1.3 or greater +## Transport +The REST API MUST operate over HTTP/1.1; HTTP/1.1 is used to ensure maximum support for existing infrastructure within our install base. The transport is secured by mTLS as specified in [Identity and Authentication](#identity-and-authentication). -- The device establishes a secure HTTPS connection using server-side TLS. -- It validates the server’s identity using the public root CA certificate. -- Client authentication is achieved using application-layer HTTP Message Signatures (RFC 9421), as defined in the [Payload Security](#payload-security-method) section. +To minimize the ports required on the customer's infrastructure for cloud to edge communication, the API MUST use only port 443 for its traffic. -#### API Port Details +## Identity and Authentication +Authentication is mutual TLS per the MIAF [TLS requirements](../identity/tls-requirements.md). Both sides present an X.509-SVID and validate the peer's SVID and `wfm-id` per the WFM Identity Profile ([Recognition by the WFM](../identity/wfm-identity-profile.md#recognition-by-the-wfm), [Recognition by the WFM Client](../identity/wfm-identity-profile.md#recognition-by-the-wfm-client)). A WFM MUST reject any Management Interface request that is not authenticated by mTLS with a valid WFM Client X.509-SVID. -This API is designed to minimize the ports required on the customer's infrastructure to enable cloud to edge communication. - - The API MUST ONLY utilize port 443 for its traffic. +The caller identity for every request is the authenticated WFM Client SPIFFE ID; the request itself does not carry it. A WFM derives the caller from the SPIFFE ID, not from any identifier in the request path or body. -#### Unique Identifiers +Every Management Interface endpoint is implicitly scoped to the authenticated caller. A WFM associates the resources a client reports (device capabilities and deployment status) with that client's identity, and MUST NOT expose or mutate one client's resources on behalf of another. -The WFM MUST create a URL-safe client ID to uniquely identify each client within the architecture. +The WFM authorizes each request using local policy keyed on the authenticated WFM Client identity, and MAY deny a request from a still-valid credential, per [Authorization](../identity/wfm-identity-profile.md#authorization). When a WFM denies a request by local policy (for example, a retired client relationship), it SHOULD respond `403 Forbidden` with an [RFC 9457](https://datatracker.ietf.org/doc/html/rfc9457) Problem Details body (`Content-Type: application/problem+json`) using the `wfm-client-relationship-retired` type: -- This client ID MAY be in the format of UUIDv4 -- Other URL safe string identifiers are permitted to be used - - -#### Certificate Information -Both the WFM and Device client MUST utilize X.509 certificates to represent themselves within the API interactions. +```json +{ + "type": "https://docs.margo.org/specification/problems/wfm-client-relationship-retired", + "title": "Client Relationship Retired", + "status": 403, + "detail": "The WFM Client relationship has been retired by local policy." +} +``` -- The WFM's certificate is used to authenticate the server during TLS. The WFM Client’s certificate is used to authenticate the client and sign request payloads at the application layer, not during the TLS handshake. -- All client certificates MUST conform to RFC 5280 standards. -- Signature algorithm requirements: - - Implementations MUST support the following set of allowable signature algorithms: - - ecdsa-p256-sha256 - - ecdsa-p384-sha384 - - rsa-v1_5-sha256 - - rsa-pss-sha256 - - Additional algorithms MAY be supported, but the above list defines the minimum baseline for interoperability. - - The server MUST support all required algorithms (see above), so it can verify any compliant client, while clients MAY choose any of the required algorithms that fits their hardware capabilities. +Both parties represent themselves with an X.509-SVID, an X.509 certificate carrying a SPIFFE ID in its URI SAN. SVID structure, key and signature algorithms, and validation follow the MIAF [X.509-SVID profile](../identity/svids.md#x509-svid-profile) and [cryptographic requirements](../identity/svids.md#cryptographic-requirements). +The Management Interface follows the MIAF [traffic-inspecting proxies](../identity/tls-requirements.md#traffic-inspecting-proxies) rules: a TLS-offloading proxy that forwards the validated client identity to the backend is supported, and an operator MUST exempt Margo mTLS endpoints from inline traffic inspection. -#### Support for Extended Device Communications Downtime -Interface patterns MUST support extended device communication downtime. +## Extended Device Downtime +Interface patterns MUST support extended device communication downtime. - The Management Interface MUST allow an end user to configure the following: - - Downtime configuration - ensures the device's management client is not retrying communication when operating under a known downtime. Additionally, communication errors MUST be ignored during this configurable period. + - Downtime configuration - ensures the device's management client is not retrying communication when operating under a known downtime. Additionally, communication errors MUST be ignored during this configurable period. - Polling Interval Period - describes a configurable time period indicating the hours in which the device's management client checks for updates to the device's desired state. - Polling Interval Rate - describes the rate for how frequently the device's management client checks for updates to the device's desire state. - Running the device's management client as containerized services is preferred. By following Margo application packaging guidelines, it makes the management interface easier to lifecycle manage, however this is not required. - -### Payload Security Method -#### Security and Integrity Information -Due to the limitations of utilizing mTLS with common OT infrastructure components, such as TLS-terminating HTTPS load-balancer or a HTTPS proxy doing lawful inspection, Margo has adopted a certificate-based payload signing approach to protect payloads from being tampered with. By utilizing the certificates to create payload envelopes (HTTP Request body), the device's management client can ensure secure transport between the device's management client and the Workload Fleet Management's web service. - -- For API security, server-side TLS 1.3 (minimum) is used, where the keys are obtained from the Server's X.509 Certificate as defined in standard HTTP over TLS -- For API integrity, the device's management client is issued a client-specific X.509 certificate. -- The issuer of the client X.509 certificate is trusted under the assumption that the root CA download to the Workload Fleet Management server occurs as a precondition to onboarding the devices -- Similarly, the issuer of the server X.509 certificate is trusted under the assumption that the root CA download to the device's management client occurs over a "protected" connection as part of the yet to be defined device onboarding procedure -#### Device Management Client -Once the device management client has a message prepared for the Workload Fleet Management's web service, it MUST establish message integrity as defined in RFC 9421 by performing the following steps: - -- The device's management client MUST generate a SHA256 digest of the HTTP request body. Encode the digest in Base64 and include it in the Content-Digest header. -``` - Content-Digest: sha-256=:: -``` -- The device management client MUST create a Signature Base String including @method, @target-uri, and Content-Digest. An example is given below. -``` - @method: POST - @target-uri: https://api.example.com/resource - Content-Digest: sha-256=:: - @signature-params: ("@method" "@target-uri" "Content-Digest");created=1680575171;keyid="my-rsa-key" -``` -- The device management client MUST generate the Signature field by signing the raw byte array of the Signature Base String using the clients X.509 private key. The resulting signature in Base64 is included in the Signature header. -- The devices's management client MUST insert the following in the HTTP1.1 Header: - - Content-Digest as formed above - - Signature-Input as given below, replacing the created and keyid parts appropriately: - ``` - sig1=("@method" "@target-uri" "Content-Digest");created=;keyid="" - ``` - - Signature: - ``` - sig1=:: - ``` - -> Note: The server MUST use the `created` timestamp from the signature input to detect and prevent replay attacks. Requests with a `created` timestamp older than a configurable validity window (e.g., 5 minutes) or in the future (allowing for clock skew) MUST be rejected. - -#### Workload Fleet Manager Web-Service -- On receiving the message from the Device Client, the Workload Fleet Management's web service MUST do the following : - - It looks up the client certificate from the Client-ID in the API Request URL - - The Workload Fleet Management's web service reads the following from the HTTP Request Header: - - Signature-Input - - Signature - - Content-Digest (if body is present) - - Use the Signature-Input in the header to determine which components were signed. Reconstruct the Signature Base canonical string using the actual values from the request, including the SHA256 encoded content-digest from the received request body - - Then extract the base64-encoded message signature from the Signature header and verifies the message signature string using the client's X.509 public-key. - - If the message signature in the HTTP Header and the verified message signature match, then the payload is processed by the Workload Fleet Management's web service. - - If the two do not match, the Workload Fleet Manager will respond with HTTP Error 401 as given below, and discontinue the session - ``` - HTTP/1.1 401 Unauthorized - Content-Type: application/json - { - "error": "Invalid signature", - "message": "The X-Body-Signature header does not match the content of the request body." - } - ``` -#### Open Source examples: -- Full RFC 9421 implementation with support for RSA, ECDSA, HMAC - - [LINK](https://github.com/lestrrat-go/htmsig) -- Feature-complete RFC 9421 implementation with RSA-PSS and RSA-v1_5 support - - [LINK](https://github.com/yaronf/httpsign) diff --git a/system-design/specification/margo-management-interface/certificate-api.md b/system-design/specification/margo-management-interface/certificate-api.md deleted file mode 100644 index 65e4c48d..00000000 --- a/system-design/specification/margo-management-interface/certificate-api.md +++ /dev/null @@ -1,26 +0,0 @@ -# Certificate API - -In order to facilitate secure communication between the device's management client and the Workload Fleet Manager, the device must first retrieve the root CA certificate using the Onboarding API's `certificate` endpoint. - -- This endpoint MAY only be used to retrieve the root CA over an already trusted channel. -- If no trusted channel exists, the root CA MUST be provided via an out-of-band method. -- In the response body, the certificate field MUST contain the PEM-encoded X.509 root CA certificate, Base64 without line breaks. - -## Route and HTTP Methods - -```http -GET /api/v1/onboarding/certificate -``` -### Response Code - -| Code | Description | -|------|-------------| -| 200 OK | Root CA certificate | - -## Example Response Body - -```json -{ - "certificate":"" -} -``` diff --git a/system-design/specification/margo-management-interface/deployment-status.md b/system-design/specification/margo-management-interface/deployment-status.md index 4702e0bc..4265b687 100644 --- a/system-design/specification/margo-management-interface/deployment-status.md +++ b/system-design/specification/margo-management-interface/deployment-status.md @@ -2,21 +2,18 @@ While applying a new desired state, the device's management client MUST provide the Workload Fleet Manager service with an indication of the current workload deployment status. This is done by calling the Device API's `deployment status` endpoint. -- Requests to this endpoint MUST be authenticated using the HTTP Message Signature method as defined in the [Payload Security](../margo-management-interface/api-requirements-and-security.md#payload-security-method) section. - > Note: This assumes consistent connection to the WFM, we will address intermittent or extended disconnection scenarios in the future. ## Route and HTTP Methods ```https -POST /api/v1/clients/{clientId}/deployments/{deploymentId}/status +POST /api/v1/deployments/{deploymentId}/status ``` ### Route Parameters |Parameter | Type | Required? | Description| |----------|------|-----------|------------| -| {clientId} | string | Y | The unique identifier of the (device) client registered with the WFM during onboarding. | | {deploymentId} | string | Y | The UUID of the `ApplicationDeployment` YAML being reported. ### Response Codes @@ -24,9 +21,8 @@ POST /api/v1/clients/{clientId}/deployments/{deploymentId}/status | Code | Description | |------|-------------| | 200 OK | The deployment status was added, or updated, successfully. | -| 400 Bad Request | Missing or invalid content-digest header. Ensure the SHA256 hash of the base64-encoded payload is included. | -| 401 Unauthorized | Signature verification failed. Ensure you are signing with the correct X.509 private key. | -| 403 Forbidden | Client certificate is not trusted or has been revoked. | +| 400 Bad Request | Malformed request body. | +| 403 Forbidden | The request is not authorized by the WFM's local policy (for example, the client relationship has been retired; see [Authorization](../identity/wfm-identity-profile.md#authorization)). | | 422 Unprocessable Content | Request body includes a semantic error. | diff --git a/system-design/specification/margo-management-interface/device-capabilities.md b/system-design/specification/margo-management-interface/device-capabilities.md index 7f9cb9db..f5eaa7dd 100644 --- a/system-design/specification/margo-management-interface/device-capabilities.md +++ b/system-design/specification/margo-management-interface/device-capabilities.md @@ -1,26 +1,23 @@ # Device Capabilities -Devices MUST provide the Workload Fleet Management service with their capabilities and characteristics. This is done by calling the Device API's `device capabilities` endpoint. Reporting the device capabilities is the final step in the onboarding of the device's client. +Devices MUST provide the Workload Fleet Management service with their capabilities and characteristics. This is done by calling the Device API's `device capabilities` endpoint. Reporting the device capabilities is the first exchange a WFM Client makes after it is provisioned and connects to the WFM. The reported capabilities represent only the subset of device resources delegated exclusively to Margo for workload placement and reconciliation, and do not necessarily reflect the device's total physical capacity. The mechanisms used to isolate, reserve, and preserve these resources for exclusive Margo use are implementation-specific and outside the scope of this specification. To ensure the WFM is kept up to date, the device's client MUST send updated capabilities information if any changes occur to the information originally provided (i.e., additional memory is added to the device). -- Requests to this endpoint MUST be authenticated using the HTTP Message Signature method as defined in the [Payload Security](../margo-management-interface/api-requirements-and-security.md#payload-security-method) section. - ## Route and HTTP Methods ```https -POST /api/v1/clients/{clientId}/capabilities/{deviceId} -PUT /api/v1/clients/{clientId}/capabilities/{deviceId} -DELETE /api/v1/clients/{clientId}/capabilities/{deviceId} +POST /api/v1/capabilities/{deviceId} +PUT /api/v1/capabilities/{deviceId} +DELETE /api/v1/capabilities/{deviceId} ``` ### Route Parameters |Parameter | Type | Required? | Description| |----------|------|-----------|------------| -| {clientId} | string | Y | The unique identifier of the (device) client registered with the WFM during onboarding. | | {deviceId} | string | Y | The unique identifier of the device reporting the capabilities.
It must have the following format: "{id}[/{id}[/{id}...]]". The top-level `id` is required and must include only unreserved characters as specified in [RFC3986](https://www.rfc-editor.org/rfc/rfc3986#section-2.3). If reporting capabilties for a child device, the subsequent `id`s are required and must include only unreserved characters as specified in [RFC3986](https://www.rfc-editor.org/rfc/rfc3986#section-2.3).
Using multiple ids in the endpoint does not register multiple devices in a single request, but indicates a hierarchy of devices, with a parent/child relationship. | ### Response Codes @@ -29,10 +26,9 @@ DELETE /api/v1/clients/{clientId}/capabilities/{deviceId} |------|-------------| | 201 OK | The device capabilities document was added, or updated, successfully | | 204 No Content | The device capabilities document was deleted successfully. | -| 400 Bad Request | Missing or invalid content-digest header. Ensure the SHA256 hash of the base64-encoded payload is included. | -| 401 Unauthorized | Signature verification failed. Ensure you are signing with the correct X.509 private key. | -| 403 Forbidden | Client certificate is not trusted or has been revoked. | -| 404 Not Found | POST, PUT: No client with the given `clientID` was found, or no gateway was found for the given child-device `deviceId` (see [Gateways considerations](#gateways-considerations) for more details).
DELETE: No client with the given `clientID` was found or no device with the given `deviceId` was found for the client. | +| 400 Bad Request | POST, PUT: Malformed request body. | +| 403 Forbidden | The request is not authorized by the WFM's local policy (for example, the client relationship has been retired; see [Authorization](../identity/wfm-identity-profile.md#authorization)). | +| 404 Not Found | POST, PUT: No gateway was found for the given child-device `deviceId` (see [Gateways considerations](#gateways-considerations) for more details).
DELETE: No device with the given `deviceId` was found for the client. | | 422 Unprocessable Content | Request body includes a semantic error. | ## Request Body Attributes @@ -194,7 +190,7 @@ A WFM client reporting the "Gateway" role MUST report its own capabilities to th * See-thru gateway, without hosting capabilities, reporting its capabilities to the WFM: ``` - POST /api/v1/clients/{clientId}/capabilities/gateway1 + POST /api/v1/capabilities/gateway1 ``` ```json { @@ -215,7 +211,7 @@ A WFM client reporting the "Gateway" role MUST report its own capabilities to th * See-thru gateway, with hosting capabilities, reporting its capabilities to the WFM: ``` - POST /api/v1/clients/{clientId}/capabilities/gateway1 + POST /api/v1/capabilities/gateway1 ``` ```json { @@ -253,7 +249,7 @@ A WFM client reporting the "Gateway" role MUST report its own capabilities to th * See-thru gateway reporting the capabilities of a child device to the WFM: ``` - POST /api/v1/clients/{clientId}/capabilities/gateway1/deviceA + POST /api/v1/capabilities/gateway1/deviceA ``` ```json { @@ -296,7 +292,7 @@ A WFM client reporting the "Gateway" role MUST report its own capabilities to th * See-thru gateway reporting the capabilities of a child device with deeper hierarchy to the WFM: ``` - POST /api/v1/clients/{clientId}/capabilities/gateway1/path1/deviceA + POST /api/v1/capabilities/gateway1/path1/deviceA ``` ```json { @@ -333,5 +329,5 @@ A WFM client reporting the "Gateway" role MUST report its own capabilities to th * See-thru gateway informing the WFM that a child device is no longer available: ``` - DELETE /api/v1/clients/{clientId}/capabilities/gateway1/deviceA + DELETE /api/v1/capabilities/gateway1/deviceA ``` diff --git a/system-design/specification/margo-management-interface/device-client-onboarding.md b/system-design/specification/margo-management-interface/device-client-onboarding.md deleted file mode 100644 index fe90cf88..00000000 --- a/system-design/specification/margo-management-interface/device-client-onboarding.md +++ /dev/null @@ -1,100 +0,0 @@ -# Device Client Onboarding -In order for the Workload Fleet Management software to manage the edge device's workloads, the device's management client must first complete onboarding. - -- Requests to this endpoint MUST be authenticated using the HTTP Message Signature method as defined in the [Payload Security](../margo-management-interface/api-requirements-and-security.md#payload-security-method) section. - -## Onboarding Sequence - -- The end user provides the the Workload Fleet Management web service's root URL to the device's management client -- The device's management client downloads the Workload Fleet Manager's public root CA certificate using the [Certificate API](../../specification/margo-management-interface/certificate-api.md) -- Context and trust is established between the device's management client and the Workload Fleet Management web service -- The device's management client uses the [Onboarding API](../../specification/margo-management-interface/device-client-onboarding.md) to onboard with the Workload Fleet Management service by providing its X.509 certificate -- The device's management client receives its unique client Id assigned via the Workload Fleet Manager -- The [device capabilities](../../concepts/workload-fleet-managers/device-capabilities.md) information is sent from the device to the WFM using the [Device API](../../specification/margo-management-interface/device-capabilities.md) - -## Onboarding Sequence diagram - -```mermaid -sequenceDiagram - autonumber - participant Client - participant Server - - Note over Client,Server: 🔐 Initial Trust Establishment - Note over Client,Server: The device's management client obtains the WFM's root CA certificate either out-of-band or via the Certificate API over an existing trusted channel. - Client->>Server: GET /onboarding/certificate - Server-->>Client: Base64-encoded Root CA certificate - Client-->>Client: Injecting Root CA into trusted store - - Note over Client,Server: 🔒 Standard TLS Handshake Start - Client->>Server: TLS ClientHello (TLS versions, cipher suites, random) - Server-->>Client: TLS ServerHello (chosen version, cipher, random) - Server-->>Client: X.509 certificate chain(server + intermediate(if applicable)) - Client-->>Client: Verifies server cert chain based on RootCA - Note over Client,Server: 🔒 Standard TLS Handshake completed - WFMUser->>Server: Opportunity for user to manually upload client certificate - Note over Client,Server: Device Client onboarding begins - Client->>Server: POST /onboarding using the device's public certificate (could exchange additional info) - WFMUser->>Server: Opportunity for user to approve or reject client onboarding - alt Public key trusted/user approved - Note over Server: Server verifies client certificate and assigns UUID - Server-->>Client: 201 Created {clientId} - else Client rejected - Server-->>Client: 400 Bad Request {error: "Invalid certificate"} - else Client rejected - Server-->>Client: 403 Forbidden {error: "Client rejected"} - end - - Note over Client,Server: Device Client onboarding Ends - Note over Client,Server: 📡 Secure API Usage with Signed Payloads can now begin - - Client->>Server: POST /clients/{clientId}/capabilities - Server-->>Client: 201 Created - Note over Client,Server: Enrollment is complete - Note over Client,Server: 📡 Secure API Usage with Signed Payloads - Client->>Server: POST /clients/{clientId}/deployments/{deploymentId}/status - Server-->>Client: 201 Created -``` - -## Onboarding API Details - -## Route and HTTP Methods - -```https -POST /api/v1/onboarding -``` -## Request Body Attributes - -| Fields | Type | Required? | Description | -|-----------------|-----------------|-----------------|-----------------| -| apiVersion | string | Y | Identifier of the version the API resource follows.| -| kind | string | Y | Must be `OnboardingRequest`.| -| certificate | string | Y | Base64-encoded X.509 certificate of the client. | - -### Example Request Body - -```json -{ - "apiVersion": "onboarding.margo.org/v1alpha1", - "kind": "OnboardingRequest", - "certificate": "MIIDdzCCAl+gAwIBAgIEb1......." -} -``` - -### Response Codes - -| Code | Description | -|------|-------------| -| 201 Created | New client onboarded successfully. | -| 400 Invalid Certificate | Invalid certificate format or structure. | -| 403 Forbidden | Client certificate is not trusted or client rejected. | - -## Example Response Body - -```json -{ - "clientId": "" -} -``` - - diff --git a/system-design/specification/margo-management-interface/workload-management-api-1.0.0.yaml b/system-design/specification/margo-management-interface/workload-management-api-1.0.0.yaml index 829889d9..8ccdc4a9 100644 --- a/system-design/specification/margo-management-interface/workload-management-api-1.0.0.yaml +++ b/system-design/specification/margo-management-interface/workload-management-api-1.0.0.yaml @@ -1,101 +1,30 @@ -openapi: 3.0.3 +openapi: 3.1.0 info: title: Margo Workload Management API version: 1.0.0 - description: + description: > API for managing workloads on Margo-compliant edge devices. Includes the APIs for exchanging desired state and current state. - Communication is secured using server-side TLS (TLS 1.3 preferred), - and payloads are signed using X.509 certificates. + Communication is secured with mutual TLS, defaulting to TLS 1.3 per the MIAF + TLS requirements: the WFM Client + and the WFM each present an X.509-SVID, validated against the Trust Domain's + Trust Bundle per the Margo Identity and Authorization Framework (MIAF) and + the WFM Identity Profile. The caller identity for every request is the + authenticated WFM Client SPIFFE ID. Trust anchors are distributed via the + MIAF Trust Bundle. servers: - - url: https://wfm.margo.org/ + - url: https://wfm.margo.org description: Workload Fleet Manager API security: - - PayloadSignature: [] + - mTLS: [] paths: - /api/v1/onboarding/certificate: - get: - summary: Download Root CA certificate - security: [] - responses: - '200': - description: Root CA certificate - content: - application/json: - schema: - type: object - properties: - certificate: - type: string - description: Base64-encoded certificate text - /api/v1/onboarding: - post: - requestBody: - content: - application/json: - schema: - type: object - required: [apiVersion, kind, certificate] - properties: - apiVersion: - type: string - description: API version identifier - kind: - type: string - enum: [OnboardingRequest] - description: Resource kind - certificate: - description: Base64-encoded client certificate - type: string - required: true - responses: - '201': - content: - application/json: - schema: - properties: - clientId: - type: string - type: object - description: New client onboarded successfully. - '400': - content: - application/json: - schema: - properties: - error: - example: Invalid certificate - type: string - type: object - description: Invalid certificate format or structure. - '403': - content: - application/json: - schema: - properties: - error: - example: Client rejected - type: string - type: object - description: Client certificate not trusted or client rejected. - security: - - PayloadSignature: [] - summary: Complete onboarding with client certificate - - /api/v1/clients/{clientId}/capabilities/{deviceId}: + /api/v1/capabilities/{deviceId}: post: summary: Report device capabilities - security: - - PayloadSignature: [] parameters: - - name: clientId - in: path - required: true - schema: - type: string - name: deviceId in: path required: true @@ -111,25 +40,16 @@ paths: '201': description: Capabilities reported successfully '400': - description: Missing or invalid content-digest header. Ensure the SHA256 hash of the payload is included. - '401': - description: Signature verification failed. Ensure you are signing with the correct X.509 private key. + description: Malformed request body. '403': - description: Client certificate is not trusted or has been revoked. + description: The request is not authorized by the WFM's local policy (for example, the client relationship has been retired). '404': - description: No client with the given `clientID` was found. + description: No gateway was found for the given child-device deviceId. '422': description: Request body includes a semantic error. put: summary: Update device capabilities (Update) - security: - - PayloadSignature: [] parameters: - - name: clientId - in: path - required: true - schema: - type: string - name: deviceId in: path required: true @@ -145,25 +65,16 @@ paths: '201': description: Capabilities reported successfully '400': - description: Missing or invalid content-digest header. Ensure the SHA256 hash of the payload is included. - '401': - description: Signature verification failed. Ensure you are signing with the correct X.509 private key. + description: Malformed request body. '403': - description: Client certificate is not trusted or has been revoked. + description: The request is not authorized by the WFM's local policy (for example, the client relationship has been retired). '404': - description: No client with the given `clientID` was found. + description: No gateway was found for the given child-device deviceId. '422': description: Request body includes a semantic error. delete: summary: Remove device (Unregister) - security: - - PayloadSignature: [] parameters: - - name: clientId - in: path - required: true - schema: - type: string - name: deviceId in: path required: true @@ -172,26 +83,14 @@ paths: responses: '204': description: Device capabilities removed successfully - '400': - description: Missing or invalid content-digest header. Ensure the SHA256 hash of the payload is included. - '401': - description: Signature verification failed. Ensure you are signing with the correct X.509 private key. '403': - description: Client certificate is not trusted or has been revoked. + description: The request is not authorized by the WFM's local policy (for example, the client relationship has been retired). '404': - description: Client or device not found. - /api/v1/clients/{clientId}/bundles/{digest}: + description: No device with the given deviceId was found for the client. + /api/v1/bundles/{digest}: get: - summary: Retrieve bundle information for a specific device and digest - security: - - PayloadSignature: [] + summary: Retrieve a deployment bundle by its content digest parameters: - - name: clientId - in: path - required: true - schema: - type: string - description: Unique identifier of the device-client - name: digest in: path required: true @@ -211,11 +110,11 @@ paths: ETag: schema: type: string - description: New ETag for the returned manifest + description: Quoted digest of the returned bundle archive Cache-Control: schema: type: string - description: public, max-age=31536000, immutable + description: private, max-age=31536000, immutable content: application/vnd.margo.bundle.v1+tar+gzip: schema: @@ -224,26 +123,15 @@ paths: description: Gzip-compressed tar containing one YAML file per deployment. '304': description: Representation not modified + '403': + description: The request is not authorized by the WFM's local policy (for example, the client relationship has been retired). '404': description: Bundle not found for the given digest - '400': - description: Invalid request. - # TBD - # '500': - # $ref: '#/components/responses/ErrorResponse' - /api/v1/clients/{clientId}/deployments: + /api/v1/deployments: get: summary: Retrieve the complete desired state for all workloads assigned to a device - security: - - PayloadSignature: [] parameters: - - name: clientId - in: path - required: true - schema: - type: string - description: The unique identifier of the Edge Compute Device making the request - name: If-None-Match in: header required: false @@ -271,31 +159,29 @@ paths: schema: type: string description: New ETag for the returned manifest + Cache-Control: + schema: + type: string + description: private content: application/vnd.margo.manifest.v1+json: schema: $ref: '#/components/schemas/UnsignedAppStateManifest' '304': description: Not Modified - Manifest has not changed + '403': + description: The request is not authorized by the WFM's local policy (for example, the client relationship has been retired). '406': description: Not Acceptable - Server cannot generate a response matching the Accept header - /api/v1/clients/{clientId}/deployments/{deploymentId}/{digest}: + /api/v1/deployments/{deploymentId}/{digest}: get: summary: Retrieve an individual ApplicationDeployment YAML file - security: - - PayloadSignature: [] description: > This endpoint is used by the client to fetch the YAML for a single ApplicationDeployment after it has processed a new State Manifest and identified a small number of new or updated deployments. This allows for highly efficient, incremental updates without needing to download the full bundle. To make individual workload retrievals race-free and cache-friendly, this endpoint is content-addressable: the digest of the expected YAML is part of the URL. This guarantees immutability of the fetched resource and prevents a time-of-check / time-of-use race where a deployment changes between manifest retrieval and content fetch. parameters: - - name: clientId - in: path - required: true - schema: - type: string - description: Unique identifier of the Edge Compute Device - name: deploymentId in: path required: true @@ -339,7 +225,7 @@ paths: Cache-Control: schema: type: string - description: public, max-age=31536000, immutable + description: private, max-age=31536000, immutable Vary: schema: type: string @@ -349,20 +235,15 @@ paths: schema: type: string description: Raw YAML content of the ApplicationDeployment + '403': + description: The request is not authorized by the WFM's local policy (for example, the client relationship has been retired). '404': description: Deployment not found for the given digest - /api/v1/clients/{clientId}/deployments/{deploymentId}/status: + /api/v1/deployments/{deploymentId}/status: post: summary: Report deployment status - security: - - PayloadSignature: [] parameters: - - name: clientId - in: path - required: true - schema: - type: string - name: deploymentId in: path required: true @@ -378,24 +259,22 @@ paths: '200': description: The deployment status was added, or updated, successfully. '400': - description: Missing or invalid content-digest header. Ensure the SHA256 hash of the base64-encoded payload is included. - '401': - description: Signature verification failed. Ensure you are signing with the correct X.509 private key. + description: Malformed request body. '403': - description: Client certificate is not trusted or has been revoked. + description: The request is not authorized by the WFM's local policy (for example, the client relationship has been retired). '422': description: Request body includes a semantic error. components: securitySchemes: - # TODO: fix this as we are following RFC 9421, instead of a custom signature header field - PayloadSignature: - type: apiKey - in: header - name: X-Payload-Signature + mTLS: + type: mutualTLS description: > - Base64-encoded payload signature using SHA-256 and device certificate. - Format: public_key;digital_signature + Mutual TLS, defaulting to TLS 1.3 (TLS 1.2 permitted only as a + non-default fallback). The WFM Client and the WFM each + present an X.509-SVID, validated against the Trust Domain's Trust + Bundle per the Margo Identity and Authorization Framework (MIAF) + and the WFM Identity Profile. schemas: ManifestVersion: type: number @@ -403,8 +282,7 @@ components: Monotonically increasing unsigned 64-bit integer in the inclusive range [1, 2^64-1]. Prevents rollback attacks. The first manifest MUST use 1. DeploymentBundleRef: - type: object - nullable: true + type: [object, 'null'] description: > Describes a single archive containing all ApplicationDeployment documents. If there are zero deployments (deployments array is empty) the property MUST be present with the value null (it MUST NOT be omitted). properties: @@ -423,7 +301,7 @@ components: url: type: string description: > - Content-addressable retrieval endpoint of the form /api/v1/clients/{clientId}/bundles/{digest} where {digest} equals bundle.digest. + Content-addressable retrieval endpoint of the form /api/v1/bundles/{digest} where {digest} equals bundle.digest. DeploymentManifestRef: type: object description: > @@ -448,7 +326,7 @@ components: url: type: string description: > - Content-addressable endpoint of the form /api/v1/clients/{clientId}/deployments/{deploymentId}/{digest}. The {digest} MUST equal deployments[].digest; the referenced resource is immutable + Content-addressable endpoint of the form /api/v1/deployments/{deploymentId}/{digest}. The {digest} MUST equal deployments[].digest; the referenced resource is immutable UnsignedAppStateManifest: type: object required: From 9e36da65219e760b642b9e5c3bb52469217c54ee Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Tue, 21 Jul 2026 09:12:40 +0000 Subject: [PATCH 02/26] docs: require a CSPRNG for SVID key generation Signed-off-by: Matthias Lechner --- system-design/specification/identity/svids.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system-design/specification/identity/svids.md b/system-design/specification/identity/svids.md index 0899b35d..b74ac409 100644 --- a/system-design/specification/identity/svids.md +++ b/system-design/specification/identity/svids.md @@ -26,6 +26,8 @@ This section constrains the signature algorithms and key parameters used for SVI > **Crypto-agility (informative):** MIAF names its algorithms explicitly so the permitted set can evolve. The set above is classical; post-quantum signature suites are expected to arrive as additional permitted algorithms rather than a redesign of the framework. Defaulting the transport to TLS 1.3 (see [TLS requirements](tls-requirements.md)) supports this: it is the version track on which post-quantum key exchange and authentication are being standardized. +Keys MUST be generated with a cryptographically secure random number generator seeded from an entropy source carrying enough genuine entropy for the key size ([RFC 4086](https://datatracker.ietf.org/doc/html/rfc4086)). + These requirements apply to MIAF-generated artifacts and to the keys used in SVIDs and CSRs. They do not constrain an external bootstrap ecosystem (for example, a manufacturer PKI used as a bootstrap input), which MAY use algorithms permitted by its governing standards, subject to Trust Domain policy. ## X.509-SVID Validation From ff92bb6253a978d42b6c5c9730af23614d963c1d Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 10:31:12 +0000 Subject: [PATCH 03/26] docs: serve a single SPIFFE bundle from the trust endpoint Rework the Trust Bundle retrieval endpoint to return a single SPIFFE bundle (a JWK Set) per the SPIFFE Federation bundle- endpoint model, instead of a SPIFFE Bundle Map. Scope federation as future work and state that a verifier validates SVIDs against its own Trust Domain. Signed-off-by: Matthias Lechner --- .../identity/identity-framework.md | 15 +++-- .../identity-security-considerations.md | 8 +-- system-design/specification/identity/svids.md | 2 +- .../identity/tls-requirements.md | 8 +-- .../identity/trust-bundle-and-discovery.md | 52 +++++++------- .../identity/trust-bundle-api-1.0.0.yaml | 67 +++++++++---------- 6 files changed, 74 insertions(+), 78 deletions(-) diff --git a/system-design/specification/identity/identity-framework.md b/system-design/specification/identity/identity-framework.md index 5b9c754c..719ba7d8 100644 --- a/system-design/specification/identity/identity-framework.md +++ b/system-design/specification/identity/identity-framework.md @@ -6,7 +6,7 @@ MIAF defines: - a **Trust Domain** model and the **SPIFFE ID** namespace for identifying Margo components; - an **X.509-SVID** profile (an X.509 certificate carrying a SPIFFE ID in its URI SAN) as the credential a component presents; -- the **SPIFFE Bundle Map** as the canonical format for distributing trust anchors, located through an optional [discovery document](trust-bundle-and-discovery.md); +- the **SPIFFE bundle** (a JWK Set per [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as the format for distributing trust anchors, located through an optional [discovery document](trust-bundle-and-discovery.md); - the **Margo Identity Service (MIS)** as the identity-authority role within a Trust Domain; and - a cryptographic and [TLS baseline](tls-requirements.md) shared by all Margo components, with authentication by mTLS using X.509-SVIDs validated against the Trust Bundle. @@ -18,10 +18,10 @@ Authentication is mTLS with an X.509-SVID. Authorization is performed locally by MIAF has four moving parts: -1. **Trust Domain**: the security boundary within which MIAF identities are issued and validated. Each SPIFFE ID belongs to exactly one Trust Domain; a verifier MAY also accept identities from other Trust Domains through configuration or federation. +1. **Trust Domain**: the security boundary within which MIAF identities are issued and validated. Each SPIFFE ID belongs to exactly one Trust Domain, and a verifier validates SVIDs against its own Trust Domain's Trust Bundle. 2. **Margo Identity Service (MIS)**: the identity-authority role. It issues SVIDs, publishes the discovery document and Trust Bundle, and enforces the cryptographic and SVID-profile rules. The role is not pinned to a specific service API. 3. **Margo components**: DFMs, WFMs, their clients, and infrastructure services. A component acts as an **SVID holder** when it authenticates and as a **verifier** when it validates a peer's SVID. -4. **Trust Bundles**: each Trust Domain publishes a Trust Bundle (a set of X.509 trust anchors) that verifiers validate SVIDs against. Bundles are distributed via the SPIFFE [Bundle Map](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md). +4. **Trust Bundles**: each Trust Domain publishes a Trust Bundle (a set of X.509 trust anchors) that verifiers validate SVIDs against. A Trust Bundle is distributed as a SPIFFE [bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md). Once a component holds an SVID: @@ -74,6 +74,8 @@ MIAF is a general foundation: any Margo component MAY adopt it, and future ident Other Margo components (the Device Fleet Manager, observability collectors, or component registries, for example) MAY hold MIAF identities, but no identity profile is defined for their interfaces yet, so how they authenticate is not governed here until such a profile exists. For an interface into an external ecosystem that carries its own established authentication convention (such as an OCI registry), a MIAF identity is expected to serve as the root credential a component uses to obtain an ecosystem-native credential, rather than as the wire-level authentication mechanism itself. +Each principal belongs to a single Trust Domain. A WFM and its WFM Clients share one Trust Domain, and that shared Trust Domain is the basis of their mutual recognition. An operator MAY run several independent Trust Domains, for example to separate environments or tiers of differing criticality; these Trust Domains do not trust one another. Trust across Trust Domains (federation) is not defined in this release and is expected to be addressed in a future revision. + ## Terminology The following terms form the common vocabulary for Margo's non-human identity and authorization model. Some are adopted directly from SPIFFE; others are Margo-specific. @@ -85,7 +87,7 @@ Terms adopted from SPIFFE, used here as SPIFFE defines them: - **Trust Domain**: the governed security boundary within which identities are issued and mutually recognized, a trust-root-backed identity namespace and policy boundary. A Trust Domain defines its authoritative trust anchors (the X.509 authority certificates published for the domain), the namespace for SPIFFE IDs, and the policies for identity lifecycle and authorization. - **SPIFFE ID**: a URI of the form `spiffe:///` that names an identity within a Trust Domain. MIAF adopts [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md) syntax and validation rules by reference and defines Margo path conventions where needed (see [Identity model](#identity-model)). - **SPIFFE Verifiable Identity Document (SVID)**: the verifiable credential representing an identity within a Trust Domain. An SVID binds a SPIFFE ID to a key pair. Its profile, cryptography, and validation are defined in [SVIDs](svids.md). -- **Trust Bundle**: the cryptographic material (X.509 trust anchors) used to validate SVIDs issued within a Trust Domain, distributed via the SPIFFE Bundle Map (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). +- **Trust Bundle**: the cryptographic material (X.509 trust anchors) used to validate SVIDs issued within a Trust Domain, distributed as a SPIFFE bundle (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). Terms introduced by MIAF: @@ -97,7 +99,7 @@ Terms introduced by MIAF: MIAF reuses SPIFFE identity primitives rather than inventing Margo-specific credential formats or trust semantics. This framework: -- adopts by reference the SPIFFE concepts of **Trust Domain**, **SPIFFE ID**, **X.509-SVID**, and **Trust Bundle / Bundle Map**; +- adopts by reference the SPIFFE concepts of **Trust Domain**, **SPIFFE ID**, **X.509-SVID**, and **Trust Bundle**; - profiles or constrains those standards where Margo needs additional rules; and - defines Margo-specific behavior for discovery and the MIS role, and constrains the SPIFFE ID path namespace to paths beginning with `/margo/`. @@ -107,7 +109,8 @@ MIAF references the current published text of each SPIFFE specification instead | :---- | :----- | :---- | | SPIFFE ID syntax and validation rules | [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md), adopted by reference | Margo defines only path conventions where needed. | | X.509-SVID baseline semantics | [SPIFFE X.509-SVID](https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md), adopted by reference and constrained | Margo adds the profile constraints in [SVIDs](svids.md#x509-svid-profile). | -| Trust Bundle / Bundle Map | [SPIFFE Trust Domain and Bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), adopted by reference | Margo defines discovery and retrieval conventions around it. | +| Trust Bundle | [SPIFFE Trust Domain and Bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), adopted by reference | Margo serves a single SPIFFE bundle per Trust Domain and defines discovery conventions around it. | +| Bundle endpoint | [SPIFFE Federation](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md), bundle-endpoint model adopted by reference | The Trust Bundle retrieval endpoint follows the bundle-endpoint model (one URL per Trust Domain). The `https_web` and `https_spiffe` endpoint profiles are candidates for future adoption; this release authenticates retrieval per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). | | Discovery document | Margo | Not part of SPIFFE; defined in [Trust Bundle and Discovery Endpoints](trust-bundle-and-discovery.md#discovery-document-endpoint). | ## Identity Model diff --git a/system-design/specification/identity/identity-security-considerations.md b/system-design/specification/identity/identity-security-considerations.md index d134e899..f3567117 100644 --- a/system-design/specification/identity/identity-security-considerations.md +++ b/system-design/specification/identity/identity-security-considerations.md @@ -12,12 +12,12 @@ This section is scoped to identity. For each threat it states the mitigation MIA | **Private key compromise** | An attacker exfiltrates a principal's private key and can then authenticate as that principal until the credential is withdrawn. | A principal protects its private key per deployment policy; MIAF specifies no key-protection mechanism of its own. The [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)'s preferred path issues by CSR, which keeps the key on the principal and can bind it to hardware (TPM, secure element, or HSM) where available. Once compromise is known, access is withdrawn via the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook), bounded by short [SVID lifetimes](identity-lifecycle.md#svid-lifetime-guidance). | | **Central key-custody concentration** | Where a principal cannot generate its own key pair, the operator generates it centrally (see [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)), concentrating many principals' private keys in one store whose breach exposes all of them at once. | The provisioning playbook's preferred path generates the key on the principal (issuance by CSR), leaving no central copy; where a principal cannot generate its own key pair, the operator generates it centrally and accepts the resulting key-custody concentration risk, protecting the store per deployment policy and treating its breach as private-key compromise (row above). | | **Compromised issuance authority (MIS)** | A compromised [MIS](identity-framework.md#the-mis-role) can mint a valid SVID for any SPIFFE ID in the Trust Domain, impersonating any WFM or client, or issue under a WFM namespace at will. | MIAF concentrates issuance in the MIS by design, so protecting it is an operator PKI-governance responsibility: scope the issuing authority (for example an intermediate CA per the [deployment patterns](identity-framework.md#deployment-patterns-informative)) and protect its signing key per deployment policy. Recovery is [Trust Bundle rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) to retire the compromised authority. | -| **Initial trust anchor confusion** | An attacker causes a principal to trust the wrong HTTPS authority and serves a malicious discovery document or Bundle Map. | A principal MUST authenticate the first HTTPS retrieval of the discovery document and `trustBundleUri` per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). | -| **Trust Bundle substitution or anchor injection** | An attacker who compromises a Trust Bundle origin (or its server certificate) injects a trust anchor. Because the SPIFFE Bundle Map is authenticated only by the transport and is not itself signed, every SVID chaining to the injected anchor is then accepted. This applies on every refresh, not only at bootstrap. | A client retrieves the Bundle Map only over HTTPS validated against its configured initial-trust anchors or pins, on each refresh, per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap) and [certificate validation](tls-requirements.md#certificate-validation), and MUST fail closed on an empty or unusable bundle per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | -| **Trust Bundle rollback** | A stale, cached, or replayed Bundle Map carrying an older `spiffe_sequence` is served to reverse a completed rotation, re-admitting a trust anchor that was removed to revoke a compromise. | A client SHOULD track the highest `spiffe_sequence` it has accepted for the local Trust Domain and reject a Bundle Map whose sequence has regressed, per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | +| **Initial trust anchor confusion** | An attacker causes a principal to trust the wrong HTTPS authority and serves a malicious discovery document or Trust Bundle. | A principal MUST authenticate the first HTTPS retrieval of the discovery document and `trustBundleUri` per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). | +| **Trust Bundle substitution or anchor injection** | An attacker who compromises a Trust Bundle origin (or its server certificate) injects a trust anchor. Because the SPIFFE bundle is authenticated only by the transport and is not itself signed, every SVID chaining to the injected anchor is then accepted. This applies on every refresh, not only at bootstrap. | A client retrieves the Trust Bundle only over HTTPS validated against its configured initial-trust anchors or pins, on each refresh, per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap) and [certificate validation](tls-requirements.md#certificate-validation), and MUST fail closed on an empty or unusable bundle per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | +| **Trust Bundle rollback** | A stale, cached, or replayed Trust Bundle carrying an older `spiffe_sequence` is served to reverse a completed rotation, re-admitting a trust anchor that was removed to revoke a compromise. | A client SHOULD track the highest `spiffe_sequence` it has accepted for the Trust Domain and reject a Trust Bundle whose sequence has regressed, per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | | **Certificate revocation lag** | A revocation event is not propagated promptly. | A deployment relies on the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook) and short SVID lifetimes; a verifier SHOULD bound connection lifetime so that a long-lived mTLS session does not extend the lag indefinitely (see [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation)). | | **Service impersonation / MITM** | An adversary attempts to impersonate the MIS or another service. | All endpoints MUST use HTTPS with strict certificate validation per the [TLS requirements](tls-requirements.md); a peer MUST verify a presented SVID against the configured Trust Domain and Trust Bundle. | -| **Cross-domain trust confusion** | A component accepts identities from an unintended Trust Domain. | A verifier MUST determine the Trust Domain from the SPIFFE ID and MUST NOT trust an SVID unless the domain is explicitly configured or federated. | +| **Cross-domain trust confusion** | A component accepts identities from an unintended Trust Domain. | A verifier MUST determine the Trust Domain from the SPIFFE ID and MUST NOT trust an SVID whose Trust Domain is not the verifier's own. | | **Inadvertent inspection-proxy MITM** | An operator-deployed traffic-inspecting proxy intercepts and re-signs Margo mTLS traffic, presenting a substitute certificate to either endpoint. | An operator MUST exempt Margo mTLS endpoints from inspection per [traffic-inspecting proxies](tls-requirements.md#traffic-inspecting-proxies). Both endpoints MUST validate peer SVIDs against the Trust Bundle and reject a substituted certificate. | | **Forwarded-identity header injection** | In a TLS-offload topology, an attacker injects a forged `Client-Cert` header to impersonate an authenticated caller at the backend. | The proxy MUST remove or overwrite any `Client-Cert` or `Client-Cert-Chain` header on incoming requests, and the backend MUST accept a forwarded identity only over the trusted proxy boundary, per [traffic-inspecting proxies](tls-requirements.md#traffic-inspecting-proxies). | | **Unreliable or manipulated validator clock** | A principal mis-evaluates certificate validity periods because its clock is skewed or unset (a dead RTC battery, no time source at first boot) or because an attacker rewinds it, for example by spoofing an unauthenticated time source on the adversarial network. Rewinding the clock extends the validity window of an expired SVID, defeating the use of expiry as revocation. | SVID validation depends on a trustworthy local time source. A principal SHOULD synchronize time from a trustworthy source before validating certificates, and operators SHOULD account for devices with weak clocks when choosing SVID lifetimes (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). A deployment that cannot assure clock integrity SHOULD NOT rely on expiry alone for revocation. | diff --git a/system-design/specification/identity/svids.md b/system-design/specification/identity/svids.md index b74ac409..3a66a759 100644 --- a/system-design/specification/identity/svids.md +++ b/system-design/specification/identity/svids.md @@ -34,7 +34,7 @@ These requirements apply to MIAF-generated artifacts and to the keys used in SVI A verifier authenticates a peer by validating the presented X.509-SVID against the peer's Trust Domain and, on success, treating the SPIFFE ID it carries as the peer's identity. A verifier MUST, in order: -- read the SPIFFE ID from the leaf certificate's **URI SAN** to determine the peer's Trust Domain, and reject the SVID unless that Trust Domain is explicitly configured or federated. DNS hostname matching does not apply to SVID identity and MUST NOT override the SPIFFE ID. +- read the SPIFFE ID from the leaf certificate's **URI SAN** to determine the peer's Trust Domain, and reject the SVID unless that Trust Domain is the verifier's own. DNS hostname matching does not apply to SVID identity and MUST NOT override the SPIFFE ID. - validate the presented chain against that Trust Domain's [Trust Bundle](trust-bundle-and-discovery.md), accepting an SVID that chains to any anchor in the current bundle. A bundle MAY contain more than one anchor (for example, during a [trust anchor rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) overlap), and every anchor in it is equally authoritative. Reject any certificate outside its validity period. A verifier MUST NOT rely on AIA fetching or other out-of-band intermediate retrieval; the presenter supplies the intermediates the chain needs. - enforce the SPIFFE X.509-SVID leaf constraints and reject any SVID that violates them: basic-constraints `cA` MUST be `false`; `keyCertSign` and `cRLSign` MUST NOT be set in key usage; the SPIFFE ID MUST use the `spiffe` scheme with a non-root path; and the certificate MUST carry exactly one URI SAN. diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md index 8f2c21f0..b35f37fe 100644 --- a/system-design/specification/identity/tls-requirements.md +++ b/system-design/specification/identity/tls-requirements.md @@ -16,18 +16,18 @@ The TLS 1.2 fallback carries a confidentiality cost specific to MIAF. A MIAF cli ## Initial Trust Bootstrap -A client cannot validate MIAF-issued SVIDs against the Trust Bundle until it holds that bundle, and it must acquire the first bundle before it has any MIAF credential of its own to authenticate the exchange. A client acquires the trust material (the discovery document, if used, and the Bundle Map) by one of two paths. +A client cannot validate MIAF-issued SVIDs against the Trust Bundle until it holds that bundle, and it must acquire the first bundle before it has any MIAF credential of its own to authenticate the exchange. A client acquires the trust material (the discovery document, if used, and the Trust Bundle) by one of two paths. -**Authenticated HTTPS retrieval.** The client fetches the discovery document and the Bundle Map over HTTPS (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). Because the client holds no MIAF credential yet, these connections rely on an initial trust mechanism established outside MIAF. The client MUST authenticate both connections using at least one of: +**Authenticated HTTPS retrieval.** The client fetches the discovery document and the Trust Bundle over HTTPS (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). Because the client holds no MIAF credential yet, these connections rely on an initial trust mechanism established outside MIAF. The client MUST authenticate both connections using at least one of: 1. **PKI-anchored validation**: validate the [MIS](identity-framework.md#the-mis-role) server certificate chain to a configured set of trust anchors (web PKI, enterprise PKI, or an operator-configured private CA), with DNS name validation per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). 2. **Pinned trust**: validate the MIS server certificate chain or public key against operator-provisioned pins (for example, a pinned CA certificate). An operator MAY deliver the trust material for either option (the configured anchors for the first, or the pins for the second) through the same channel used to provision the principal's SVID. A client that cannot authenticate a connection by one of these mechanisms MUST abort. -**Out-of-band delivery.** The operator delivers the Bundle Map, and the Trust Domain identifier and Trust Bundle URI, directly through the provisioning or deployment channel (see [Bundle Map contents and distribution](trust-bundle-and-discovery.md#bundle-map-contents-and-distribution) and the [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)). No HTTPS retrieval takes place, so there is no bootstrap connection to authenticate; the integrity and authenticity of the delivered material rest on that channel. +**Out-of-band delivery.** The operator delivers the Trust Bundle, and the Trust Domain identifier and Trust Bundle URI, directly through the provisioning or deployment channel (see [Bundle contents and distribution](trust-bundle-and-discovery.md#bundle-contents-and-distribution) and the [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)). No HTTPS retrieval takes place, so there is no bootstrap connection to authenticate; the integrity and authenticity of the delivered material rest on that channel. -Whichever path is used, a client MUST NOT accept trust material from an unauthenticated source, and MUST NOT treat the first acquisition as "trust on first use". Once acquired, the discovery document (if used) and the Bundle Map are MIAF's authoritative sources; the bundle they select then validates SVIDs within the Trust Domain. +Whichever path is used, a client MUST NOT accept trust material from an unauthenticated source, and MUST NOT treat the first acquisition as "trust on first use". Once acquired, the discovery document (if used) and the Trust Bundle are MIAF's authoritative sources; that bundle then validates SVIDs within the Trust Domain. ## Certificate Validation diff --git a/system-design/specification/identity/trust-bundle-and-discovery.md b/system-design/specification/identity/trust-bundle-and-discovery.md index 8d25deae..a5d064e7 100644 --- a/system-design/specification/identity/trust-bundle-and-discovery.md +++ b/system-design/specification/identity/trust-bundle-and-discovery.md @@ -42,7 +42,7 @@ The path above is the default convention; an origin serving several Trust Domain | Field | Type | Required? | Description | | :---- | :--- | :-------- | :---------- | | `trustDomain` | string | Y | Identifier of the Trust Domain (for example, `factory.example`). Every SPIFFE ID issued by the MIS MUST belong to this Trust Domain. | -| `trustBundleUri` | string | Y | Absolute HTTPS URL to the **SPIFFE Bundle Map** resource for this Trust Domain. The resource MUST conform to the [SPIFFE Bundle Map specification](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#5-spiffe-bundle-map) and MUST contain an entry for the domain named by `trustDomain`; that entry is the authoritative local Trust Bundle. The resource SHOULD expose an `ETag` for cache revalidation. | +| `trustBundleUri` | string | Y | Absolute HTTPS URL to the **SPIFFE bundle** for this Trust Domain, conforming to the [SPIFFE bundle format](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#4-spiffe-bundle-format) and holding the domain's authoritative trust anchors. The resource SHOULD expose an `ETag` for cache revalidation. | A client MUST ignore unknown fields in the discovery document. @@ -66,7 +66,7 @@ Response (`200 OK`): ## Trust Bundle Retrieval Endpoint -The resource identified by `trustBundleUri` returns a SPIFFE Bundle Map. The entry keyed by the local `trustDomain` holds the authoritative set of public trust anchors for that Trust Domain. +The resource identified by `trustBundleUri` returns the Trust Domain's SPIFFE bundle, which holds the authoritative set of public trust anchors for that Trust Domain. The endpoint follows the SPIFFE [bundle endpoint](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md) model, where one URL serves one Trust Domain's bundle. The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). A client cannot yet validate MIAF-issued SVIDs when it first retrieves trust material, so this connection relies on an initial trust mechanism established outside MIAF, not on a MIAF SVID. When `trustBundleUri` names a different origin than the discovery document, the client's initial-trust material (configured PKI anchors or operator-provisioned pins) MUST cover that origin. @@ -82,56 +82,50 @@ GET | Header | Description | | ------ | ----------- | -| `Accept` *(optional)* | The client SHOULD request the Bundle Map in `application/json`. | -| `If-None-Match` *(optional)* | The `ETag` from the last retrieved Bundle Map, used to revalidate a cached copy. | +| `Accept` *(optional)* | The client SHOULD request the bundle in `application/json`. | +| `If-None-Match` *(optional)* | The `ETag` from the last retrieved bundle, used to revalidate a cached copy. | ### Response Codes | Code | Description | | ---- | ----------- | -| 200 OK | The response body is a SPIFFE Bundle Map conforming to the [SPIFFE Bundle Map format](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#5-spiffe-bundle-map). The server SHOULD include an `ETag` for cache revalidation. | +| 200 OK | The response body is a SPIFFE bundle conforming to the [SPIFFE bundle format](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#4-spiffe-bundle-format). The server SHOULD include an `ETag` for cache revalidation. | | 304 Not Modified | The cached copy is still valid; returned when the `If-None-Match` `ETag` matches. The response body is empty. | | 404 Not Found | The bundle is unavailable. | -### Example Bundle Map Response +### Example Bundle Response -The Bundle Map is keyed by Trust Domain under `trust_domains`; the entry for the local `trustDomain` carries that domain's X.509 trust anchors as JWK entries with `"use": "x509-svid"`. Each authority's certificate travels in `x5c` (base64-encoded DER). During a [trust anchor rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) overlap the `keys` array carries more than one `x509-svid` entry. +The bundle carries the Trust Domain's X.509 trust anchors as JWK entries with `"use": "x509-svid"`. Each authority's certificate travels in `x5c` (base64-encoded DER). During a [trust anchor rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) overlap the `keys` array carries more than one `x509-svid` entry. Response (`200 OK`): ```json { - "trust_domains": { - "factory.example": { - "spiffe_sequence": 12, - "spiffe_refresh_hint": 86400, - "keys": [ - { - "kty": "EC", - "crv": "P-256", - "x": "", - "y": "", - "use": "x509-svid", - "x5c": [""] - } - ] + "spiffe_sequence": 12, + "spiffe_refresh_hint": 86400, + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "x": "", + "y": "", + "use": "x509-svid", + "x5c": [""] } - } + ] } ``` ### Selecting and Refreshing the Bundle -A client selects the Trust Bundle for `trustDomain` from the retrieved Bundle Map and uses it as the authoritative source when validating SVIDs issued within the Trust Domain. A client that finds no entry for its `trustDomain`, or finds an entry that carries no X.509 trust anchors, MUST reject the Bundle Map and MUST NOT validate SVIDs against it, failing closed rather than proceeding with an empty anchor set. To resist rollback, where the Bundle Map carries `spiffe_sequence` a client SHOULD track the highest value it has accepted for the local Trust Domain and SHOULD reject a retrieved Bundle Map whose `spiffe_sequence` has regressed, since a lower value signals a stale, cached, or replayed bundle that could re-admit a trust anchor that was retired to revoke a compromise. +A client uses the retrieved bundle as the authoritative source when validating SVIDs issued within the Trust Domain. A client that retrieves a bundle carrying no X.509 trust anchors MUST reject it and MUST NOT validate SVIDs against it, failing closed rather than proceeding with an empty anchor set. To resist rollback, where the bundle carries `spiffe_sequence` a client SHOULD track the highest value it has accepted for the Trust Domain and SHOULD reject a retrieved bundle whose `spiffe_sequence` has regressed, since a lower value signals a stale, cached, or replayed bundle that could re-admit a trust anchor that was retired to revoke a compromise. A client SHOULD refresh its cached bundle at the interval given by the bundle's `spiffe_refresh_hint`, when present, and otherwise at an operator-configured interval. This refresh cadence is authoritative: HTTP cache revalidation (`If-None-Match`/`304`, and any `Cache-Control` freshness) is an efficiency optimization within it and MUST NOT defer a refresh the interval requires. The refresh interval bounds how quickly a Trust Bundle rotation reaches the fleet; the [trust anchor rotation playbook](identity-lifecycle.md#trust-anchor-rotation-playbook) depends on it. -## Bundle Map Contents and Distribution +## Bundle Contents and Distribution -A Trust Bundle is distributed via the SPIFFE [Trust Domain and Bundle Map](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), MAY additionally be delivered through deployment tooling or provisioning flows, and SHOULD be cached locally by a client to support offline validation. +A Trust Bundle is distributed as a SPIFFE [bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), MAY additionally be delivered through deployment tooling or provisioning flows, and SHOULD be cached locally by a client to support offline validation. -A Trust Domain's bundle entry contains that domain's X.509 trust anchors only; intermediate CA certificates travel with the presented SVID chain, not in the bundle (see [chain delivery](svids.md#x509-svid-profile)). +The bundle contains the Trust Domain's X.509 trust anchors only; intermediate CA certificates travel with the presented SVID chain, not in the bundle (see [chain delivery](svids.md#x509-svid-profile)). -The SPIFFE Bundle Map format reserves a slot for JWKS material. MIAF does not populate it, and an implementation MUST ignore any JWKS material found on retrieval. - -A Bundle Map can carry the bundles of several Trust Domains, but from a given `trustBundleUri` only the entry keyed by that resource's local `trustDomain` is authoritative: a client MUST use that entry, and only that entry, as the trust anchors for the local Trust Domain. A client MUST NOT treat an entry for some other Trust Domain that happens to appear in the same map as authoritative for that domain; the trust anchors for another Trust Domain MUST be obtained from that domain's own authoritative source (its `trustBundleUri` or operator configuration), so that the operator of one domain's MIS cannot vouch for another domain. Where an operator configures cross-domain trust, packing the additional domain's bundle into one map MAY serve as a delivery convenience, but each domain's anchors remain bound to that domain's authoritative source. Full [SPIFFE Federation](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md) lifecycle semantics are out of scope. +A SPIFFE bundle is a JWK Set that MAY also carry JWT-SVID signing keys (`"use": "jwt-svid"`). MIAF uses only `x509-svid` entries; an implementation MUST ignore any `jwt-svid` or other non-`x509-svid` key material found in the bundle. diff --git a/system-design/specification/identity/trust-bundle-api-1.0.0.yaml b/system-design/specification/identity/trust-bundle-api-1.0.0.yaml index 01f86204..f32cf131 100644 --- a/system-design/specification/identity/trust-bundle-api-1.0.0.yaml +++ b/system-design/specification/identity/trust-bundle-api-1.0.0.yaml @@ -5,7 +5,7 @@ info: description: > Read-only HTTPS endpoints served by the Margo Identity Service (MIS) role: an optional discovery document that points a client to the Trust Bundle, - and the SPIFFE Bundle Map retrieval endpoint. + and the SPIFFE bundle retrieval endpoint. Both endpoints are served over HTTPS. Because a client cannot yet validate @@ -62,15 +62,16 @@ paths: /.well-known/spiffe/bundle.json: get: - summary: Retrieve the SPIFFE Bundle Map + summary: Retrieve the SPIFFE bundle description: > - Returns the SPIFFE Bundle Map for the Trust Domain. The path shown here - is illustrative; the authoritative location is the trustBundleUri value - from the discovery document (or operator configuration). The entry keyed - by the local trustDomain holds the authoritative set of public trust - anchors for that Trust Domain. The response MUST conform to the SPIFFE - Bundle Map format - (https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#5-spiffe-bundle-map). + Returns the Trust Domain's SPIFFE bundle, holding the authoritative + set of public trust anchors for that Trust Domain. The + path shown here is illustrative; the authoritative location is the + trustBundleUri value from the discovery document (or operator + configuration). The endpoint follows the SPIFFE Federation bundle + endpoint model (one URL per Trust Domain). The response MUST conform to + the SPIFFE bundle format + (https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md#4-spiffe-bundle-format). parameters: - name: If-None-Match in: header @@ -80,7 +81,7 @@ paths: description: Previously returned ETag; used to revalidate a cached copy. responses: '200': - description: SPIFFE Bundle Map + description: SPIFFE bundle headers: ETag: schema: @@ -89,7 +90,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SpiffeBundleMap' + $ref: '#/components/schemas/SpiffeBundle' '304': description: Cached copy still valid '404': @@ -100,6 +101,7 @@ components: DiscoveryDocument: type: object required: [trustDomain, trustBundleUri] + additionalProperties: true description: > Describes exactly one Trust Domain. Clients MUST ignore unknown fields. properties: @@ -114,32 +116,29 @@ components: type: string format: uri description: > - Absolute HTTPS URL to the SPIFFE Bundle Map resource for this Trust - Domain. A client MUST reject a value whose scheme is not https. The - resource MUST contain an entry for the domain named by trustDomain; - that entry is the authoritative local Trust Bundle. + Absolute HTTPS URL to the SPIFFE bundle for this Trust Domain. A + client MUST reject a value whose scheme is not https. The + bundle holds the authoritative trust anchors for the Trust Domain. examples: - https://mis.factory.example/.well-known/spiffe/bundle.json - SpiffeBundleMap: + SpiffeBundle: type: object description: > - SPIFFE Bundle Map. The concrete schema is defined by the SPIFFE Trust - Domain and Bundle Map specification; the map is keyed by Trust Domain - identifier. A client MUST reject the map, and MUST NOT validate SVIDs - against it, if it contains no entry for the local trustDomain or that - entry carries no X.509 trust anchors (fail closed). A JWKS slot MAY be - present but is not populated by MIAF and MUST be ignored on retrieval. + SPIFFE bundle for one Trust Domain. The concrete schema is defined by + the SPIFFE Trust Domain and Bundle specification (a JWK Set). A client + MUST reject the bundle, and MUST NOT validate SVIDs against it, if it + carries no X.509 trust anchors (fail closed). The JWK Set MAY also carry + jwt-svid signing keys; MIAF uses only x509-svid entries and MUST ignore + any other key material on retrieval. additionalProperties: true examples: - - trust_domains: - factory.example: - spiffe_sequence: 12 - spiffe_refresh_hint: 86400 - keys: - - kty: EC - crv: P-256 - x: - y: - use: x509-svid - x5c: - - + - spiffe_sequence: 12 + spiffe_refresh_hint: 86400 + keys: + - kty: EC + crv: P-256 + x: + y: + use: x509-svid + x5c: + - From 390e153ec059c6a23fa4d8f9669ceac55d7f632c Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 13:13:10 +0000 Subject: [PATCH 04/26] docs: assign SVID algorithm selection to the operator Signed-off-by: Matthias Lechner --- system-design/specification/identity/svids.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system-design/specification/identity/svids.md b/system-design/specification/identity/svids.md index 3a66a759..104c73b5 100644 --- a/system-design/specification/identity/svids.md +++ b/system-design/specification/identity/svids.md @@ -20,8 +20,8 @@ This section constrains the signature algorithms and key parameters used for SVI | **EdDSA (Ed25519)** | Keys MUST use the Ed25519 curve; signatures follow [RFC 8032](https://datatracker.ietf.org/doc/html/rfc8032) (`EdDSA`). | | **RSA (≥3072 + SHA-256)** | Modulus MUST be at least 3072 bits; signatures MUST use SHA-256. RSASSA-PSS (`PS256`, [RFC 8017](https://datatracker.ietf.org/doc/html/rfc8017)) is RECOMMENDED. RSASSA-PKCS#1 v1.5 (`RS256`) MAY be used only for X.509 certificate and CSR signatures (today the only RSA signatures in MIAF), for compatibility with issuing CAs that cannot produce PSS signatures (its use there remains sanctioned by [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf)). Any other RSA signature MIAF defines later MUST use PSS. | -- **ECDSA P-256 with SHA-256 is mandatory to implement.** Every MIAF component MUST implement it, for both presenting and validating SVIDs, so that any two components share at least one algorithm. -- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. A component that enables a non-mandatory algorithm for the SVID it presents is responsible for confirming that its peers accept it, since only ECDSA P-256 is guaranteed everywhere. +- **ECDSA P-256 with SHA-256 is mandatory to implement.** Every MIAF component MUST implement it, for both presenting and validating SVIDs, so that any two components always share at least one algorithm. +- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. Because a principal presents a single SVID bound to one signature algorithm, an SVID issued with a non-mandatory algorithm can be validated only by peers that implement that algorithm; a peer that does not will fail to validate it. Selecting an SVID signature algorithm that is supported across the Trust Domain is therefore the operator's responsibility at issuance (see [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)). ECDSA P-256 with SHA-256 is always a safe choice. - A component that validates SVIDs MUST validate every algorithm it accepts from peers. For RSA, this includes accepting both PSS and PKCS#1 v1.5 signatures on certificates. > **Crypto-agility (informative):** MIAF names its algorithms explicitly so the permitted set can evolve. The set above is classical; post-quantum signature suites are expected to arrive as additional permitted algorithms rather than a redesign of the framework. Defaulting the transport to TLS 1.3 (see [TLS requirements](tls-requirements.md)) supports this: it is the version track on which post-quantum key exchange and authentication are being standardized. From aafc2c60a321c5dba79275d3147077b8b449dfbc Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 13:30:23 +0000 Subject: [PATCH 05/26] docs: consolidate WFM identity terminology and model sections Signed-off-by: Matthias Lechner --- .../identity/wfm-identity-profile.md | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/system-design/specification/identity/wfm-identity-profile.md b/system-design/specification/identity/wfm-identity-profile.md index 6d62bfb9..e4c952f5 100644 --- a/system-design/specification/identity/wfm-identity-profile.md +++ b/system-design/specification/identity/wfm-identity-profile.md @@ -8,20 +8,9 @@ All MIAF terminology is reused by reference unless specialized here. ## Identity Terminology -**WFM Identity (`wfm-id`)** is the identity of a WFM within its Trust Domain, expressed as a SPIFFE URI of the form `spiffe:///margo/wfm/` and represented by an X.509-SVID. It anchors the namespace under which WFM Client identities are issued. The `wfm-id` segment: +**WFM Identity (`wfm-id`)** is the identity of a WFM within its Trust Domain, expressed as a SPIFFE URI of the form `spiffe:///margo/wfm/` and represented by an X.509-SVID. It anchors the namespace under which WFM Client identities are issued. -- MUST be unique within the Trust Domain; -- MUST consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters; -- SHOULD be assigned by the operator deploying the WFM into the Trust Domain, not unilaterally by the WFM vendor, so that operators can prevent namespace collisions in multi-vendor deployments; and -- MUST be stable for the life of the WFM identity it names. Rebinding a `wfm-id` to a different WFM identity is not defined by this profile and MUST NOT be performed silently; replacement requires a new `wfm-id`. - -An operator MAY assign one shared `wfm-id` across several WFM instances to present a single logical identity, or distinct `wfm-id`s for each instance for finer-grained lifecycle management. - -**WFM Client Identity (`wfm-client-id`)** is the stable, verifiable identity of a WFM Client relationship within a Trust Domain, expressed as a SPIFFE URI of the form `spiffe:///margo/wfm//client/` and represented by an X.509-SVID. The `wfm-client-id` segment: - -- MUST be stable for the lifetime of the relationship; -- MUST be unique within the issuing WFM's namespace; and -- MUST consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters. +**WFM Client Identity (`wfm-client-id`)** is the stable, verifiable identity of a WFM Client relationship within a Trust Domain, expressed as a SPIFFE URI of the form `spiffe:///margo/wfm//client/` and represented by an X.509-SVID. ## Identity Model @@ -33,6 +22,15 @@ A WFM identity is a SPIFFE ID of the form: spiffe:///margo/wfm/ ``` +The `wfm-id` segment: + +- MUST be non-empty, MUST consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters, and MUST NOT be `.` or `..`; +- MUST be unique within the Trust Domain; +- MUST be stable for the life of the WFM identity it names. Rebinding a `wfm-id` to a different WFM identity is not defined by this profile and MUST NOT be performed silently; replacement requires a new `wfm-id`; and +- SHOULD be assigned by the operator deploying the WFM into the Trust Domain, not unilaterally by the WFM vendor, so that operators can prevent namespace collisions in multi-vendor deployments. + +An operator MAY assign one shared `wfm-id` across several WFM instances to present a single logical identity, or distinct `wfm-id`s for each instance for finer-grained lifecycle management. + A WFM participating in this profile: - MUST hold a valid WFM X.509-SVID; and @@ -48,12 +46,13 @@ A WFM Client identity is a SPIFFE ID of the form: spiffe:///margo/wfm//client/ ``` -The `wfm-id` and `wfm-client-id` segments: +The `wfm-client-id` segment: -- MUST each be non-empty, consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters, and MUST NOT be `.` or `..`; and -- carry no meaning beyond naming the WFM and the client relationship: apart from the recognition checks defined in this profile, a WFM Client MUST NOT infer structure or attributes from their content. +- MUST be non-empty, MUST consist only of [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) unreserved characters, and MUST NOT be `.` or `..`; +- MUST be unique within the issuing WFM's namespace; and +- MUST be stable for the lifetime of the relationship. -All comparisons of these segments are exact and case-sensitive, following SPIFFE path semantics. This SPIFFE ID is the canonical WFM Client identity within the Trust Domain. +The `wfm-id` and `wfm-client-id` segments carry no meaning beyond naming the WFM and the client relationship: apart from the recognition checks defined in this profile, a WFM Client MUST NOT infer structure or attributes from their content. All comparisons of these segments are exact and case-sensitive, following SPIFFE path semantics. This SPIFFE ID is the canonical WFM Client identity within the Trust Domain. ### Identity Representation From ce1dc58ef97f396323a736c995ba4f0fba7c5b4d Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 13:41:24 +0000 Subject: [PATCH 06/26] docs: lead the framework with terminology, de-duplicate the overview Signed-off-by: Matthias Lechner --- .../identity/identity-framework.md | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/system-design/specification/identity/identity-framework.md b/system-design/specification/identity/identity-framework.md index 719ba7d8..dd388171 100644 --- a/system-design/specification/identity/identity-framework.md +++ b/system-design/specification/identity/identity-framework.md @@ -14,14 +14,28 @@ The framework is generic: it does not define an enrollment protocol or a specifi Authentication is mTLS with an X.509-SVID. Authorization is performed locally by each verifier, based on the peer's verified SPIFFE ID. There is no central authorization server. -## Framework Overview +## Terminology + +The following terms form the common vocabulary for Margo's non-human identity and authorization model. Some are adopted directly from SPIFFE; others are Margo-specific. + +These identities belong to *non-human* **Margo components**: the logical units of the Margo system such as the Device Fleet Manager (DFM), Workload Fleet Manager (WFM), their clients, and infrastructure services such as registries or observability collectors. Which of their interfaces MIAF governs is defined in [Scope and Applicability](#scope-and-applicability). + +Terms adopted from SPIFFE, used here as SPIFFE defines them: + +- **Trust Domain**: the governed security boundary within which identities are issued and mutually recognized, a trust-root-backed identity namespace and policy boundary. A Trust Domain defines its authoritative trust anchors (the X.509 authority certificates published for the domain), the namespace for SPIFFE IDs, and the policies for identity lifecycle and authorization. +- **SPIFFE ID**: a URI of the form `spiffe:///` that names an identity within a Trust Domain. MIAF adopts [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md) syntax and validation rules by reference and defines Margo path conventions where needed (see [Identity model](#identity-model)). +- **SPIFFE Verifiable Identity Document (SVID)**: the verifiable credential representing an identity within a Trust Domain. An SVID binds a SPIFFE ID to a key pair. Its profile, cryptography, and validation are defined in [SVIDs](svids.md). +- **Trust Bundle**: the cryptographic material (X.509 trust anchors) used to validate SVIDs issued within a Trust Domain, distributed as a SPIFFE bundle (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). + +Terms introduced by MIAF: -MIAF has four moving parts: +- **Principal**: a non-human Margo component that holds, or is being provisioned with, a SPIFFE identity in a Trust Domain. Edge Compute Devices, WFMs, and WFM Clients are all principals. +- **Margo Identity Service (MIS)**: the identity-authority **role** within a Trust Domain. The MIS issues SVIDs, publishes the discovery document and Trust Bundle, and enforces MIAF's cryptographic and SVID-profile rules. The MIS is defined by its responsibilities, not by a specific API (see [The MIS role](#the-mis-role)). +- **Policy-based authorization**: each verifier makes authorization decisions locally, based on the peer's verified SPIFFE ID. MIAF does not use OAuth-style token scopes or a central authorization server. + +## Framework Overview -1. **Trust Domain**: the security boundary within which MIAF identities are issued and validated. Each SPIFFE ID belongs to exactly one Trust Domain, and a verifier validates SVIDs against its own Trust Domain's Trust Bundle. -2. **Margo Identity Service (MIS)**: the identity-authority role. It issues SVIDs, publishes the discovery document and Trust Bundle, and enforces the cryptographic and SVID-profile rules. The role is not pinned to a specific service API. -3. **Margo components**: DFMs, WFMs, their clients, and infrastructure services. A component acts as an **SVID holder** when it authenticates and as a **verifier** when it validates a peer's SVID. -4. **Trust Bundles**: each Trust Domain publishes a Trust Bundle (a set of X.509 trust anchors) that verifiers validate SVIDs against. A Trust Bundle is distributed as a SPIFFE [bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md). +MIAF has four moving parts: the **Trust Domain**, the **Margo Identity Service (MIS)**, the **Margo components** that hold and verify identities, and the **Trust Bundles** each Trust Domain publishes. Each SPIFFE ID belongs to exactly one Trust Domain, and a verifier validates SVIDs against its own Trust Domain's Trust Bundle. A component acts as an **SVID holder** when it authenticates and as a **verifier** when it validates a peer's SVID. Once a component holds an SVID: @@ -76,25 +90,6 @@ Other Margo components (the Device Fleet Manager, observability collectors, or c Each principal belongs to a single Trust Domain. A WFM and its WFM Clients share one Trust Domain, and that shared Trust Domain is the basis of their mutual recognition. An operator MAY run several independent Trust Domains, for example to separate environments or tiers of differing criticality; these Trust Domains do not trust one another. Trust across Trust Domains (federation) is not defined in this release and is expected to be addressed in a future revision. -## Terminology - -The following terms form the common vocabulary for Margo's non-human identity and authorization model. Some are adopted directly from SPIFFE; others are Margo-specific. - -These identities belong to *non-human* **Margo components**: the logical units of the Margo system such as the Device Fleet Manager (DFM), Workload Fleet Manager (WFM), their clients, and infrastructure services such as registries or observability collectors. Which of their interfaces MIAF governs is defined in [Scope and Applicability](#scope-and-applicability). - -Terms adopted from SPIFFE, used here as SPIFFE defines them: - -- **Trust Domain**: the governed security boundary within which identities are issued and mutually recognized, a trust-root-backed identity namespace and policy boundary. A Trust Domain defines its authoritative trust anchors (the X.509 authority certificates published for the domain), the namespace for SPIFFE IDs, and the policies for identity lifecycle and authorization. -- **SPIFFE ID**: a URI of the form `spiffe:///` that names an identity within a Trust Domain. MIAF adopts [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md) syntax and validation rules by reference and defines Margo path conventions where needed (see [Identity model](#identity-model)). -- **SPIFFE Verifiable Identity Document (SVID)**: the verifiable credential representing an identity within a Trust Domain. An SVID binds a SPIFFE ID to a key pair. Its profile, cryptography, and validation are defined in [SVIDs](svids.md). -- **Trust Bundle**: the cryptographic material (X.509 trust anchors) used to validate SVIDs issued within a Trust Domain, distributed as a SPIFFE bundle (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). - -Terms introduced by MIAF: - -- **Principal**: a non-human Margo component that holds, or is being provisioned with, a SPIFFE identity in a Trust Domain. Edge Compute Devices, WFMs, and WFM Clients are all principals. -- **Margo Identity Service (MIS)**: the identity-authority **role** within a Trust Domain. The MIS issues SVIDs, publishes the discovery document and Trust Bundle, and enforces MIAF's cryptographic and SVID-profile rules. The MIS is defined by its responsibilities, not by a specific API (see [The MIS role](#the-mis-role)). -- **Policy-based authorization**: each verifier makes authorization decisions locally, based on the peer's verified SPIFFE ID. MIAF does not use OAuth-style token scopes or a central authorization server. - ## Relationship to SPIFFE MIAF reuses SPIFFE identity primitives rather than inventing Margo-specific credential formats or trust semantics. This framework: From 100c0189c9b9150f42a2129b9b357c222021dcb5 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:06:05 +0200 Subject: [PATCH 07/26] Update system-design/specification/margo-management-interface/api-requirements-and-security.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- .../margo-management-interface/api-requirements-and-security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/margo-management-interface/api-requirements-and-security.md b/system-design/specification/margo-management-interface/api-requirements-and-security.md index 9f3e3a01..d6c8928b 100644 --- a/system-design/specification/margo-management-interface/api-requirements-and-security.md +++ b/system-design/specification/margo-management-interface/api-requirements-and-security.md @@ -22,7 +22,7 @@ The REST API is defined via the OpenAPI Specification: ## Transport The REST API MUST operate over HTTP/1.1; HTTP/1.1 is used to ensure maximum support for existing infrastructure within our install base. The transport is secured by mTLS as specified in [Identity and Authentication](#identity-and-authentication). -To minimize the ports required on the customer's infrastructure for cloud to edge communication, the API MUST use only port 443 for its traffic. +To minimize the ports required on the customer's infrastructure for cloud-to-edge communication, the API MUST use port 443 for its traffic. ## Identity and Authentication Authentication is mutual TLS per the MIAF [TLS requirements](../identity/tls-requirements.md). Both sides present an X.509-SVID and validate the peer's SVID and `wfm-id` per the WFM Identity Profile ([Recognition by the WFM](../identity/wfm-identity-profile.md#recognition-by-the-wfm), [Recognition by the WFM Client](../identity/wfm-identity-profile.md#recognition-by-the-wfm-client)). A WFM MUST reject any Management Interface request that is not authenticated by mTLS with a valid WFM Client X.509-SVID. From 3958aae408b9d190d8b54992b5f8b894bf1fed73 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:06:44 +0200 Subject: [PATCH 08/26] Update system-design/specification/identity/identity-security-considerations.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- .../specification/identity/identity-security-considerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/identity-security-considerations.md b/system-design/specification/identity/identity-security-considerations.md index f3567117..b851a3ce 100644 --- a/system-design/specification/identity/identity-security-considerations.md +++ b/system-design/specification/identity/identity-security-considerations.md @@ -1,6 +1,6 @@ # Identity Security Considerations -MIAF assumes an adversarial network and the possibility of a compromised individual device or service. Its goals are to preserve identity integrity and minimize blast radius; protecting the private keys those identities rest on is assumed of the deployment rather than provided by the framework. +MIAF assumes an adversarial network and the possibility of a compromised individual device or service. Its goals are to preserve identity integrity and minimize blast radius; protecting the private keys those identities rest on is the responsibility of the deployment rather than provided by the framework. This section is scoped to identity. For each threat it states the mitigation MIAF relies on, with the concrete rules in the linked sections. Some mitigations are normative framework requirements; others are residual risks carried by the operator's deployment. From 662903c49353fa567f01292cd108b27e36dc13ab Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:06:59 +0200 Subject: [PATCH 09/26] Update system-design/specification/identity/wfm-identity-profile.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- system-design/specification/identity/wfm-identity-profile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/wfm-identity-profile.md b/system-design/specification/identity/wfm-identity-profile.md index e4c952f5..50d594f1 100644 --- a/system-design/specification/identity/wfm-identity-profile.md +++ b/system-design/specification/identity/wfm-identity-profile.md @@ -79,7 +79,7 @@ A WFM Client MUST recognize the WFM it connects to from the authenticated SPIFFE 3. verify that the SPIFFE ID is exactly `spiffe:///margo/wfm/`, using the `` and `` of the client's own SVID; and 4. abort the connection if any of these checks fails. -A WFM Client holding a long-lived connection SHOULD bound the connection's lifetime, or otherwise re-validate the WFM SVID, per the MIAF [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation) rules, rather than relying solely on the connection-time check above. +A WFM Client holding a long-lived connection SHOULD limit the connection's lifetime, or otherwise re-validate the WFM SVID, per the MIAF [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation) rules, rather than relying solely on the connection-time check above. ## Provisioning From 4aa3ecc4b934b9e76476a62844a1067e6c19ba63 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:07:32 +0200 Subject: [PATCH 10/26] Update system-design/specification/identity/identity-security-considerations.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- .../specification/identity/identity-security-considerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/identity-security-considerations.md b/system-design/specification/identity/identity-security-considerations.md index b851a3ce..d6690cc5 100644 --- a/system-design/specification/identity/identity-security-considerations.md +++ b/system-design/specification/identity/identity-security-considerations.md @@ -2,7 +2,7 @@ MIAF assumes an adversarial network and the possibility of a compromised individual device or service. Its goals are to preserve identity integrity and minimize blast radius; protecting the private keys those identities rest on is the responsibility of the deployment rather than provided by the framework. -This section is scoped to identity. For each threat it states the mitigation MIAF relies on, with the concrete rules in the linked sections. Some mitigations are normative framework requirements; others are residual risks carried by the operator's deployment. +This section is scoped to identity. For each threat, it describes the mitigation that MIAF relies on, along with links to the specific rules. Some mitigations are normative framework requirements; others are residual risks carried by the operator's deployment. ## Framework Threats From 4471a915f0c29332b47276baaf7825e6e1c731b5 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:07:56 +0200 Subject: [PATCH 11/26] Update system-design/specification/identity/identity-security-considerations.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- .../specification/identity/identity-security-considerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/identity-security-considerations.md b/system-design/specification/identity/identity-security-considerations.md index d6690cc5..ae9222c6 100644 --- a/system-design/specification/identity/identity-security-considerations.md +++ b/system-design/specification/identity/identity-security-considerations.md @@ -15,7 +15,7 @@ This section is scoped to identity. For each threat, it describes the mitigation | **Initial trust anchor confusion** | An attacker causes a principal to trust the wrong HTTPS authority and serves a malicious discovery document or Trust Bundle. | A principal MUST authenticate the first HTTPS retrieval of the discovery document and `trustBundleUri` per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). | | **Trust Bundle substitution or anchor injection** | An attacker who compromises a Trust Bundle origin (or its server certificate) injects a trust anchor. Because the SPIFFE bundle is authenticated only by the transport and is not itself signed, every SVID chaining to the injected anchor is then accepted. This applies on every refresh, not only at bootstrap. | A client retrieves the Trust Bundle only over HTTPS validated against its configured initial-trust anchors or pins, on each refresh, per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap) and [certificate validation](tls-requirements.md#certificate-validation), and MUST fail closed on an empty or unusable bundle per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | | **Trust Bundle rollback** | A stale, cached, or replayed Trust Bundle carrying an older `spiffe_sequence` is served to reverse a completed rotation, re-admitting a trust anchor that was removed to revoke a compromise. | A client SHOULD track the highest `spiffe_sequence` it has accepted for the Trust Domain and reject a Trust Bundle whose sequence has regressed, per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | -| **Certificate revocation lag** | A revocation event is not propagated promptly. | A deployment relies on the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook) and short SVID lifetimes; a verifier SHOULD bound connection lifetime so that a long-lived mTLS session does not extend the lag indefinitely (see [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation)). | +| **Certificate revocation lag** | A revocation event is not propagated promptly. | A deployment relies on the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook) and short SVID lifetimes; a verifier SHOULD limit connection lifetime so that a long-lived mTLS session does not extend the lag indefinitely (see [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation)). | | **Service impersonation / MITM** | An adversary attempts to impersonate the MIS or another service. | All endpoints MUST use HTTPS with strict certificate validation per the [TLS requirements](tls-requirements.md); a peer MUST verify a presented SVID against the configured Trust Domain and Trust Bundle. | | **Cross-domain trust confusion** | A component accepts identities from an unintended Trust Domain. | A verifier MUST determine the Trust Domain from the SPIFFE ID and MUST NOT trust an SVID whose Trust Domain is not the verifier's own. | | **Inadvertent inspection-proxy MITM** | An operator-deployed traffic-inspecting proxy intercepts and re-signs Margo mTLS traffic, presenting a substitute certificate to either endpoint. | An operator MUST exempt Margo mTLS endpoints from inspection per [traffic-inspecting proxies](tls-requirements.md#traffic-inspecting-proxies). Both endpoints MUST validate peer SVIDs against the Trust Bundle and reject a substituted certificate. | From 1de8f45c7cc7cda41d5850ea6b0b0677e1b50162 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:08:50 +0200 Subject: [PATCH 12/26] Update system-design/specification/identity/svids.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- system-design/specification/identity/svids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/svids.md b/system-design/specification/identity/svids.md index 104c73b5..9bf19bba 100644 --- a/system-design/specification/identity/svids.md +++ b/system-design/specification/identity/svids.md @@ -8,7 +8,7 @@ MIAF adopts the [SPIFFE X.509-SVID specification](https://github.com/spiffe/spif An identity profile MAY further constrain validity periods, key-protection rules, or path conventions for its own principal classes. -The SPIFFE X.509-SVID specification defines the certificate profile and RFC 5280 path validation but leaves how the chain is conveyed out of scope, so MIAF specifies chain delivery here. When presenting an X.509-SVID, the presenter MUST include the leaf SVID and every intermediate CA certificate needed to build a path to a trust anchor; a certificate the Trust Bundle already carries as a trust anchor (typically the self-signed root) MAY be omitted. This presented chain travels inline wherever an X.509-SVID is conveyed, including the TLS `Certificate` message during mTLS. Because the [Trust Bundle](trust-bundle-and-discovery.md) holds only trust anchors, the presented chain is the sole carrier of the intermediates. +The SPIFFE X.509-SVID specification defines the certificate profile and RFC 5280 path validation but leaves how the chain is conveyed out of scope, so MIAF must specify chain delivery. When presenting an X.509-SVID, the presenter MUST include the leaf SVID and every intermediate CA certificate needed to build a path to a trust anchor; a certificate the Trust Bundle already carries as a trust anchor (typically the self-signed root) MAY be omitted. This presented chain travels inline wherever an X.509-SVID is conveyed, including the TLS `Certificate` message during mTLS. Because the [Trust Bundle](trust-bundle-and-discovery.md) holds only trust anchors, the presented chain is the sole carrier of the intermediates. ## Cryptographic Requirements From 71096427b233541c1a24800c29a8dbcd33340639 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:09:45 +0200 Subject: [PATCH 13/26] Update system-design/specification/identity/tls-requirements.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- system-design/specification/identity/tls-requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md index b35f37fe..c1a1ac4f 100644 --- a/system-design/specification/identity/tls-requirements.md +++ b/system-design/specification/identity/tls-requirements.md @@ -49,7 +49,7 @@ A verifier revokes access to a compromised SVID through the [operator revocation mTLS authenticates a peer only at the handshake. Because MIAF revokes through short SVID lifetimes and Trust Bundle changes rather than an online status mechanism, a long-lived or pooled connection that outlives its peer's SVID, or that survives a Trust Bundle rotation, extends the revocation lag for as long as it stays open. -A verifier therefore SHOULD bound how long an authenticated connection stays in service after the SVID that established it has expired or has ceased to validate against the current Trust Bundle, and that bound SHOULD be short relative to the SVID lifetimes in use (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). Capping the maximum age of a connection satisfies this, since the re-establishing handshake re-validates the peer's current SVID against the current Trust Bundle; a verifier MAY instead re-validate the SVID on the open connection and close it when the SVID is no longer valid, and MAY tighten the bound to the SVID's own `notAfter`. Separately, a verifier SHOULD re-evaluate its local authorization policy for the peer's SPIFFE ID on each request, so that an allowlist removal takes effect without waiting for the connection to close. +A verifier therefore SHOULD limit how long an authenticated connection stays in service after the SVID that established it has expired or has ceased to validate against the current Trust Bundle. That limit SHOULD be short relative to the SVID lifetimes in use (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). Capping the maximum age of a connection satisfies this, since the re-establishing handshake re-validates the peer's current SVID against the current Trust Bundle; a verifier MAY instead re-validate the SVID on the open connection and close it when the SVID is no longer valid, and MAY tighten the limit to the SVID's own `notAfter`. Separately, a verifier SHOULD re-evaluate its local authorization policy for the peer's SPIFFE ID on each request, so that an allowlist removal takes effect without waiting for the connection to close. These bounds are measured from the full TLS handshake that validated the peer's SVID, not from any later resumption of the session. A resumed session inherits the authentication time of that original handshake, so resumption MUST NOT keep a peer authenticated beyond those bounds, and a verifier that issues session tickets SHOULD limit their lifetime accordingly. A client SHOULD proactively re-establish affected connections after renewing its own SVID. From da1da5c290b04f3fcdff81e31288a1c3d8e368e9 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 23 Jul 2026 16:10:50 +0200 Subject: [PATCH 14/26] Update system-design/specification/identity/tls-requirements.md Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- system-design/specification/identity/tls-requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md index c1a1ac4f..b70cbc28 100644 --- a/system-design/specification/identity/tls-requirements.md +++ b/system-design/specification/identity/tls-requirements.md @@ -51,7 +51,7 @@ mTLS authenticates a peer only at the handshake. Because MIAF revokes through sh A verifier therefore SHOULD limit how long an authenticated connection stays in service after the SVID that established it has expired or has ceased to validate against the current Trust Bundle. That limit SHOULD be short relative to the SVID lifetimes in use (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). Capping the maximum age of a connection satisfies this, since the re-establishing handshake re-validates the peer's current SVID against the current Trust Bundle; a verifier MAY instead re-validate the SVID on the open connection and close it when the SVID is no longer valid, and MAY tighten the limit to the SVID's own `notAfter`. Separately, a verifier SHOULD re-evaluate its local authorization policy for the peer's SPIFFE ID on each request, so that an allowlist removal takes effect without waiting for the connection to close. -These bounds are measured from the full TLS handshake that validated the peer's SVID, not from any later resumption of the session. A resumed session inherits the authentication time of that original handshake, so resumption MUST NOT keep a peer authenticated beyond those bounds, and a verifier that issues session tickets SHOULD limit their lifetime accordingly. A client SHOULD proactively re-establish affected connections after renewing its own SVID. +These limits are measured from the full TLS handshake that validated the peer's SVID, not from any later resumption of the session. A resumed session inherits the authentication time of that original handshake, so resumption MUST NOT keep a peer authenticated beyond those limits, and a verifier that issues session tickets SHOULD limit their lifetime accordingly. A client SHOULD proactively re-establish affected connections after renewing its own SVID. ## Traffic-Inspecting Proxies From e7f92f62fe8fea45c8097e62d7eda699371a6469 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 07:52:06 +0000 Subject: [PATCH 15/26] fix: prefix links to pages in the same folder Prefix every link to a page in the same folder with "./" to force the Fumadocs resolving the link as a relative link (solves the 404 issue) Signed-off-by: Matthias Lechner --- .../identity/identity-framework.md | 30 ++++++++-------- .../identity/identity-lifecycle.md | 8 ++--- .../identity-security-considerations.md | 36 +++++++++---------- system-design/specification/identity/svids.md | 12 +++---- .../identity/tls-requirements.md | 18 +++++----- .../identity/trust-bundle-and-discovery.md | 16 ++++----- .../identity/wfm-identity-profile.md | 20 +++++------ 7 files changed, 70 insertions(+), 70 deletions(-) diff --git a/system-design/specification/identity/identity-framework.md b/system-design/specification/identity/identity-framework.md index dd388171..1ac8fc12 100644 --- a/system-design/specification/identity/identity-framework.md +++ b/system-design/specification/identity/identity-framework.md @@ -6,11 +6,11 @@ MIAF defines: - a **Trust Domain** model and the **SPIFFE ID** namespace for identifying Margo components; - an **X.509-SVID** profile (an X.509 certificate carrying a SPIFFE ID in its URI SAN) as the credential a component presents; -- the **SPIFFE bundle** (a JWK Set per [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as the format for distributing trust anchors, located through an optional [discovery document](trust-bundle-and-discovery.md); +- the **SPIFFE bundle** (a JWK Set per [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as the format for distributing trust anchors, located through an optional [discovery document](./trust-bundle-and-discovery.md); - the **Margo Identity Service (MIS)** as the identity-authority role within a Trust Domain; and -- a cryptographic and [TLS baseline](tls-requirements.md) shared by all Margo components, with authentication by mTLS using X.509-SVIDs validated against the Trust Bundle. +- a cryptographic and [TLS baseline](./tls-requirements.md) shared by all Margo components, with authentication by mTLS using X.509-SVIDs validated against the Trust Bundle. -The framework is generic: it does not define an enrollment protocol or a specific identity profile. Those are layered on top. The [Margo WFM Identity Profile](wfm-identity-profile.md) is the first such profile, naming WFMs and WFM Clients and applying MIAF authentication to the [Margo Management Interface](../margo-management-interface/api-requirements-and-security.md). +The framework is generic: it does not define an enrollment protocol or a specific identity profile. Those are layered on top. The [Margo WFM Identity Profile](./wfm-identity-profile.md) is the first such profile, naming WFMs and WFM Clients and applying MIAF authentication to the [Margo Management Interface](../margo-management-interface/api-requirements-and-security.md). Authentication is mTLS with an X.509-SVID. Authorization is performed locally by each verifier, based on the peer's verified SPIFFE ID. There is no central authorization server. @@ -24,8 +24,8 @@ Terms adopted from SPIFFE, used here as SPIFFE defines them: - **Trust Domain**: the governed security boundary within which identities are issued and mutually recognized, a trust-root-backed identity namespace and policy boundary. A Trust Domain defines its authoritative trust anchors (the X.509 authority certificates published for the domain), the namespace for SPIFFE IDs, and the policies for identity lifecycle and authorization. - **SPIFFE ID**: a URI of the form `spiffe:///` that names an identity within a Trust Domain. MIAF adopts [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md) syntax and validation rules by reference and defines Margo path conventions where needed (see [Identity model](#identity-model)). -- **SPIFFE Verifiable Identity Document (SVID)**: the verifiable credential representing an identity within a Trust Domain. An SVID binds a SPIFFE ID to a key pair. Its profile, cryptography, and validation are defined in [SVIDs](svids.md). -- **Trust Bundle**: the cryptographic material (X.509 trust anchors) used to validate SVIDs issued within a Trust Domain, distributed as a SPIFFE bundle (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). +- **SPIFFE Verifiable Identity Document (SVID)**: the verifiable credential representing an identity within a Trust Domain. An SVID binds a SPIFFE ID to a key pair. Its profile, cryptography, and validation are defined in [SVIDs](./svids.md). +- **Trust Bundle**: the cryptographic material (X.509 trust anchors) used to validate SVIDs issued within a Trust Domain, distributed as a SPIFFE bundle (see [Trust Bundle and Discovery](./trust-bundle-and-discovery.md)). Terms introduced by MIAF: @@ -39,11 +39,11 @@ MIAF has four moving parts: the **Trust Domain**, the **Margo Identity Service ( Once a component holds an SVID: -1. **Acquire trust material.** The component acquires its Trust Domain's Trust Bundle: it either locates the bundle through the discovery document and retrieves it over HTTPS, or receives the bundle and Trust Domain identifier through operator-provided configuration or out-of-band delivery (see [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap)). +1. **Acquire trust material.** The component acquires its Trust Domain's Trust Bundle: it either locates the bundle through the discovery document and retrieves it over HTTPS, or receives the bundle and Trust Domain identifier through operator-provided configuration or out-of-band delivery (see [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap)). 2. **Authenticate to peers.** The component and peer complete an mTLS handshake: the component presents its X.509-SVID, and the peer validates the chain against the Trust Bundle. 3. **Authorize the call.** The peer applies its local policy to the now-verified SPIFFE ID. -A component obtains its SVID through the [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook). +A component obtains its SVID through the [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook). > **Conceptual trust and identity architecture (informative)** > @@ -83,8 +83,8 @@ A component obtains its SVID through the [operator provisioning playbook](identi MIAF is a general foundation: any Margo component MAY adopt it, and future identity profiles will extend it to new principal classes. In this release, MIAF governs: -- the **MIS trust endpoints**: the discovery document and Trust Bundle retrieval described in [Trust Bundle and Discovery Endpoints](trust-bundle-and-discovery.md); and -- the **Workload Fleet Management interface**, through the [WFM Identity Profile](wfm-identity-profile.md), which is the only identity profile defined so far. +- the **MIS trust endpoints**: the discovery document and Trust Bundle retrieval described in [Trust Bundle and Discovery Endpoints](./trust-bundle-and-discovery.md); and +- the **Workload Fleet Management interface**, through the [WFM Identity Profile](./wfm-identity-profile.md), which is the only identity profile defined so far. Other Margo components (the Device Fleet Manager, observability collectors, or component registries, for example) MAY hold MIAF identities, but no identity profile is defined for their interfaces yet, so how they authenticate is not governed here until such a profile exists. For an interface into an external ecosystem that carries its own established authentication convention (such as an OCI registry), a MIAF identity is expected to serve as the root credential a component uses to obtain an ecosystem-native credential, rather than as the wire-level authentication mechanism itself. @@ -103,17 +103,17 @@ MIAF references the current published text of each SPIFFE specification instead | Topic | Source | Notes | | :---- | :----- | :---- | | SPIFFE ID syntax and validation rules | [SPIFFE ID](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE-ID.md), adopted by reference | Margo defines only path conventions where needed. | -| X.509-SVID baseline semantics | [SPIFFE X.509-SVID](https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md), adopted by reference and constrained | Margo adds the profile constraints in [SVIDs](svids.md#x509-svid-profile). | +| X.509-SVID baseline semantics | [SPIFFE X.509-SVID](https://github.com/spiffe/spiffe/blob/main/standards/X509-SVID.md), adopted by reference and constrained | Margo adds the profile constraints in [SVIDs](./svids.md#x509-svid-profile). | | Trust Bundle | [SPIFFE Trust Domain and Bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), adopted by reference | Margo serves a single SPIFFE bundle per Trust Domain and defines discovery conventions around it. | -| Bundle endpoint | [SPIFFE Federation](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md), bundle-endpoint model adopted by reference | The Trust Bundle retrieval endpoint follows the bundle-endpoint model (one URL per Trust Domain). The `https_web` and `https_spiffe` endpoint profiles are candidates for future adoption; this release authenticates retrieval per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). | -| Discovery document | Margo | Not part of SPIFFE; defined in [Trust Bundle and Discovery Endpoints](trust-bundle-and-discovery.md#discovery-document-endpoint). | +| Bundle endpoint | [SPIFFE Federation](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md), bundle-endpoint model adopted by reference | The Trust Bundle retrieval endpoint follows the bundle-endpoint model (one URL per Trust Domain). The `https_web` and `https_spiffe` endpoint profiles are candidates for future adoption; this release authenticates retrieval per [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap). | +| Discovery document | Margo | Not part of SPIFFE; defined in [Trust Bundle and Discovery Endpoints](./trust-bundle-and-discovery.md#discovery-document-endpoint). | ## Identity Model - **Identity representation.** An identity is named by a **SPIFFE ID** and represented by an **SVID** issued under the Trust Domain's MIS. -- **Path namespace.** A SPIFFE ID issued under a MIAF identity profile MUST have a path beginning with `/margo/`. Each identity profile claims a non-conflicting sub-prefix and defines its structure (the path conventions for WFMs and WFM Clients are in the [WFM Identity Profile](wfm-identity-profile.md)). So that `/margo/` remains a reliable signal of MIAF provenance, a non-MIAF SVID in the same Trust Domain MUST NOT use it. +- **Path namespace.** A SPIFFE ID issued under a MIAF identity profile MUST have a path beginning with `/margo/`. Each identity profile claims a non-conflicting sub-prefix and defines its structure (the path conventions for WFMs and WFM Clients are in the [WFM Identity Profile](./wfm-identity-profile.md)). So that `/margo/` remains a reliable signal of MIAF provenance, a non-MIAF SVID in the same Trust Domain MUST NOT use it. - **Uniqueness.** Each SPIFFE ID names a single identity within its Trust Domain. -- **Lifecycle.** All identities follow the [lifecycle vocabulary](identity-lifecycle.md#lifecycle-vocabulary). +- **Lifecycle.** All identities follow the [lifecycle vocabulary](./identity-lifecycle.md#lifecycle-vocabulary). - **Extensibility.** The MIS, Trust Domain, SVID, and Trust Bundle concepts are generic; further profiles may be added for new principal classes without redefining the framework. ## The MIS Role @@ -121,7 +121,7 @@ MIAF references the current published text of each SPIFFE specification instead The **Margo Identity Service (MIS)** is a role, not a specific service. Within a Trust Domain, the MIS is responsible for: - issuing X.509-SVIDs to principals; -- serving the [Trust Bundle retrieval endpoint](trust-bundle-and-discovery.md#trust-bundle-retrieval-endpoint) and, when used, the [discovery document endpoint](trust-bundle-and-discovery.md#discovery-document-endpoint) over HTTPS; and +- serving the [Trust Bundle retrieval endpoint](./trust-bundle-and-discovery.md#trust-bundle-retrieval-endpoint) and, when used, the [discovery document endpoint](./trust-bundle-and-discovery.md#discovery-document-endpoint) over HTTPS; and - enforcing MIAF's cryptographic and SVID-profile requirements. Anything that meets these responsibilities can fill the role: [SPIRE](https://spiffe.io/docs/latest/spire-about/), a CA configured for a MIAF profile, an operator's provisioning workflow, or something else. The only wire contract MIAF fixes for the MIS is the two HTTPS trust endpoints above; it does not standardize how the MIS issues SVIDs, and beyond those endpoints conformance is judged by behavior rather than by API surface. diff --git a/system-design/specification/identity/identity-lifecycle.md b/system-design/specification/identity/identity-lifecycle.md index f73bebd6..307015ed 100644 --- a/system-design/specification/identity/identity-lifecycle.md +++ b/system-design/specification/identity/identity-lifecycle.md @@ -41,18 +41,18 @@ Without an automated revocation protocol, a deployment revokes an SVID through o 2. **Trust Bundle rotation**: the operator removes the compromised trust anchor from the Trust Bundle, invalidating every SVID that chains to it. This is heavy-handed but effective when an entire issuance authority is compromised. See the [Trust Anchor Rotation Playbook](#trust-anchor-rotation-playbook) below, which also covers the case where the issuer is an intermediate CA whose anchor is the root above it. 3. **Expiry**: wait for the SVID to expire. This is viable only with shorter SVID lifetimes. -None of these options is instantaneous. Allowlist removal takes effect promptly only where the relying party re-evaluates its authorization policy per request; a Trust Bundle rotation propagates no faster than the fleet's refresh interval. In either case a long-lived mTLS connection can keep a revoked peer authenticated until the connection is re-established (see [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation)). +None of these options is instantaneous. Allowlist removal takes effect promptly only where the relying party re-evaluates its authorization policy per request; a Trust Bundle rotation propagates no faster than the fleet's refresh interval. In either case a long-lived mTLS connection can keep a revoked peer authenticated until the connection is re-established (see [session lifetime and re-validation](./tls-requirements.md#session-lifetime-and-re-validation)). ## Trust Anchor Rotation Playbook -Rotating a Trust Domain's trust anchor is routine CA lifecycle. This playbook is informative operator guidance; the normative rule it relies on is the [X.509-SVID validation](svids.md#x509-svid-validation) rule that a verifier accepts an SVID chaining to any anchor in the current Trust Bundle. That is what lets a bundle carry the old and new anchors together during an overlap. Because a verifier only learns about anchor changes when it refreshes the Trust Bundle (see [Trust Bundle retrieval endpoint](trust-bundle-and-discovery.md#trust-bundle-retrieval-endpoint)), sequence a rotation so that no principal is asked to validate, or authenticate with, material its peers have not yet learned to trust: +Rotating a Trust Domain's trust anchor is routine CA lifecycle. This playbook is informative operator guidance; the normative rule it relies on is the [X.509-SVID validation](./svids.md#x509-svid-validation) rule that a verifier accepts an SVID chaining to any anchor in the current Trust Bundle. That is what lets a bundle carry the old and new anchors together during an overlap. Because a verifier only learns about anchor changes when it refreshes the Trust Bundle (see [Trust Bundle retrieval endpoint](./trust-bundle-and-discovery.md#trust-bundle-retrieval-endpoint)), sequence a rotation so that no principal is asked to validate, or authenticate with, material its peers have not yet learned to trust: 1. **Publish both anchors.** Add the new trust anchor to the Trust Bundle alongside the old one and publish the updated bundle. -2. **Wait for propagation.** Allow every verifier to refresh the bundle before proceeding: at least the longest refresh interval in use across the fleet, extended to cover realistic offline windows for intermittently connected principals. Setting the bundle's `spiffe_refresh_hint` (see [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle)) puts every principal on the same refresh interval, so this wait has one known value to measure against. +2. **Wait for propagation.** Allow every verifier to refresh the bundle before proceeding: at least the longest refresh interval in use across the fleet, extended to cover realistic offline windows for intermittently connected principals. Setting the bundle's `spiffe_refresh_hint` (see [selecting and refreshing the bundle](./trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle)) puts every principal on the same refresh interval, so this wait has one known value to measure against. 3. **Switch issuance.** Issue new SVIDs (and renewals) so they chain to the new anchor. SVIDs that chain to the old anchor remain valid and keep validating, because both anchors are in the bundle. 4. **Retire the old anchor.** Remove the old anchor from the bundle only once no SVID still chains to it: after all principals have renewed, or after the last old SVID has expired. Removing it earlier revokes every remaining SVID that chains to it (which is exactly the intent when rotation is used for compromise response, and an outage otherwise). -Where the MIS operates as an intermediate CA beneath an enterprise or offline root (see the [MIS deployment patterns](identity-framework.md#deployment-patterns-informative)), the trust anchor in the bundle is that root, not the issuing intermediate. Rotation and bundle-level revocation then act at the root: replacing an intermediate issuer under an unchanged root needs no bundle change and does not follow this playbook, while removing the root anchor invalidates every SVID chaining through it, not only those from one intermediate. +Where the MIS operates as an intermediate CA beneath an enterprise or offline root (see the [MIS deployment patterns](./identity-framework.md#deployment-patterns-informative)), the trust anchor in the bundle is that root, not the issuing intermediate. Rotation and bundle-level revocation then act at the root: replacing an intermediate issuer under an unchanged root needs no bundle change and does not follow this playbook, while removing the root anchor invalidates every SVID chaining through it, not only those from one intermediate. ## SVID Lifetime Guidance diff --git a/system-design/specification/identity/identity-security-considerations.md b/system-design/specification/identity/identity-security-considerations.md index f3567117..ab4cc49f 100644 --- a/system-design/specification/identity/identity-security-considerations.md +++ b/system-design/specification/identity/identity-security-considerations.md @@ -8,20 +8,20 @@ This section is scoped to identity. For each threat it states the mitigation MIA | Threat | Description | Mitigation | | :----- | :---------- | :---------- | -| **Theft and misuse of a credential** | An attacker who obtains a principal's SVID together with its private key authenticates as that principal anywhere its SPIFFE ID is accepted; an X.509-SVID carries no audience restriction confining it to one relying party. | A principal MUST protect its private key (see the private-key-compromise row below); SVIDs SHOULD be short-lived per [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance) to bound the exposure window; and each verifier MUST validate the SVID against the Trust Bundle and authorize the specific SPIFFE ID locally, so a stolen credential reaches only what that identity is granted (see [X.509-SVID validation](svids.md#x509-svid-validation)). | -| **Private key compromise** | An attacker exfiltrates a principal's private key and can then authenticate as that principal until the credential is withdrawn. | A principal protects its private key per deployment policy; MIAF specifies no key-protection mechanism of its own. The [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)'s preferred path issues by CSR, which keeps the key on the principal and can bind it to hardware (TPM, secure element, or HSM) where available. Once compromise is known, access is withdrawn via the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook), bounded by short [SVID lifetimes](identity-lifecycle.md#svid-lifetime-guidance). | -| **Central key-custody concentration** | Where a principal cannot generate its own key pair, the operator generates it centrally (see [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)), concentrating many principals' private keys in one store whose breach exposes all of them at once. | The provisioning playbook's preferred path generates the key on the principal (issuance by CSR), leaving no central copy; where a principal cannot generate its own key pair, the operator generates it centrally and accepts the resulting key-custody concentration risk, protecting the store per deployment policy and treating its breach as private-key compromise (row above). | -| **Compromised issuance authority (MIS)** | A compromised [MIS](identity-framework.md#the-mis-role) can mint a valid SVID for any SPIFFE ID in the Trust Domain, impersonating any WFM or client, or issue under a WFM namespace at will. | MIAF concentrates issuance in the MIS by design, so protecting it is an operator PKI-governance responsibility: scope the issuing authority (for example an intermediate CA per the [deployment patterns](identity-framework.md#deployment-patterns-informative)) and protect its signing key per deployment policy. Recovery is [Trust Bundle rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) to retire the compromised authority. | -| **Initial trust anchor confusion** | An attacker causes a principal to trust the wrong HTTPS authority and serves a malicious discovery document or Trust Bundle. | A principal MUST authenticate the first HTTPS retrieval of the discovery document and `trustBundleUri` per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). | -| **Trust Bundle substitution or anchor injection** | An attacker who compromises a Trust Bundle origin (or its server certificate) injects a trust anchor. Because the SPIFFE bundle is authenticated only by the transport and is not itself signed, every SVID chaining to the injected anchor is then accepted. This applies on every refresh, not only at bootstrap. | A client retrieves the Trust Bundle only over HTTPS validated against its configured initial-trust anchors or pins, on each refresh, per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap) and [certificate validation](tls-requirements.md#certificate-validation), and MUST fail closed on an empty or unusable bundle per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | -| **Trust Bundle rollback** | A stale, cached, or replayed Trust Bundle carrying an older `spiffe_sequence` is served to reverse a completed rotation, re-admitting a trust anchor that was removed to revoke a compromise. | A client SHOULD track the highest `spiffe_sequence` it has accepted for the Trust Domain and reject a Trust Bundle whose sequence has regressed, per [selecting and refreshing the bundle](trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | -| **Certificate revocation lag** | A revocation event is not propagated promptly. | A deployment relies on the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook) and short SVID lifetimes; a verifier SHOULD bound connection lifetime so that a long-lived mTLS session does not extend the lag indefinitely (see [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation)). | -| **Service impersonation / MITM** | An adversary attempts to impersonate the MIS or another service. | All endpoints MUST use HTTPS with strict certificate validation per the [TLS requirements](tls-requirements.md); a peer MUST verify a presented SVID against the configured Trust Domain and Trust Bundle. | +| **Theft and misuse of a credential** | An attacker who obtains a principal's SVID together with its private key authenticates as that principal anywhere its SPIFFE ID is accepted; an X.509-SVID carries no audience restriction confining it to one relying party. | A principal MUST protect its private key (see the private-key-compromise row below); SVIDs SHOULD be short-lived per [SVID lifetime guidance](./identity-lifecycle.md#svid-lifetime-guidance) to bound the exposure window; and each verifier MUST validate the SVID against the Trust Bundle and authorize the specific SPIFFE ID locally, so a stolen credential reaches only what that identity is granted (see [X.509-SVID validation](./svids.md#x509-svid-validation)). | +| **Private key compromise** | An attacker exfiltrates a principal's private key and can then authenticate as that principal until the credential is withdrawn. | A principal protects its private key per deployment policy; MIAF specifies no key-protection mechanism of its own. The [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)'s preferred path issues by CSR, which keeps the key on the principal and can bind it to hardware (TPM, secure element, or HSM) where available. Once compromise is known, access is withdrawn via the [operator revocation playbook](./identity-lifecycle.md#operator-revocation-playbook), bounded by short [SVID lifetimes](./identity-lifecycle.md#svid-lifetime-guidance). | +| **Central key-custody concentration** | Where a principal cannot generate its own key pair, the operator generates it centrally (see [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)), concentrating many principals' private keys in one store whose breach exposes all of them at once. | The provisioning playbook's preferred path generates the key on the principal (issuance by CSR), leaving no central copy; where a principal cannot generate its own key pair, the operator generates it centrally and accepts the resulting key-custody concentration risk, protecting the store per deployment policy and treating its breach as private-key compromise (row above). | +| **Compromised issuance authority (MIS)** | A compromised [MIS](./identity-framework.md#the-mis-role) can mint a valid SVID for any SPIFFE ID in the Trust Domain, impersonating any WFM or client, or issue under a WFM namespace at will. | MIAF concentrates issuance in the MIS by design, so protecting it is an operator PKI-governance responsibility: scope the issuing authority (for example an intermediate CA per the [deployment patterns](./identity-framework.md#deployment-patterns-informative)) and protect its signing key per deployment policy. Recovery is [Trust Bundle rotation](./identity-lifecycle.md#trust-anchor-rotation-playbook) to retire the compromised authority. | +| **Initial trust anchor confusion** | An attacker causes a principal to trust the wrong HTTPS authority and serves a malicious discovery document or Trust Bundle. | A principal MUST authenticate the first HTTPS retrieval of the discovery document and `trustBundleUri` per [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap). | +| **Trust Bundle substitution or anchor injection** | An attacker who compromises a Trust Bundle origin (or its server certificate) injects a trust anchor. Because the SPIFFE bundle is authenticated only by the transport and is not itself signed, every SVID chaining to the injected anchor is then accepted. This applies on every refresh, not only at bootstrap. | A client retrieves the Trust Bundle only over HTTPS validated against its configured initial-trust anchors or pins, on each refresh, per [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap) and [certificate validation](./tls-requirements.md#certificate-validation), and MUST fail closed on an empty or unusable bundle per [selecting and refreshing the bundle](./trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | +| **Trust Bundle rollback** | A stale, cached, or replayed Trust Bundle carrying an older `spiffe_sequence` is served to reverse a completed rotation, re-admitting a trust anchor that was removed to revoke a compromise. | A client SHOULD track the highest `spiffe_sequence` it has accepted for the Trust Domain and reject a Trust Bundle whose sequence has regressed, per [selecting and refreshing the bundle](./trust-bundle-and-discovery.md#selecting-and-refreshing-the-bundle). | +| **Certificate revocation lag** | A revocation event is not propagated promptly. | A deployment relies on the [operator revocation playbook](./identity-lifecycle.md#operator-revocation-playbook) and short SVID lifetimes; a verifier SHOULD bound connection lifetime so that a long-lived mTLS session does not extend the lag indefinitely (see [session lifetime and re-validation](./tls-requirements.md#session-lifetime-and-re-validation)). | +| **Service impersonation / MITM** | An adversary attempts to impersonate the MIS or another service. | All endpoints MUST use HTTPS with strict certificate validation per the [TLS requirements](./tls-requirements.md); a peer MUST verify a presented SVID against the configured Trust Domain and Trust Bundle. | | **Cross-domain trust confusion** | A component accepts identities from an unintended Trust Domain. | A verifier MUST determine the Trust Domain from the SPIFFE ID and MUST NOT trust an SVID whose Trust Domain is not the verifier's own. | -| **Inadvertent inspection-proxy MITM** | An operator-deployed traffic-inspecting proxy intercepts and re-signs Margo mTLS traffic, presenting a substitute certificate to either endpoint. | An operator MUST exempt Margo mTLS endpoints from inspection per [traffic-inspecting proxies](tls-requirements.md#traffic-inspecting-proxies). Both endpoints MUST validate peer SVIDs against the Trust Bundle and reject a substituted certificate. | -| **Forwarded-identity header injection** | In a TLS-offload topology, an attacker injects a forged `Client-Cert` header to impersonate an authenticated caller at the backend. | The proxy MUST remove or overwrite any `Client-Cert` or `Client-Cert-Chain` header on incoming requests, and the backend MUST accept a forwarded identity only over the trusted proxy boundary, per [traffic-inspecting proxies](tls-requirements.md#traffic-inspecting-proxies). | -| **Unreliable or manipulated validator clock** | A principal mis-evaluates certificate validity periods because its clock is skewed or unset (a dead RTC battery, no time source at first boot) or because an attacker rewinds it, for example by spoofing an unauthenticated time source on the adversarial network. Rewinding the clock extends the validity window of an expired SVID, defeating the use of expiry as revocation. | SVID validation depends on a trustworthy local time source. A principal SHOULD synchronize time from a trustworthy source before validating certificates, and operators SHOULD account for devices with weak clocks when choosing SVID lifetimes (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). A deployment that cannot assure clock integrity SHOULD NOT rely on expiry alone for revocation. | -| **Handshake identity disclosure on TLS 1.2 fallback** | Where an operator enables the optional TLS 1.2 fallback, the mTLS handshake sends the client X.509-SVID in cleartext, so a passive on-path observer can read the peer's SPIFFE ID, and with it its Trust Domain and client relationship. | Default to TLS 1.3, which encrypts the certificate messages, per the [TLS requirements](tls-requirements.md#minimum-tls-baseline). Enable TLS 1.2 only where a deployment requires it, accepting this passive-disclosure risk; when used, TLS 1.2 MUST follow modern security guidance ([RFC 9325](https://datatracker.ietf.org/doc/html/rfc9325)). | +| **Inadvertent inspection-proxy MITM** | An operator-deployed traffic-inspecting proxy intercepts and re-signs Margo mTLS traffic, presenting a substitute certificate to either endpoint. | An operator MUST exempt Margo mTLS endpoints from inspection per [traffic-inspecting proxies](./tls-requirements.md#traffic-inspecting-proxies). Both endpoints MUST validate peer SVIDs against the Trust Bundle and reject a substituted certificate. | +| **Forwarded-identity header injection** | In a TLS-offload topology, an attacker injects a forged `Client-Cert` header to impersonate an authenticated caller at the backend. | The proxy MUST remove or overwrite any `Client-Cert` or `Client-Cert-Chain` header on incoming requests, and the backend MUST accept a forwarded identity only over the trusted proxy boundary, per [traffic-inspecting proxies](./tls-requirements.md#traffic-inspecting-proxies). | +| **Unreliable or manipulated validator clock** | A principal mis-evaluates certificate validity periods because its clock is skewed or unset (a dead RTC battery, no time source at first boot) or because an attacker rewinds it, for example by spoofing an unauthenticated time source on the adversarial network. Rewinding the clock extends the validity window of an expired SVID, defeating the use of expiry as revocation. | SVID validation depends on a trustworthy local time source. A principal SHOULD synchronize time from a trustworthy source before validating certificates, and operators SHOULD account for devices with weak clocks when choosing SVID lifetimes (see [SVID lifetime guidance](./identity-lifecycle.md#svid-lifetime-guidance)). A deployment that cannot assure clock integrity SHOULD NOT rely on expiry alone for revocation. | +| **Handshake identity disclosure on TLS 1.2 fallback** | Where an operator enables the optional TLS 1.2 fallback, the mTLS handshake sends the client X.509-SVID in cleartext, so a passive on-path observer can read the peer's SPIFFE ID, and with it its Trust Domain and client relationship. | Default to TLS 1.3, which encrypts the certificate messages, per the [TLS requirements](./tls-requirements.md#minimum-tls-baseline). Enable TLS 1.2 only where a deployment requires it, accepting this passive-disclosure risk; when used, TLS 1.2 MUST follow modern security guidance ([RFC 9325](https://datatracker.ietf.org/doc/html/rfc9325)). | ## WFM Identity Profile Threats @@ -29,9 +29,9 @@ WFM and WFM Client identities inherit the framework threats above. The threats b | Threat | Description | Mitigation | | :--- | :--- | :--- | -| **Wrong-WFM impersonation** | A WFM Client connects to a WFM whose SVID is validly issued by the Trust Domain but whose `wfm-id` is not the one that issued the client, so the client authenticates to the wrong WFM. | The WFM Client MUST verify the `wfm-id` in the WFM's SPIFFE path against the `wfm-id` in its own SVID before authenticating (see [Recognition by the WFM Client](wfm-identity-profile.md#recognition-by-the-wfm-client)). | -| **Wrong-WFM-Client acceptance** | A WFM accepts an SVID whose `wfm-id` belongs to a different WFM's namespace, treating the bearer as a client of itself. | The WFM MUST verify the `wfm-id` in the SPIFFE path against its own namespace before authorizing (see [Recognition by the WFM](wfm-identity-profile.md#recognition-by-the-wfm)). | -| **Unauthorized client within the namespace** | A validly issued SVID under a WFM's own namespace (`.../wfm//client/...`) is presented by a party the WFM's operator never authorized; for instance, the MIS issues under the namespace to the wrong principal. | Namespace match alone does not grant access: a WFM MUST admit a caller only if its identity is accepted by the WFM's accepted-client policy (see [Authorization](wfm-identity-profile.md#authorization)). Operators SHOULD also constrain the MIS to issue under a WFM's namespace only to that WFM's authorized clients. | -| **Stale accepted-client policy** | An entry remains in a WFM's accepted-client policy after the relationship is retired, letting a still-valid credential keep accessing the API. | An operator removes `wfm-client-id` entries promptly per the revocation row of the [lifecycle table](wfm-identity-profile.md#lifecycle). The WFM MAY also deny the request by local policy, surfaced as described in [API Requirements and Security](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication), per [Authorization](wfm-identity-profile.md#authorization). | -| **No precise revocation of a compromised WFM** | A WFM has no client-side accepted-server allowlist comparable to the WFM's accepted-client policy, so a compromised WFM SVID cannot be revoked precisely: only fleet-wide Trust Bundle rotation or expiry withdraws it, so both the blast radius and the revocation lag are large. | Keep WFM SVIDs short-lived and reissue with the same SPIFFE ID before expiry; use [Trust Bundle rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) as the cryptographically enforced revocation path, accepting its fleet-wide scope (see the WFM [lifecycle](wfm-identity-profile.md#lifecycle)). | +| **Wrong-WFM impersonation** | A WFM Client connects to a WFM whose SVID is validly issued by the Trust Domain but whose `wfm-id` is not the one that issued the client, so the client authenticates to the wrong WFM. | The WFM Client MUST verify the `wfm-id` in the WFM's SPIFFE path against the `wfm-id` in its own SVID before authenticating (see [Recognition by the WFM Client](./wfm-identity-profile.md#recognition-by-the-wfm-client)). | +| **Wrong-WFM-Client acceptance** | A WFM accepts an SVID whose `wfm-id` belongs to a different WFM's namespace, treating the bearer as a client of itself. | The WFM MUST verify the `wfm-id` in the SPIFFE path against its own namespace before authorizing (see [Recognition by the WFM](./wfm-identity-profile.md#recognition-by-the-wfm)). | +| **Unauthorized client within the namespace** | A validly issued SVID under a WFM's own namespace (`.../wfm//client/...`) is presented by a party the WFM's operator never authorized; for instance, the MIS issues under the namespace to the wrong principal. | Namespace match alone does not grant access: a WFM MUST admit a caller only if its identity is accepted by the WFM's accepted-client policy (see [Authorization](./wfm-identity-profile.md#authorization)). Operators SHOULD also constrain the MIS to issue under a WFM's namespace only to that WFM's authorized clients. | +| **Stale accepted-client policy** | An entry remains in a WFM's accepted-client policy after the relationship is retired, letting a still-valid credential keep accessing the API. | An operator removes `wfm-client-id` entries promptly per the revocation row of the [lifecycle table](./wfm-identity-profile.md#lifecycle). The WFM MAY also deny the request by local policy, surfaced as described in [API Requirements and Security](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication), per [Authorization](./wfm-identity-profile.md#authorization). | +| **No precise revocation of a compromised WFM** | A WFM has no client-side accepted-server allowlist comparable to the WFM's accepted-client policy, so a compromised WFM SVID cannot be revoked precisely: only fleet-wide Trust Bundle rotation or expiry withdraws it, so both the blast radius and the revocation lag are large. | Keep WFM SVIDs short-lived and reissue with the same SPIFFE ID before expiry; use [Trust Bundle rotation](./identity-lifecycle.md#trust-anchor-rotation-playbook) as the cryptographically enforced revocation path, accepting its fleet-wide scope (see the WFM [lifecycle](./wfm-identity-profile.md#lifecycle)). | | **Non-conformant authentication paths** | A WFM exposes an authentication path outside the mTLS model (an unauthenticated onboarding endpoint or an application-layer signature scheme) that an attacker uses to bypass SVID validation. | A WFM MUST authenticate every Management Interface request with mTLS and an X.509-SVID, and MUST reject requests that do not conform per [API Requirements and Security](../margo-management-interface/api-requirements-and-security.md). | diff --git a/system-design/specification/identity/svids.md b/system-design/specification/identity/svids.md index 104c73b5..ddbeef21 100644 --- a/system-design/specification/identity/svids.md +++ b/system-design/specification/identity/svids.md @@ -8,11 +8,11 @@ MIAF adopts the [SPIFFE X.509-SVID specification](https://github.com/spiffe/spif An identity profile MAY further constrain validity periods, key-protection rules, or path conventions for its own principal classes. -The SPIFFE X.509-SVID specification defines the certificate profile and RFC 5280 path validation but leaves how the chain is conveyed out of scope, so MIAF specifies chain delivery here. When presenting an X.509-SVID, the presenter MUST include the leaf SVID and every intermediate CA certificate needed to build a path to a trust anchor; a certificate the Trust Bundle already carries as a trust anchor (typically the self-signed root) MAY be omitted. This presented chain travels inline wherever an X.509-SVID is conveyed, including the TLS `Certificate` message during mTLS. Because the [Trust Bundle](trust-bundle-and-discovery.md) holds only trust anchors, the presented chain is the sole carrier of the intermediates. +The SPIFFE X.509-SVID specification defines the certificate profile and RFC 5280 path validation but leaves how the chain is conveyed out of scope, so MIAF specifies chain delivery here. When presenting an X.509-SVID, the presenter MUST include the leaf SVID and every intermediate CA certificate needed to build a path to a trust anchor; a certificate the Trust Bundle already carries as a trust anchor (typically the self-signed root) MAY be omitted. This presented chain travels inline wherever an X.509-SVID is conveyed, including the TLS `Certificate` message during mTLS. Because the [Trust Bundle](./trust-bundle-and-discovery.md) holds only trust anchors, the presented chain is the sole carrier of the intermediates. ## Cryptographic Requirements -This section constrains the signature algorithms and key parameters used for SVIDs, CSRs, and the keys that back them. The signatures below are those approved in [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf), over the elliptic curves specified in [NIST SP 800-186](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf); RSA and ECDSA key lengths additionally meet the minimum strengths in [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf). The `ES256`-style codes in the table are [JWA](https://datatracker.ietf.org/doc/html/rfc7518) identifiers, used here as familiar shorthand; the certificate itself carries the equivalent PKIX signature-algorithm OID. Transport-layer cryptography is governed separately by the [TLS requirements](tls-requirements.md). +This section constrains the signature algorithms and key parameters used for SVIDs, CSRs, and the keys that back them. The signatures below are those approved in [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf), over the elliptic curves specified in [NIST SP 800-186](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf); RSA and ECDSA key lengths additionally meet the minimum strengths in [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf). The `ES256`-style codes in the table are [JWA](https://datatracker.ietf.org/doc/html/rfc7518) identifiers, used here as familiar shorthand; the certificate itself carries the equivalent PKIX signature-algorithm OID. Transport-layer cryptography is governed separately by the [TLS requirements](./tls-requirements.md). | Algorithm | Requirements | | :-------- | :----------- | @@ -21,10 +21,10 @@ This section constrains the signature algorithms and key parameters used for SVI | **RSA (≥3072 + SHA-256)** | Modulus MUST be at least 3072 bits; signatures MUST use SHA-256. RSASSA-PSS (`PS256`, [RFC 8017](https://datatracker.ietf.org/doc/html/rfc8017)) is RECOMMENDED. RSASSA-PKCS#1 v1.5 (`RS256`) MAY be used only for X.509 certificate and CSR signatures (today the only RSA signatures in MIAF), for compatibility with issuing CAs that cannot produce PSS signatures (its use there remains sanctioned by [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf)). Any other RSA signature MIAF defines later MUST use PSS. | - **ECDSA P-256 with SHA-256 is mandatory to implement.** Every MIAF component MUST implement it, for both presenting and validating SVIDs, so that any two components always share at least one algorithm. -- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. Because a principal presents a single SVID bound to one signature algorithm, an SVID issued with a non-mandatory algorithm can be validated only by peers that implement that algorithm; a peer that does not will fail to validate it. Selecting an SVID signature algorithm that is supported across the Trust Domain is therefore the operator's responsibility at issuance (see [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)). ECDSA P-256 with SHA-256 is always a safe choice. +- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. Because a principal presents a single SVID bound to one signature algorithm, an SVID issued with a non-mandatory algorithm can be validated only by peers that implement that algorithm; a peer that does not will fail to validate it. Selecting an SVID signature algorithm that is supported across the Trust Domain is therefore the operator's responsibility at issuance (see [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)). ECDSA P-256 with SHA-256 is always a safe choice. - A component that validates SVIDs MUST validate every algorithm it accepts from peers. For RSA, this includes accepting both PSS and PKCS#1 v1.5 signatures on certificates. -> **Crypto-agility (informative):** MIAF names its algorithms explicitly so the permitted set can evolve. The set above is classical; post-quantum signature suites are expected to arrive as additional permitted algorithms rather than a redesign of the framework. Defaulting the transport to TLS 1.3 (see [TLS requirements](tls-requirements.md)) supports this: it is the version track on which post-quantum key exchange and authentication are being standardized. +> **Crypto-agility (informative):** MIAF names its algorithms explicitly so the permitted set can evolve. The set above is classical; post-quantum signature suites are expected to arrive as additional permitted algorithms rather than a redesign of the framework. Defaulting the transport to TLS 1.3 (see [TLS requirements](./tls-requirements.md)) supports this: it is the version track on which post-quantum key exchange and authentication are being standardized. Keys MUST be generated with a cryptographically secure random number generator seeded from an entropy source carrying enough genuine entropy for the key size ([RFC 4086](https://datatracker.ietf.org/doc/html/rfc4086)). @@ -35,9 +35,9 @@ These requirements apply to MIAF-generated artifacts and to the keys used in SVI A verifier authenticates a peer by validating the presented X.509-SVID against the peer's Trust Domain and, on success, treating the SPIFFE ID it carries as the peer's identity. A verifier MUST, in order: - read the SPIFFE ID from the leaf certificate's **URI SAN** to determine the peer's Trust Domain, and reject the SVID unless that Trust Domain is the verifier's own. DNS hostname matching does not apply to SVID identity and MUST NOT override the SPIFFE ID. -- validate the presented chain against that Trust Domain's [Trust Bundle](trust-bundle-and-discovery.md), accepting an SVID that chains to any anchor in the current bundle. A bundle MAY contain more than one anchor (for example, during a [trust anchor rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) overlap), and every anchor in it is equally authoritative. Reject any certificate outside its validity period. A verifier MUST NOT rely on AIA fetching or other out-of-band intermediate retrieval; the presenter supplies the intermediates the chain needs. +- validate the presented chain against that Trust Domain's [Trust Bundle](./trust-bundle-and-discovery.md), accepting an SVID that chains to any anchor in the current bundle. A bundle MAY contain more than one anchor (for example, during a [trust anchor rotation](./identity-lifecycle.md#trust-anchor-rotation-playbook) overlap), and every anchor in it is equally authoritative. Reject any certificate outside its validity period. A verifier MUST NOT rely on AIA fetching or other out-of-band intermediate retrieval; the presenter supplies the intermediates the chain needs. - enforce the SPIFFE X.509-SVID leaf constraints and reject any SVID that violates them: basic-constraints `cA` MUST be `false`; `keyCertSign` and `cRLSign` MUST NOT be set in key usage; the SPIFFE ID MUST use the `spiffe` scheme with a non-root path; and the certificate MUST carry exactly one URI SAN. These leaf constraints, and the other structural rules of the SPIFFE X.509-SVID specification, apply during validation as well as issuance. -On success, the verifier applies its local authorization policy to the verified SPIFFE ID (see [Identity model](identity-framework.md#identity-model)); MIAF has no central authorization server. +On success, the verifier applies its local authorization policy to the verified SPIFFE ID (see [Identity model](./identity-framework.md#identity-model)); MIAF has no central authorization server. diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md index b35f37fe..bf97dc4f 100644 --- a/system-design/specification/identity/tls-requirements.md +++ b/system-design/specification/identity/tls-requirements.md @@ -12,28 +12,28 @@ MIAF follows [RFC 9852](https://datatracker.ietf.org/doc/html/rfc9852), which re | **TLS 1.2 fallback** | TLS 1.2 MAY be supported as a non-default fallback where a deployment requires it. When supported, it MUST conform to [RFC 9325](https://datatracker.ietf.org/doc/html/rfc9325). | [RFC 9852](https://datatracker.ietf.org/doc/html/rfc9852), [RFC 9325](https://datatracker.ietf.org/doc/html/rfc9325) | | **Deprecated versions** | SSL v2, SSL v3, TLS 1.0, and TLS 1.1 MUST NOT be used. | [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996) | -The TLS 1.2 fallback carries a confidentiality cost specific to MIAF. A MIAF client certificate is an X.509-SVID, and TLS 1.2 sends the certificate messages in cleartext during the handshake (TLS 1.3 encrypts them), so a passive on-path observer can read the peer's SPIFFE ID, and with it the peer's Trust Domain and client relationship, from any TLS 1.2 mTLS handshake. Defaulting to TLS 1.3 avoids this. An operator that enables the fallback accepts the exposure (see [handshake identity disclosure on TLS 1.2 fallback](identity-security-considerations.md#framework-threats)). +The TLS 1.2 fallback carries a confidentiality cost specific to MIAF. A MIAF client certificate is an X.509-SVID, and TLS 1.2 sends the certificate messages in cleartext during the handshake (TLS 1.3 encrypts them), so a passive on-path observer can read the peer's SPIFFE ID, and with it the peer's Trust Domain and client relationship, from any TLS 1.2 mTLS handshake. Defaulting to TLS 1.3 avoids this. An operator that enables the fallback accepts the exposure (see [handshake identity disclosure on TLS 1.2 fallback](./identity-security-considerations.md#framework-threats)). ## Initial Trust Bootstrap A client cannot validate MIAF-issued SVIDs against the Trust Bundle until it holds that bundle, and it must acquire the first bundle before it has any MIAF credential of its own to authenticate the exchange. A client acquires the trust material (the discovery document, if used, and the Trust Bundle) by one of two paths. -**Authenticated HTTPS retrieval.** The client fetches the discovery document and the Trust Bundle over HTTPS (see [Trust Bundle and Discovery](trust-bundle-and-discovery.md)). Because the client holds no MIAF credential yet, these connections rely on an initial trust mechanism established outside MIAF. The client MUST authenticate both connections using at least one of: +**Authenticated HTTPS retrieval.** The client fetches the discovery document and the Trust Bundle over HTTPS (see [Trust Bundle and Discovery](./trust-bundle-and-discovery.md)). Because the client holds no MIAF credential yet, these connections rely on an initial trust mechanism established outside MIAF. The client MUST authenticate both connections using at least one of: -1. **PKI-anchored validation**: validate the [MIS](identity-framework.md#the-mis-role) server certificate chain to a configured set of trust anchors (web PKI, enterprise PKI, or an operator-configured private CA), with DNS name validation per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). +1. **PKI-anchored validation**: validate the [MIS](./identity-framework.md#the-mis-role) server certificate chain to a configured set of trust anchors (web PKI, enterprise PKI, or an operator-configured private CA), with DNS name validation per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). 2. **Pinned trust**: validate the MIS server certificate chain or public key against operator-provisioned pins (for example, a pinned CA certificate). An operator MAY deliver the trust material for either option (the configured anchors for the first, or the pins for the second) through the same channel used to provision the principal's SVID. A client that cannot authenticate a connection by one of these mechanisms MUST abort. -**Out-of-band delivery.** The operator delivers the Trust Bundle, and the Trust Domain identifier and Trust Bundle URI, directly through the provisioning or deployment channel (see [Bundle contents and distribution](trust-bundle-and-discovery.md#bundle-contents-and-distribution) and the [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook)). No HTTPS retrieval takes place, so there is no bootstrap connection to authenticate; the integrity and authenticity of the delivered material rest on that channel. +**Out-of-band delivery.** The operator delivers the Trust Bundle, and the Trust Domain identifier and Trust Bundle URI, directly through the provisioning or deployment channel (see [Bundle contents and distribution](./trust-bundle-and-discovery.md#bundle-contents-and-distribution) and the [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)). No HTTPS retrieval takes place, so there is no bootstrap connection to authenticate; the integrity and authenticity of the delivered material rest on that channel. Whichever path is used, a client MUST NOT accept trust material from an unauthenticated source, and MUST NOT treat the first acquisition as "trust on first use". Once acquired, the discovery document (if used) and the Trust Bundle are MIAF's authoritative sources; that bundle then validates SVIDs within the Trust Domain. ## Certificate Validation -Each endpoint MUST validate the peer's TLS certificate chain and identity in accordance with [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280). The validating party MUST verify that the presented certificate chain is within its validity period and MUST reject an expired certificate. MIAF does not use RFC 5280 online revocation checking (CRL or OCSP); a compromised credential is withdrawn by removing its trust anchor from the Trust Bundle and by short SVID lifetimes (see [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook) and [Session Lifetime and Re-validation](#session-lifetime-and-re-validation)). +Each endpoint MUST validate the peer's TLS certificate chain and identity in accordance with [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280). The validating party MUST verify that the presented certificate chain is within its validity period and MUST reject an expired certificate. MIAF does not use RFC 5280 online revocation checking (CRL or OCSP); a compromised credential is withdrawn by removing its trust anchor from the Trust Bundle and by short SVID lifetimes (see [operator revocation playbook](./identity-lifecycle.md#operator-revocation-playbook) and [Session Lifetime and Re-validation](#session-lifetime-and-re-validation)). -Validity-period evaluation depends on a trustworthy local clock (see [unreliable validator clock](identity-security-considerations.md#framework-threats)). A verifier MAY apply a small, bounded clock-skew tolerance consistent with its time-synchronization assumptions. +Validity-period evaluation depends on a trustworthy local clock (see [unreliable validator clock](./identity-security-considerations.md#framework-threats)). A verifier MAY apply a small, bounded clock-skew tolerance consistent with its time-synchronization assumptions. **Server identity for MIAF HTTPS endpoints (discovery and Trust Bundle retrieval):** @@ -41,15 +41,15 @@ Validity-period evaluation depends on a trustworthy local clock (see [unreliable - Under PKI-anchored validation, the client MUST validate the expected DNS name per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). Under pinned trust, the pin itself establishes server identity, so RFC 6125 DNS-name validation applies only where the client connects by a DNS name; a client connecting to a pinned endpoint by IP address is not required to perform it. - The Trust Bundle selected from `trustBundleUri` MUST NOT replace these TLS server-validation checks for MIAF HTTPS endpoints; it is used to validate SVIDs within the Trust Domain. -**SVID identity for MIAF mTLS:** when a peer presents an X.509-SVID at the mTLS layer, the verifier validates it and derives the peer's identity per the [X.509-SVID validation](svids.md#x509-svid-validation) rules. SVID identity is established by the SPIFFE ID in the URI SAN, not by a DNS name. +**SVID identity for MIAF mTLS:** when a peer presents an X.509-SVID at the mTLS layer, the verifier validates it and derives the peer's identity per the [X.509-SVID validation](./svids.md#x509-svid-validation) rules. SVID identity is established by the SPIFFE ID in the URI SAN, not by a DNS name. -A verifier revokes access to a compromised SVID through the [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook). +A verifier revokes access to a compromised SVID through the [operator revocation playbook](./identity-lifecycle.md#operator-revocation-playbook). ## Session Lifetime and Re-validation mTLS authenticates a peer only at the handshake. Because MIAF revokes through short SVID lifetimes and Trust Bundle changes rather than an online status mechanism, a long-lived or pooled connection that outlives its peer's SVID, or that survives a Trust Bundle rotation, extends the revocation lag for as long as it stays open. -A verifier therefore SHOULD bound how long an authenticated connection stays in service after the SVID that established it has expired or has ceased to validate against the current Trust Bundle, and that bound SHOULD be short relative to the SVID lifetimes in use (see [SVID lifetime guidance](identity-lifecycle.md#svid-lifetime-guidance)). Capping the maximum age of a connection satisfies this, since the re-establishing handshake re-validates the peer's current SVID against the current Trust Bundle; a verifier MAY instead re-validate the SVID on the open connection and close it when the SVID is no longer valid, and MAY tighten the bound to the SVID's own `notAfter`. Separately, a verifier SHOULD re-evaluate its local authorization policy for the peer's SPIFFE ID on each request, so that an allowlist removal takes effect without waiting for the connection to close. +A verifier therefore SHOULD bound how long an authenticated connection stays in service after the SVID that established it has expired or has ceased to validate against the current Trust Bundle, and that bound SHOULD be short relative to the SVID lifetimes in use (see [SVID lifetime guidance](./identity-lifecycle.md#svid-lifetime-guidance)). Capping the maximum age of a connection satisfies this, since the re-establishing handshake re-validates the peer's current SVID against the current Trust Bundle; a verifier MAY instead re-validate the SVID on the open connection and close it when the SVID is no longer valid, and MAY tighten the bound to the SVID's own `notAfter`. Separately, a verifier SHOULD re-evaluate its local authorization policy for the peer's SPIFFE ID on each request, so that an allowlist removal takes effect without waiting for the connection to close. These bounds are measured from the full TLS handshake that validated the peer's SVID, not from any later resumption of the session. A resumed session inherits the authentication time of that original handshake, so resumption MUST NOT keep a peer authenticated beyond those bounds, and a verifier that issues session tickets SHOULD limit their lifetime accordingly. A client SHOULD proactively re-establish affected connections after renewing its own SVID. diff --git a/system-design/specification/identity/trust-bundle-and-discovery.md b/system-design/specification/identity/trust-bundle-and-discovery.md index a5d064e7..18ccfb0d 100644 --- a/system-design/specification/identity/trust-bundle-and-discovery.md +++ b/system-design/specification/identity/trust-bundle-and-discovery.md @@ -1,10 +1,10 @@ # Trust Bundle and Discovery -The MIS role serves two read-only HTTPS endpoints: an optional **discovery document** that points a client to the Trust Bundle, and the **Trust Bundle retrieval** endpoint itself. Because the MIS is a role rather than a fixed service (see [The MIS role](identity-framework.md#the-mis-role)), the origin hosting these endpoints is chosen by the MIS implementation; this section constrains only the path convention (when discovery is used) and the response payloads. +The MIS role serves two read-only HTTPS endpoints: an optional **discovery document** that points a client to the Trust Bundle, and the **Trust Bundle retrieval** endpoint itself. Because the MIS is a role rather than a fixed service (see [The MIS role](./identity-framework.md#the-mis-role)), the origin hosting these endpoints is chosen by the MIS implementation; this section constrains only the path convention (when discovery is used) and the response payloads. -Both endpoints MUST be served over HTTPS authenticated per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap), and a client MUST tolerate unknown response fields so that future revisions can add fields without breaking existing implementations. +Both endpoints MUST be served over HTTPS authenticated per [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap), and a client MUST tolerate unknown response fields so that future revisions can add fields without breaking existing implementations. -A machine-readable description of both endpoints is available as the [Trust Bundle API OpenAPI definition](trust-bundle-api-swagger.md). +A machine-readable description of both endpoints is available as the [Trust Bundle API OpenAPI definition](./trust-bundle-api-swagger.md). ## Discovery Document Endpoint @@ -12,7 +12,7 @@ The discovery document is an optional entry point to a Trust Domain that points When discovery is used, an origin serving exactly one Trust Domain SHOULD expose the document at `GET /.well-known/margo` per [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615); an origin serving several Trust Domains MAY use other absolute HTTPS URLs. When discovery is not used, the Trust Domain identifier and Trust Bundle URI are supplied by operator-provided configuration. -The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). +The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap). ### Route and HTTP Methods @@ -68,7 +68,7 @@ Response (`200 OK`): The resource identified by `trustBundleUri` returns the Trust Domain's SPIFFE bundle, which holds the authoritative set of public trust anchors for that Trust Domain. The endpoint follows the SPIFFE [bundle endpoint](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Federation.md) model, where one URL serves one Trust Domain's bundle. -The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](tls-requirements.md#initial-trust-bootstrap). A client cannot yet validate MIAF-issued SVIDs when it first retrieves trust material, so this connection relies on an initial trust mechanism established outside MIAF, not on a MIAF SVID. When `trustBundleUri` names a different origin than the discovery document, the client's initial-trust material (configured PKI anchors or operator-provisioned pins) MUST cover that origin. +The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap). A client cannot yet validate MIAF-issued SVIDs when it first retrieves trust material, so this connection relies on an initial trust mechanism established outside MIAF, not on a MIAF SVID. When `trustBundleUri` names a different origin than the discovery document, the client's initial-trust material (configured PKI anchors or operator-provisioned pins) MUST cover that origin. ### Route and HTTP Methods @@ -95,7 +95,7 @@ GET ### Example Bundle Response -The bundle carries the Trust Domain's X.509 trust anchors as JWK entries with `"use": "x509-svid"`. Each authority's certificate travels in `x5c` (base64-encoded DER). During a [trust anchor rotation](identity-lifecycle.md#trust-anchor-rotation-playbook) overlap the `keys` array carries more than one `x509-svid` entry. +The bundle carries the Trust Domain's X.509 trust anchors as JWK entries with `"use": "x509-svid"`. Each authority's certificate travels in `x5c` (base64-encoded DER). During a [trust anchor rotation](./identity-lifecycle.md#trust-anchor-rotation-playbook) overlap the `keys` array carries more than one `x509-svid` entry. Response (`200 OK`): @@ -120,12 +120,12 @@ Response (`200 OK`): A client uses the retrieved bundle as the authoritative source when validating SVIDs issued within the Trust Domain. A client that retrieves a bundle carrying no X.509 trust anchors MUST reject it and MUST NOT validate SVIDs against it, failing closed rather than proceeding with an empty anchor set. To resist rollback, where the bundle carries `spiffe_sequence` a client SHOULD track the highest value it has accepted for the Trust Domain and SHOULD reject a retrieved bundle whose `spiffe_sequence` has regressed, since a lower value signals a stale, cached, or replayed bundle that could re-admit a trust anchor that was retired to revoke a compromise. -A client SHOULD refresh its cached bundle at the interval given by the bundle's `spiffe_refresh_hint`, when present, and otherwise at an operator-configured interval. This refresh cadence is authoritative: HTTP cache revalidation (`If-None-Match`/`304`, and any `Cache-Control` freshness) is an efficiency optimization within it and MUST NOT defer a refresh the interval requires. The refresh interval bounds how quickly a Trust Bundle rotation reaches the fleet; the [trust anchor rotation playbook](identity-lifecycle.md#trust-anchor-rotation-playbook) depends on it. +A client SHOULD refresh its cached bundle at the interval given by the bundle's `spiffe_refresh_hint`, when present, and otherwise at an operator-configured interval. This refresh cadence is authoritative: HTTP cache revalidation (`If-None-Match`/`304`, and any `Cache-Control` freshness) is an efficiency optimization within it and MUST NOT defer a refresh the interval requires. The refresh interval bounds how quickly a Trust Bundle rotation reaches the fleet; the [trust anchor rotation playbook](./identity-lifecycle.md#trust-anchor-rotation-playbook) depends on it. ## Bundle Contents and Distribution A Trust Bundle is distributed as a SPIFFE [bundle](https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md), MAY additionally be delivered through deployment tooling or provisioning flows, and SHOULD be cached locally by a client to support offline validation. -The bundle contains the Trust Domain's X.509 trust anchors only; intermediate CA certificates travel with the presented SVID chain, not in the bundle (see [chain delivery](svids.md#x509-svid-profile)). +The bundle contains the Trust Domain's X.509 trust anchors only; intermediate CA certificates travel with the presented SVID chain, not in the bundle (see [chain delivery](./svids.md#x509-svid-profile)). A SPIFFE bundle is a JWK Set that MAY also carry JWT-SVID signing keys (`"use": "jwt-svid"`). MIAF uses only `x509-svid` entries; an implementation MUST ignore any `jwt-svid` or other non-`x509-svid` key material found in the bundle. diff --git a/system-design/specification/identity/wfm-identity-profile.md b/system-design/specification/identity/wfm-identity-profile.md index e4c952f5..94ae0473 100644 --- a/system-design/specification/identity/wfm-identity-profile.md +++ b/system-design/specification/identity/wfm-identity-profile.md @@ -1,6 +1,6 @@ # WFM Identity Profile -The WFM Identity Profile is the first identity profile under the [Margo Identity and Authorization Framework](identity-framework.md). It covers both the **WFM identity** and the **WFM Client identity**: how each is named, how each is recognized, how each is provisioned, and how the WFM authenticates and authorizes a caller at its API. +The WFM Identity Profile is the first identity profile under the [Margo Identity and Authorization Framework](./identity-framework.md). It covers both the **WFM identity** and the **WFM Client identity**: how each is named, how each is recognized, how each is provisioned, and how the WFM authenticates and authorizes a caller at its API. A WFM holds an identity within the Trust Domain that anchors its namespace. A WFM Client holds an identity within that same Trust Domain, named under the WFM that issues it. Authentication is mutual: a WFM Client presents its X.509-SVID and validates the WFM's SVID, and the caller identity at the WFM API is the authenticated WFM Client SPIFFE ID carried over mTLS. @@ -56,7 +56,7 @@ The `wfm-id` and `wfm-client-id` segments carry no meaning beyond naming the WFM ### Identity Representation -X.509-SVID is the representation used for WFM and WFM Client authentication, per the MIAF [X.509-SVID profile](svids.md#x509-svid-profile). +X.509-SVID is the representation used for WFM and WFM Client authentication, per the MIAF [X.509-SVID profile](./svids.md#x509-svid-profile). ### Recognition by the WFM @@ -68,7 +68,7 @@ A WFM MUST recognize a WFM Client from the authenticated SPIFFE ID alone. When a A WFM MUST NOT treat a peer as one of its clients when the peer's SPIFFE ID does not match this shape, even if that SVID is validly issued within the Trust Domain. -Over the life of the connection, the WFM SHOULD bound connection lifetime per the MIAF [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation) rules, and MUST authorize each request using local policy keyed on the WFM Client identity, per [Authorization](#authorization). +Over the life of the connection, the WFM SHOULD bound connection lifetime per the MIAF [session lifetime and re-validation](./tls-requirements.md#session-lifetime-and-re-validation) rules, and MUST authorize each request using local policy keyed on the WFM Client identity, per [Authorization](#authorization). ### Recognition by the WFM Client @@ -79,35 +79,35 @@ A WFM Client MUST recognize the WFM it connects to from the authenticated SPIFFE 3. verify that the SPIFFE ID is exactly `spiffe:///margo/wfm/`, using the `` and `` of the client's own SVID; and 4. abort the connection if any of these checks fails. -A WFM Client holding a long-lived connection SHOULD bound the connection's lifetime, or otherwise re-validate the WFM SVID, per the MIAF [session lifetime and re-validation](tls-requirements.md#session-lifetime-and-re-validation) rules, rather than relying solely on the connection-time check above. +A WFM Client holding a long-lived connection SHOULD bound the connection's lifetime, or otherwise re-validate the WFM SVID, per the MIAF [session lifetime and re-validation](./tls-requirements.md#session-lifetime-and-re-validation) rules, rather than relying solely on the connection-time check above. ## Provisioning -WFM and WFM Client SVIDs are both provisioned by the operator. The MIAF [operator provisioning playbook](identity-lifecycle.md#operator-provisioning-playbook) applies; the SPIFFE path and acceptance policy for each principal type are below. +WFM and WFM Client SVIDs are both provisioned by the operator. The MIAF [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook) applies; the SPIFFE path and acceptance policy for each principal type are below. **For each WFM, the operator:** 1. chooses a `wfm-id` for the WFM namespace; -2. mints an X.509-SVID with URI SAN `spiffe:///margo/wfm/`, conforming to the MIAF [X.509-SVID profile](svids.md#x509-svid-profile) and [cryptographic requirements](svids.md#cryptographic-requirements); and +2. mints an X.509-SVID with URI SAN `spiffe:///margo/wfm/`, conforming to the MIAF [X.509-SVID profile](./svids.md#x509-svid-profile) and [cryptographic requirements](./svids.md#cryptographic-requirements); and 3. installs the SVID (and private key material, if generated centrally) on the WFM. **For each WFM Client, the operator:** 1. chooses a `wfm-id` for the target WFM (matching the WFM's `wfm-id`) and a `wfm-client-id` for this client relationship; -2. mints an X.509-SVID with URI SAN `spiffe:///margo/wfm//client/`, conforming to the MIAF [X.509-SVID profile](svids.md#x509-svid-profile) and [cryptographic requirements](svids.md#cryptographic-requirements); +2. mints an X.509-SVID with URI SAN `spiffe:///margo/wfm//client/`, conforming to the MIAF [X.509-SVID profile](./svids.md#x509-svid-profile) and [cryptographic requirements](./svids.md#cryptographic-requirements); 3. installs the SVID (and private key material, if generated centrally) on the principal; 4. configures the client with the WFM's endpoint URL. The URL is routing information only: the client authenticates the WFM by its SVID, matching it against the `` and `` carried in the client's own SVID per [Recognition by the WFM Client](#recognition-by-the-wfm-client), not by the URL; and 5. adds the new `wfm-client-id` (or full SPIFFE ID) to the target WFM's accepted-client policy, so that the WFM will authorize requests from this client per [Authorization](#authorization). ## Lifecycle -The MIAF [lifecycle vocabulary](identity-lifecycle.md#lifecycle-vocabulary) applies to both WFM and WFM Client identities. The **Active** phase has a fully normative protocol surface: a client authenticates to a WFM over mTLS using its X.509-SVID per the Management Interface [identity and authentication](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication) rules, and validates the WFM SVID per [Recognition by the WFM Client](#recognition-by-the-wfm-client). The other phases are operator-driven: +The MIAF [lifecycle vocabulary](./identity-lifecycle.md#lifecycle-vocabulary) applies to both WFM and WFM Client identities. The **Active** phase has a fully normative protocol surface: a client authenticates to a WFM over mTLS using its X.509-SVID per the Management Interface [identity and authentication](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication) rules, and validates the WFM SVID per [Recognition by the WFM Client](#recognition-by-the-wfm-client). The other phases are operator-driven: | Phase | WFM | WFM Client | | :---- | :--------- | :--------- | | Enrollment | Mint SVID with URI SAN `spiffe:///margo/wfm/`; install on the WFM. | Mint SVID with URI SAN `spiffe:///margo/wfm//client/`; install on the principal; add `wfm-client-id` to the WFM's accepted-client policy. | | Renewal | Mint a replacement SVID (same SPIFFE ID) before expiry; install on the WFM. | Mint a replacement SVID (same SPIFFE ID) before expiry; install on the principal. | -| Revocation | Rotate the Trust Bundle to invalidate the issuing CA (this also invalidates the WFM Clients issued under that CA). See the MIAF [operator revocation playbook](identity-lifecycle.md#operator-revocation-playbook). | Remove `wfm-client-id` from the WFM's accepted-client policy. For mass revocation, rotate the Trust Bundle. | +| Revocation | Rotate the Trust Bundle to invalidate the issuing CA (this also invalidates the WFM Clients issued under that CA). See the MIAF [operator revocation playbook](./identity-lifecycle.md#operator-revocation-playbook). | Remove `wfm-client-id` from the WFM's accepted-client policy. For mass revocation, rotate the Trust Bundle. | | Re-issuance | Mint a new SVID with the same SPIFFE ID; install on the replacement WFM. | Mint a new SVID (same or new `wfm-client-id`, per operator policy); install on the replacement principal; update the WFM's accepted-client policy if the identifier changed. | WFM revocation is heavier-handed than WFM Client revocation because there is no client-side accepted-server allowlist comparable to the WFM's accepted-client policy. An operator reissues the WFM SVID (keeping the same SPIFFE ID) in most cases; Trust Bundle rotation is the cryptographically enforced revocation path. @@ -118,6 +118,6 @@ Removing a `wfm-client-id` revokes one client only where the accepted-client pol A WFM MUST authorize each request using local policy keyed on the authenticated WFM Client identity. Recognizing the SPIFFE ID (see [Recognition by the WFM](#recognition-by-the-wfm)) establishes only that the caller is a validly issued client within this WFM's namespace; it does not by itself grant access. -A WFM MUST maintain an accepted-client policy and admit a caller only when its identity is accepted by that policy; a matching `wfm-id` namespace is necessary but not sufficient. The policy MAY accept named `wfm-client-id`s (or full SPIFFE IDs) individually, and MAY accept any client within this WFM's namespace where the operator trusts the [MIS](identity-framework.md#the-mis-role) to issue identities under `spiffe:///margo/wfm//client/` only to authorized clients. How the policy is expressed is implementation-specific; the requirement is that acceptance is an explicit local decision, not an automatic consequence of holding a valid SVID. Policy MAY further consider deployment-specific `wfm-client-id` metadata, and a WFM MAY deny a request from a still-valid credential; for example, once a client relationship has been retired. +A WFM MUST maintain an accepted-client policy and admit a caller only when its identity is accepted by that policy; a matching `wfm-id` namespace is necessary but not sufficient. The policy MAY accept named `wfm-client-id`s (or full SPIFFE IDs) individually, and MAY accept any client within this WFM's namespace where the operator trusts the [MIS](./identity-framework.md#the-mis-role) to issue identities under `spiffe:///margo/wfm//client/` only to authorized clients. How the policy is expressed is implementation-specific; the requirement is that acceptance is an explicit local decision, not an automatic consequence of holding a valid SVID. Policy MAY further consider deployment-specific `wfm-client-id` metadata, and a WFM MAY deny a request from a still-valid credential; for example, once a client relationship has been retired. How this profile applies to the Margo Management Interface is specified in [API Requirements and Security](../margo-management-interface/api-requirements-and-security.md#identity-and-authentication): the endpoints served, the mTLS authentication of each call, the handling of caller identity, and how an authorization denial is surfaced (HTTP status and response body). From 2e3b6b5c34be58c16fcace26570d1b9ce7ceb850 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 08:26:53 +0000 Subject: [PATCH 16/26] docs: align the Fumadocs site navigation with the mkdocs preview The published site (Fumadocs, at docs.margo.org) builds its sidebar from a meta.json in each folder; with none present it falls back to alphabetical order, which scrambles the intended reading sequence. Add a meta.json for each specification section that sets the section title and lists its pages in the order given by mkdocs.yml, so the rendered site matches the local mkdocs preview. Signed-off-by: Matthias Lechner --- system-design/specification/applications/meta.json | 7 +++++++ system-design/specification/identity/meta.json | 13 +++++++++++++ system-design/specification/margo-devices/meta.json | 6 ++++++ .../margo-management-interface/meta.json | 10 ++++++++++ system-design/specification/observability/meta.json | 8 ++++++++ 5 files changed, 44 insertions(+) create mode 100644 system-design/specification/applications/meta.json create mode 100644 system-design/specification/identity/meta.json create mode 100644 system-design/specification/margo-devices/meta.json create mode 100644 system-design/specification/margo-management-interface/meta.json create mode 100644 system-design/specification/observability/meta.json diff --git a/system-design/specification/applications/meta.json b/system-design/specification/applications/meta.json new file mode 100644 index 00000000..0fd50e9a --- /dev/null +++ b/system-design/specification/applications/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Applications", + "pages": [ + "application-description", + "application-registry" + ] +} diff --git a/system-design/specification/identity/meta.json b/system-design/specification/identity/meta.json new file mode 100644 index 00000000..c617def3 --- /dev/null +++ b/system-design/specification/identity/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Identity and Authorization", + "pages": [ + "identity-framework", + "svids", + "tls-requirements", + "trust-bundle-and-discovery", + "identity-lifecycle", + "wfm-identity-profile", + "identity-security-considerations", + "trust-bundle-api-1.0.0" + ] +} diff --git a/system-design/specification/margo-devices/meta.json b/system-design/specification/margo-devices/meta.json new file mode 100644 index 00000000..dc73893c --- /dev/null +++ b/system-design/specification/margo-devices/meta.json @@ -0,0 +1,6 @@ +{ + "title": "Margo Devices", + "pages": [ + "device-requirements" + ] +} diff --git a/system-design/specification/margo-management-interface/meta.json b/system-design/specification/margo-management-interface/meta.json new file mode 100644 index 00000000..653de90b --- /dev/null +++ b/system-design/specification/margo-management-interface/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Margo Management Interface", + "pages": [ + "api-requirements-and-security", + "device-capabilities", + "desired-state", + "deployment-status", + "workload-management-api-1.0.0" + ] +} diff --git a/system-design/specification/observability/meta.json b/system-design/specification/observability/meta.json new file mode 100644 index 00000000..289bef39 --- /dev/null +++ b/system-design/specification/observability/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Observability", + "pages": [ + "publishing-workload-observability-data", + "collecting-workload-observability-data", + "consuming-workload-observability-data" + ] +} From 269cf50f2e3e8ec658e48df741d3ad58f1fe9d3a Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 09:46:12 +0000 Subject: [PATCH 17/26] fix: use a colon in desired-state headings so anchors resolve Signed-off-by: Matthias Lechner --- .../resources/index.md.jinja2 | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/specification/margo-management-interface/resources/index.md.jinja2 b/src/specification/margo-management-interface/resources/index.md.jinja2 index 3fc60ed2..b76a132c 100644 --- a/src/specification/margo-management-interface/resources/index.md.jinja2 +++ b/src/specification/margo-management-interface/resources/index.md.jinja2 @@ -23,7 +23,7 @@ This design allows the Workload Fleet Management Client to choose the optimal re The Workload Fleet Management Client compares the manifest with its current state and reconciles any differences by deploying, updating, or removing workloads. For every change in deployment state - including installation, updates, removals, and failures - the client MUST report the corresponding status to the WFM using the [Deployment Status API](../margo-management-interface/deployment-status.md). -## Endpoints - State Manifest +## Endpoints: State Manifest This section defines the API endpoint used by a client to retrieve the State Manifest from the Workload Fleet Manager, representing the complete desired workload configuration assigned to the device. @@ -76,12 +76,12 @@ GET /api/v1/deployments | `manifestVersion` | number | Y | Monotonically increasing unsigned 64-bit integer in the inclusive range `[1, 2^64-1]`. Each new manifest for the same (device) client MUST have a strictly greater value than the previous. The first manifest for a given client MUST use the value 1. | | `bundle` | object | Y | Describes an archive containing all referenced `ApplicationDeployment` YAMLs. If there are zero deployments (i.e., the `deployments` array is empty), this field MUST be present with the value `null`. An empty archive MUST NOT be served. | | `bundle.mediaType` | string | Y | MUST be `application/vnd.margo.bundle.v1+tar+gzip`, which denotes a gzip-compressed tar archive (commonly delivered as a .tar.gz) whose root contains one or more `ApplicationDeployment` YAML files. Servers MUST set the HTTP `Content-Type` to this media type. The archive MUST contain exactly the set of YAML files referenced by `deployments`. | -| `bundle.digest` | string | Y | Digest of the bundle archive. MUST equal the digest computed over the exact sequence of bytes in the [bundle endpoint's](#endpoints-deployment-bundle) HTTP `200 OK` response body. See [Protocol - Digest](#protocol-digest) for further details. | +| `bundle.digest` | string | Y | Digest of the bundle archive. MUST equal the digest computed over the exact sequence of bytes in the [bundle endpoint's](#endpoints-deployment-bundle) HTTP `200 OK` response body. See [Protocol: Digest](#protocol-digest) for further details. | | `bundle.sizeBytes` | number | N | Optional unsigned 64-bit advisory estimate of the decoded payload length in bytes for the bundle archive. Provided for bandwidth estimation and update planning. MUST NOT be used for integrity verification. | | `bundle.url` | string | Y | Content-addressable retrieval endpoint for the bundle of the form `/api/v1/bundles/{digest}` where `{digest}` equals `bundle.digest`. | | `deployments` | array | Y | List of deployment objects describing each workload. | | `deployments[].deploymentId` | string | Y | The UUID of the deployment. MUST equal top-level `id` attribute in the `ApplicationDeployment`. | -| `deployments[].digest` | string | Y | Digest of the corresponding `ApplicationDeployment` YAML file. MUST equal the digest computed over the exact sequence of bytes in the [individual deployment endpoint's](#endpoints-individual-deployment-yaml) HTTP `200 OK` response body. See [Protocol - Digest](#protocol-digest) for further details. | +| `deployments[].digest` | string | Y | Digest of the corresponding `ApplicationDeployment` YAML file. MUST equal the digest computed over the exact sequence of bytes in the [individual deployment endpoint's](#endpoints-individual-deployment-yaml) HTTP `200 OK` response body. See [Protocol: Digest](#protocol-digest) for further details. | | `deployments[].sizeBytes` | number | N | Optional unsigned 64-bit advisory estimate of the decoded payload length in bytes for the `ApplicationDeployment` YAML. Provided for bandwidth estimation and update planning. MUST NOT be used for integrity verification. | | `deployments[].url` | string | Y | Content-addressable retrieval endpoint for the `ApplicationDeployment` YAML of the form `/api/v1/deployments/{deploymentId}/{digest}` where `{digest}` equals `deployments[].digest`. | @@ -93,7 +93,7 @@ GET /api/v1/deployments - If any digest validation fails, the client MUST abort the update and retain the previous state. - The client MUST persist both the last accepted `manifestVersion` and `ETag` to prevent rollback across restarts. -## Endpoints - Individual Deployment YAML +## Endpoints: Individual Deployment YAML This section defines the API endpoint used by a client to retrieve a single `ApplicationDeployment` YAML for incremental synchronization and targeted updates. @@ -108,19 +108,19 @@ GET /api/v1/deployments/{deploymentId}/{digest} | Parameter | Type | Required? | Description | | --------- | ---- | --------- | ----------- | | `{deploymentId}` | string | Y | The UUID of the served `ApplicationDeployment` YAML. This MUST equal to top-level `id` attribute in the ApplicationDeployment. | -| `{digest}` | string | Y | Content-addressable digest of the served `ApplicationDeployment` YAML. See [Protocol - Digest](#protocol-digest) for further details. | +| `{digest}` | string | Y | Content-addressable digest of the served `ApplicationDeployment` YAML. See [Protocol: Digest](#protocol-digest) for further details. | ### Response Codes | Code | Description | | ---- | ----------- | -| 200 OK | The response body contains the raw `ApplicationDeployment` YAML (`Content-Type: application/yaml`). Server MUST set `ETag` to the quoted digest and MUST return `Cache-Control: private, max-age=31536000, immutable` (see [Protocol - ETag and Caching](#protocol-etag-and-caching)). | +| 200 OK | The response body contains the raw `ApplicationDeployment` YAML (`Content-Type: application/yaml`). Server MUST set `ETag` to the quoted digest and MUST return `Cache-Control: private, max-age=31536000, immutable` (see [Protocol: ETag and Caching](#protocol-etag-and-caching)). | | 403 Forbidden | The request is not authorized by the WFM's local policy (for example, the client relationship has been retired; see [Authorization](../identity/wfm-identity-profile.md#authorization)). | | 404 Not Found | The referenced digest does not exist on the server. `404 Not Found` indicates only that this specific digest is unavailable. It MUST NOT be interpreted as a deletion signal by a client; deletion of workloads is determined solely by absence from the state manifest. | > **Note:** Servers MAY apply HTTP `Content-Encoding` (e.g., gzip, br). The client advertises support via `Accept-Encoding`. Digests and ETags always refer to the decoded representation (i.e., the exact bytes of the response body after decompressing any HTTP `Content-Encoding` such as gzip). Servers SHOULD include `Vary: Accept-Encoding` if compression is used. -## Endpoints - Deployment Bundle +## Endpoints: Deployment Bundle This section defines the API endpoint used by a client to retrieve a compressed bundle containing all `ApplicationDeployment` YAMLs for efficient bulk synchronization. @@ -134,19 +134,19 @@ GET /api/v1/bundles/{digest} | Parameter | Type | Required? | Description | | --------- | ---- | --------- | ----------- | -| `{digest}` | string | Y | Content-addressable digest of the served bundle archive. See [Protocol - Digest](#protocol-digest) for further details. | +| `{digest}` | string | Y | Content-addressable digest of the served bundle archive. See [Protocol: Digest](#protocol-digest) for further details. | ### Response Codes | Code | Description | | ---- | ----------- | -| 200 OK | The bundle was successfully retrieved. The server MUST set `Content-Type` to the manifest-declared `bundle.mediaType`, `ETag` to the quoted digest, and MUST return `Cache-Control: private, max-age=31536000, immutable` (see [Protocol - ETag and Caching](#protocol-etag-and-caching)). | +| 200 OK | The bundle was successfully retrieved. The server MUST set `Content-Type` to the manifest-declared `bundle.mediaType`, `ETag` to the quoted digest, and MUST return `Cache-Control: private, max-age=31536000, immutable` (see [Protocol: ETag and Caching](#protocol-etag-and-caching)). | | 403 Forbidden | The request is not authorized by the WFM's local policy (for example, the client relationship has been retired; see [Authorization](../identity/wfm-identity-profile.md#authorization)). | | 404 Not Found | The referenced digest does not exist on the server. `404 Not Found` indicates only that this specific digest is unavailable. It MUST NOT be interpreted as a deletion signal by a client; deletion of workloads is determined solely by absence from the state manifest. | > **Note:** Servers MAY apply `Content-Encoding` (e.g., gzip, br) and SHOULD include `Vary: Accept-Encoding` if they do. -## Protocol - Digest +## Protocol: Digest All Desired State artifacts - including the manifest, bundle archives, and individual `ApplicationDeployment` YAMLs - use a canonical digest to ensure content integrity and consistency across client and server implementations. @@ -179,7 +179,7 @@ sha256:a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789abcdef > - The digest in this API defines immutable content identity for artifacts, verifiable independently of how they were transported. > - Mutual TLS authenticates both parties and protects the messages that carry those artifacts in transit. -## Protocol - ETag and Caching +## Protocol: ETag and Caching All Desired State endpoints implement standard HTTP caching semantics to optimize synchronization between the Workload Fleet Manager (WFM) and the Workload Fleet Management Client. ETags are used to detect content changes and avoid redundant data transfers. Two caching models are defined: one for the mutable State Manifest, and one for immutable, content-addressable resources such as individual deployments and bundles. @@ -190,7 +190,7 @@ Every response is scoped to the authenticated client, so all Desired State respo For the [State Manifest](#endpoints-state-manifest) endpoint, servers use strong ETags as defined in [RFC 9110 § 8.8.3](https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3). - The `ETag` MUST be a strong validator computed as a digest of the exact serialized JSON response body. - The format MUST follow the digest grammar defined in [Protocol – Digest](#protocol-digest): + The format MUST follow the digest grammar defined in [Protocol: Digest](#protocol-digest): `":"`, for example: `"sha256:a4e01b2c3d..."`. - Servers SHOULD serialize JSON deterministically (for example, per [RFC 8785](https://datatracker.ietf.org/doc/html/rfc8785)) so that logically identical manifests yield identical bytes and therefore identical ETags. From 8122040cf92aa555f0e6d6d8c795099ec91fa0fc Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 13:58:40 +0200 Subject: [PATCH 18/26] Update system-design/specification/margo-management-interface/meta.json Co-authored-by: Philip Presson Signed-off-by: Matthias Lechner --- system-design/specification/margo-management-interface/meta.json | 1 + 1 file changed, 1 insertion(+) diff --git a/system-design/specification/margo-management-interface/meta.json b/system-design/specification/margo-management-interface/meta.json index 653de90b..dcede03c 100644 --- a/system-design/specification/margo-management-interface/meta.json +++ b/system-design/specification/margo-management-interface/meta.json @@ -5,6 +5,7 @@ "device-capabilities", "desired-state", "deployment-status", + "specification-extensions" "workload-management-api-1.0.0" ] } From 8f786dca0978d26bd0e9fff5e461e289708d1816 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 16:10:59 +0000 Subject: [PATCH 19/26] docs: remove Trust Domain identifier as an input delivered by the operator The Trust Domain can be derived from the provisioned SVID alone by inspecting the SPIFFE ID in the cert's URI SAN. Signed-off-by: Matthias Lechner --- system-design/specification/identity/identity-framework.md | 2 +- system-design/specification/identity/tls-requirements.md | 2 +- .../specification/identity/trust-bundle-and-discovery.md | 2 +- .../specification/identity/trust-bundle-api-1.0.0.yaml | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/system-design/specification/identity/identity-framework.md b/system-design/specification/identity/identity-framework.md index 1ac8fc12..8815f7f4 100644 --- a/system-design/specification/identity/identity-framework.md +++ b/system-design/specification/identity/identity-framework.md @@ -39,7 +39,7 @@ MIAF has four moving parts: the **Trust Domain**, the **Margo Identity Service ( Once a component holds an SVID: -1. **Acquire trust material.** The component acquires its Trust Domain's Trust Bundle: it either locates the bundle through the discovery document and retrieves it over HTTPS, or receives the bundle and Trust Domain identifier through operator-provided configuration or out-of-band delivery (see [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap)). +1. **Acquire trust material.** The component acquires its Trust Domain's Trust Bundle: it either locates the bundle through the discovery document and retrieves it over HTTPS, or receives the bundle through operator-provided configuration or out-of-band delivery (see [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap)). 2. **Authenticate to peers.** The component and peer complete an mTLS handshake: the component presents its X.509-SVID, and the peer validates the chain against the Trust Bundle. 3. **Authorize the call.** The peer applies its local policy to the now-verified SPIFFE ID. diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md index d3d28d04..2e66ac72 100644 --- a/system-design/specification/identity/tls-requirements.md +++ b/system-design/specification/identity/tls-requirements.md @@ -25,7 +25,7 @@ A client cannot validate MIAF-issued SVIDs against the Trust Bundle until it hol An operator MAY deliver the trust material for either option (the configured anchors for the first, or the pins for the second) through the same channel used to provision the principal's SVID. A client that cannot authenticate a connection by one of these mechanisms MUST abort. -**Out-of-band delivery.** The operator delivers the Trust Bundle, and the Trust Domain identifier and Trust Bundle URI, directly through the provisioning or deployment channel (see [Bundle contents and distribution](./trust-bundle-and-discovery.md#bundle-contents-and-distribution) and the [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)). No HTTPS retrieval takes place, so there is no bootstrap connection to authenticate; the integrity and authenticity of the delivered material rest on that channel. +**Out-of-band delivery.** The operator delivers the Trust Bundle, and the Trust Bundle URI where later refresh is intended, directly through the provisioning or deployment channel (see [Bundle contents and distribution](./trust-bundle-and-discovery.md#bundle-contents-and-distribution) and the [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)). No HTTPS retrieval takes place, so there is no bootstrap connection to authenticate; the integrity and authenticity of the delivered material rest on that channel. Whichever path is used, a client MUST NOT accept trust material from an unauthenticated source, and MUST NOT treat the first acquisition as "trust on first use". Once acquired, the discovery document (if used) and the Trust Bundle are MIAF's authoritative sources; that bundle then validates SVIDs within the Trust Domain. diff --git a/system-design/specification/identity/trust-bundle-and-discovery.md b/system-design/specification/identity/trust-bundle-and-discovery.md index 18ccfb0d..0673ec39 100644 --- a/system-design/specification/identity/trust-bundle-and-discovery.md +++ b/system-design/specification/identity/trust-bundle-and-discovery.md @@ -10,7 +10,7 @@ A machine-readable description of both endpoints is available as the [Trust Bund The discovery document is an optional entry point to a Trust Domain that points a client to the Trust Bundle URI. Each document describes exactly one Trust Domain. -When discovery is used, an origin serving exactly one Trust Domain SHOULD expose the document at `GET /.well-known/margo` per [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615); an origin serving several Trust Domains MAY use other absolute HTTPS URLs. When discovery is not used, the Trust Domain identifier and Trust Bundle URI are supplied by operator-provided configuration. +When discovery is used, an origin serving exactly one Trust Domain SHOULD expose the document at `GET /.well-known/margo` per [RFC 8615](https://datatracker.ietf.org/doc/html/rfc8615); an origin serving several Trust Domains MAY use other absolute HTTPS URLs. When discovery is not used, the Trust Bundle URI is supplied by operator-provided configuration. The endpoint requires no authentication at the application layer; the transport is authenticated per [initial trust bootstrap](./tls-requirements.md#initial-trust-bootstrap). diff --git a/system-design/specification/identity/trust-bundle-api-1.0.0.yaml b/system-design/specification/identity/trust-bundle-api-1.0.0.yaml index f32cf131..21d57a43 100644 --- a/system-design/specification/identity/trust-bundle-api-1.0.0.yaml +++ b/system-design/specification/identity/trust-bundle-api-1.0.0.yaml @@ -34,8 +34,7 @@ paths: An origin serving exactly one Trust Domain SHOULD expose the document at this well-known path per RFC 8615; an origin serving several Trust Domains MAY use other absolute HTTPS URLs. When discovery is not used, - the Trust Domain identifier and Trust Bundle URI are supplied by - operator-provided configuration. + the Trust Bundle URI is supplied by operator-provided configuration. parameters: - name: If-None-Match in: header From 7581b390f182b69e3c77c2f97a5b89e311bb8c7e Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 16:12:35 +0000 Subject: [PATCH 20/26] docs: clarify that issuance overrides any SAN content in the CSR Signed-off-by: Matthias Lechner --- system-design/specification/identity/identity-lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/identity-lifecycle.md b/system-design/specification/identity/identity-lifecycle.md index 307015ed..aaf86a06 100644 --- a/system-design/specification/identity/identity-lifecycle.md +++ b/system-design/specification/identity/identity-lifecycle.md @@ -23,7 +23,7 @@ The Trust Bundle and discovery document are the only runtime endpoints MIAF defi For **enrollment**, the operator: 1. accepts a CSR from the principal (the preferred path, since it keeps the private key on the principal and supports hardware-bound keys such as a TPM, secure element, or HSM). Where the principal cannot generate its own key pair, the operator generates one centrally and accepts the resulting concentration of key custody; -2. mints an X.509-SVID for the chosen SPIFFE ID under the Trust Domain's issuing authority; +2. mints an X.509-SVID for the chosen SPIFFE ID under the Trust Domain's issuing authority. The issuance is authoritative for the SPIFFE ID: any subject or SAN content the CSR carries is advisory and is overridden; 3. installs the SVID on the principal over a channel that protects its integrity and authenticity, and that additionally protects confidentiality on the path where it also carries the centrally generated private key; and 4. ensures every relying party the principal will authenticate to has the Trust Bundle and any local-policy entries needed to recognize the new SPIFFE ID. From e8b4e2de0fb79b2c004fe22521193d0012599a02 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 16:13:32 +0000 Subject: [PATCH 21/26] docs: add a cross-check requirement for discovery Signed-off-by: Matthias Lechner --- .../specification/identity/trust-bundle-and-discovery.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system-design/specification/identity/trust-bundle-and-discovery.md b/system-design/specification/identity/trust-bundle-and-discovery.md index 0673ec39..8480a99d 100644 --- a/system-design/specification/identity/trust-bundle-and-discovery.md +++ b/system-design/specification/identity/trust-bundle-and-discovery.md @@ -46,6 +46,8 @@ The path above is the default convention; an origin serving several Trust Domain A client MUST ignore unknown fields in the discovery document. +A client that already holds an SVID SHOULD verify that the document's `trustDomain` matches the trust domain of its own SPIFFE ID and treat a mismatch as a configuration error; on an origin serving several Trust Domains, this check is what catches a client pointed at the wrong Trust Domain's document. + ### Example Discovery Document Response Request: From f2d18176228b09744153020c1d5f5de858740536 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 24 Jul 2026 16:24:47 +0000 Subject: [PATCH 22/26] docs: define pin semantics for pinned trust (SPKI key pinning, not cert pinning) Signed-off-by: Matthias Lechner --- system-design/specification/identity/tls-requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md index 2e66ac72..7007bf53 100644 --- a/system-design/specification/identity/tls-requirements.md +++ b/system-design/specification/identity/tls-requirements.md @@ -21,7 +21,7 @@ A client cannot validate MIAF-issued SVIDs against the Trust Bundle until it hol **Authenticated HTTPS retrieval.** The client fetches the discovery document and the Trust Bundle over HTTPS (see [Trust Bundle and Discovery](./trust-bundle-and-discovery.md)). Because the client holds no MIAF credential yet, these connections rely on an initial trust mechanism established outside MIAF. The client MUST authenticate both connections using at least one of: 1. **PKI-anchored validation**: validate the [MIS](./identity-framework.md#the-mis-role) server certificate chain to a configured set of trust anchors (web PKI, enterprise PKI, or an operator-configured private CA), with DNS name validation per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). -2. **Pinned trust**: validate the MIS server certificate chain or public key against operator-provisioned pins (for example, a pinned CA certificate). +2. **Pinned trust**: validate the MIS server certificate chain against operator-provisioned pins. A pin is the base64-encoded SHA-256 digest of the DER-encoded SubjectPublicKeyInfo of a certificate (the SPKI Fingerprint construction of [RFC 7469, Section 2.4](https://datatracker.ietf.org/doc/html/rfc7469#section-2.4)). The connection is authenticated when a certificate in the presented chain carries a SubjectPublicKeyInfo matching a provisioned pin (for example, a pin over the issuing CA's public key). An operator MAY deliver the trust material for either option (the configured anchors for the first, or the pins for the second) through the same channel used to provision the principal's SVID. A client that cannot authenticate a connection by one of these mechanisms MUST abort. From ae053af1e0f6ed27f1b33d441bf5d410374fc414 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 30 Jul 2026 12:15:19 +0000 Subject: [PATCH 23/26] docs: cite RFC 9525 for name validation, obsoleting RFC 6125 Signed-off-by: Matthias Lechner --- system-design/specification/identity/tls-requirements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system-design/specification/identity/tls-requirements.md b/system-design/specification/identity/tls-requirements.md index 7007bf53..5a315f19 100644 --- a/system-design/specification/identity/tls-requirements.md +++ b/system-design/specification/identity/tls-requirements.md @@ -20,7 +20,7 @@ A client cannot validate MIAF-issued SVIDs against the Trust Bundle until it hol **Authenticated HTTPS retrieval.** The client fetches the discovery document and the Trust Bundle over HTTPS (see [Trust Bundle and Discovery](./trust-bundle-and-discovery.md)). Because the client holds no MIAF credential yet, these connections rely on an initial trust mechanism established outside MIAF. The client MUST authenticate both connections using at least one of: -1. **PKI-anchored validation**: validate the [MIS](./identity-framework.md#the-mis-role) server certificate chain to a configured set of trust anchors (web PKI, enterprise PKI, or an operator-configured private CA), with DNS name validation per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). +1. **PKI-anchored validation**: validate the [MIS](./identity-framework.md#the-mis-role) server certificate chain to a configured set of trust anchors (web PKI, enterprise PKI, or an operator-configured private CA), with DNS name validation per [RFC 9525](https://datatracker.ietf.org/doc/html/rfc9525). 2. **Pinned trust**: validate the MIS server certificate chain against operator-provisioned pins. A pin is the base64-encoded SHA-256 digest of the DER-encoded SubjectPublicKeyInfo of a certificate (the SPKI Fingerprint construction of [RFC 7469, Section 2.4](https://datatracker.ietf.org/doc/html/rfc7469#section-2.4)). The connection is authenticated when a certificate in the presented chain carries a SubjectPublicKeyInfo matching a provisioned pin (for example, a pin over the issuing CA's public key). An operator MAY deliver the trust material for either option (the configured anchors for the first, or the pins for the second) through the same channel used to provision the principal's SVID. A client that cannot authenticate a connection by one of these mechanisms MUST abort. @@ -38,7 +38,7 @@ Validity-period evaluation depends on a trustworthy local clock (see [unreliable **Server identity for MIAF HTTPS endpoints (discovery and Trust Bundle retrieval):** - The client MUST validate the server certificate chain to its configured initial trust anchors (see [Initial Trust Bootstrap](#initial-trust-bootstrap)). -- Under PKI-anchored validation, the client MUST validate the expected DNS name per [RFC 6125](https://datatracker.ietf.org/doc/html/rfc6125). Under pinned trust, the pin itself establishes server identity, so RFC 6125 DNS-name validation applies only where the client connects by a DNS name; a client connecting to a pinned endpoint by IP address is not required to perform it. +- Under PKI-anchored validation, the client MUST validate the expected DNS name per [RFC 9525](https://datatracker.ietf.org/doc/html/rfc9525). Under pinned trust, the pin itself establishes server identity, so RFC 9525 DNS-name validation applies only where the client connects by a DNS name; a client connecting to a pinned endpoint by IP address is not required to perform it. - The Trust Bundle selected from `trustBundleUri` MUST NOT replace these TLS server-validation checks for MIAF HTTPS endpoints; it is used to validate SVIDs within the Trust Domain. **SVID identity for MIAF mTLS:** when a peer presents an X.509-SVID at the mTLS layer, the verifier validates it and derives the peer's identity per the [X.509-SVID validation](./svids.md#x509-svid-validation) rules. SVID identity is established by the SPIFFE ID in the URI SAN, not by a DNS name. From 35128226c3975188c10620b64ee2c3d9bd878f18 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 30 Jul 2026 12:43:00 +0000 Subject: [PATCH 24/26] docs: define when a retrieved Trust Bundle replaces the current one Signed-off-by: Matthias Lechner --- .../specification/identity/trust-bundle-and-discovery.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system-design/specification/identity/trust-bundle-and-discovery.md b/system-design/specification/identity/trust-bundle-and-discovery.md index 8480a99d..621382f8 100644 --- a/system-design/specification/identity/trust-bundle-and-discovery.md +++ b/system-design/specification/identity/trust-bundle-and-discovery.md @@ -122,6 +122,8 @@ Response (`200 OK`): A client uses the retrieved bundle as the authoritative source when validating SVIDs issued within the Trust Domain. A client that retrieves a bundle carrying no X.509 trust anchors MUST reject it and MUST NOT validate SVIDs against it, failing closed rather than proceeding with an empty anchor set. To resist rollback, where the bundle carries `spiffe_sequence` a client SHOULD track the highest value it has accepted for the Trust Domain and SHOULD reject a retrieved bundle whose `spiffe_sequence` has regressed, since a lower value signals a stale, cached, or replayed bundle that could re-admit a trust anchor that was retired to revoke a compromise. +A client MUST NOT validate SVIDs against a retrieved bundle it rejects (see the checks above). A client MUST keep validating SVIDs against its current bundle until it accepts a retrieved bundle. A failed or rejected retrieval therefore does not change the trust material the client uses. When a client accepts a retrieved bundle, the client MUST NOT combine trust anchors from its current bundle with trust anchors from the retrieved bundle. The trust anchors in the retrieved bundle replace all trust anchors the client used before. An anchor the operator removed from the published bundle to revoke a compromise would otherwise stay in use (see step 4 of the [trust anchor rotation playbook](./identity-lifecycle.md#trust-anchor-rotation-playbook)). + A client SHOULD refresh its cached bundle at the interval given by the bundle's `spiffe_refresh_hint`, when present, and otherwise at an operator-configured interval. This refresh cadence is authoritative: HTTP cache revalidation (`If-None-Match`/`304`, and any `Cache-Control` freshness) is an efficiency optimization within it and MUST NOT defer a refresh the interval requires. The refresh interval bounds how quickly a Trust Bundle rotation reaches the fleet; the [trust anchor rotation playbook](./identity-lifecycle.md#trust-anchor-rotation-playbook) depends on it. ## Bundle Contents and Distribution From 1fe82650b4d1635d203e0eefcb2ce30331d47178 Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Thu, 30 Jul 2026 13:13:24 +0000 Subject: [PATCH 25/26] docs: distinguish authorization withdrawal from certificate revocation Signed-off-by: Matthias Lechner --- system-design/specification/identity/identity-lifecycle.md | 6 +++--- .../specification/identity/wfm-identity-profile.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/system-design/specification/identity/identity-lifecycle.md b/system-design/specification/identity/identity-lifecycle.md index aaf86a06..719dcc0b 100644 --- a/system-design/specification/identity/identity-lifecycle.md +++ b/system-design/specification/identity/identity-lifecycle.md @@ -35,13 +35,13 @@ The provisioning channel itself is deployment-specific and out of scope. Typical ## Operator Revocation Playbook -Without an automated revocation protocol, a deployment revokes an SVID through one of: +Without an automated revocation protocol, a deployment withdraws an SVID's access through one of: -1. **Relying-party allowlist removal**: where a relying party keeps an allowlist of the identities it accepts, the operator removes the SPIFFE ID from it. This is the most precise option - it revokes one principal's access without affecting any other - and is recommended for routine revocation where such a list exists. +1. **Relying-party allowlist removal**: where a relying party keeps an allowlist of the identities it accepts, the operator removes the SPIFFE ID from it. This withdraws authorization at the application layer - the certificate itself is not revoked and stays valid until it expires. It is the most precise option - it removes one principal's access without affecting any other - and is recommended for routine use where such a list exists. 2. **Trust Bundle rotation**: the operator removes the compromised trust anchor from the Trust Bundle, invalidating every SVID that chains to it. This is heavy-handed but effective when an entire issuance authority is compromised. See the [Trust Anchor Rotation Playbook](#trust-anchor-rotation-playbook) below, which also covers the case where the issuer is an intermediate CA whose anchor is the root above it. 3. **Expiry**: wait for the SVID to expire. This is viable only with shorter SVID lifetimes. -None of these options is instantaneous. Allowlist removal takes effect promptly only where the relying party re-evaluates its authorization policy per request; a Trust Bundle rotation propagates no faster than the fleet's refresh interval. In either case a long-lived mTLS connection can keep a revoked peer authenticated until the connection is re-established (see [session lifetime and re-validation](./tls-requirements.md#session-lifetime-and-re-validation)). +None of these options is instantaneous. Allowlist removal takes effect promptly only where the relying party re-evaluates its authorization policy per request; a Trust Bundle rotation propagates no faster than the fleet's refresh interval. In either case a long-lived mTLS connection can keep the affected peer authenticated until the connection is re-established (see [session lifetime and re-validation](./tls-requirements.md#session-lifetime-and-re-validation)). ## Trust Anchor Rotation Playbook diff --git a/system-design/specification/identity/wfm-identity-profile.md b/system-design/specification/identity/wfm-identity-profile.md index adf87b3d..fcf4d2c4 100644 --- a/system-design/specification/identity/wfm-identity-profile.md +++ b/system-design/specification/identity/wfm-identity-profile.md @@ -112,7 +112,7 @@ The MIAF [lifecycle vocabulary](./identity-lifecycle.md#lifecycle-vocabulary) ap WFM revocation is heavier-handed than WFM Client revocation because there is no client-side accepted-server allowlist comparable to the WFM's accepted-client policy. An operator reissues the WFM SVID (keeping the same SPIFFE ID) in most cases; Trust Bundle rotation is the cryptographically enforced revocation path. -Removing a `wfm-client-id` revokes one client only where the accepted-client policy lists clients individually. Where a WFM instead accepts any client within its namespace (see [Authorization](#authorization)), there is no per-client entry to remove, so revoking a single client requires narrowing the policy to explicit entries or rotating the Trust Bundle. +Removing a `wfm-client-id` withdraws one client only where the accepted-client policy lists clients individually. Where a WFM instead accepts any client within its namespace (see [Authorization](#authorization)), there is no individual entry to remove, so withdrawing a single client requires narrowing the policy to explicit entries or rotating the Trust Bundle. ## Authorization From 328608b4f83d590a7c1d3b7bcacb2854db8bd34a Mon Sep 17 00:00:00 2001 From: Matthias Lechner Date: Fri, 31 Jul 2026 07:27:31 +0000 Subject: [PATCH 26/26] docs: attribute SVID algorithm choices to the chain and the leaf key Signed-off-by: Matthias Lechner --- system-design/specification/identity/svids.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-design/specification/identity/svids.md b/system-design/specification/identity/svids.md index 941b6ad0..d1520e59 100644 --- a/system-design/specification/identity/svids.md +++ b/system-design/specification/identity/svids.md @@ -21,7 +21,7 @@ This section constrains the signature algorithms and key parameters used for SVI | **RSA (≥3072 + SHA-256)** | Modulus MUST be at least 3072 bits; signatures MUST use SHA-256. RSASSA-PSS (`PS256`, [RFC 8017](https://datatracker.ietf.org/doc/html/rfc8017)) is RECOMMENDED. RSASSA-PKCS#1 v1.5 (`RS256`) MAY be used only for X.509 certificate and CSR signatures (today the only RSA signatures in MIAF), for compatibility with issuing CAs that cannot produce PSS signatures (its use there remains sanctioned by [NIST SP 800-131A Rev 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf)). Any other RSA signature MIAF defines later MUST use PSS. | - **ECDSA P-256 with SHA-256 is mandatory to implement.** Every MIAF component MUST implement it, for both presenting and validating SVIDs, so that any two components always share at least one algorithm. -- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. Because a principal presents a single SVID bound to one signature algorithm, an SVID issued with a non-mandatory algorithm can be validated only by peers that implement that algorithm; a peer that does not will fail to validate it. Selecting an SVID signature algorithm that is supported across the Trust Domain is therefore the operator's responsibility at issuance (see [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)). ECDSA P-256 with SHA-256 is always a safe choice. +- A component MAY additionally implement EdDSA (Ed25519), RSA, or both. Whether a peer can accept a presented SVID depends on two kinds of algorithm choice. The signature algorithm of each certificate in the chain is chosen by its issuing CA, and the peer needs it to validate the chain. The public-key algorithm of the leaf key is chosen when the key pair is generated, and the peer needs it to verify the TLS handshake signature. A peer that does not implement every algorithm involved fails to authenticate the presenter. Selecting algorithms supported across the Trust Domain, for the principal's key and for every CA in the chain, is therefore the operator's responsibility at issuance (see [operator provisioning playbook](./identity-lifecycle.md#operator-provisioning-playbook)). ECDSA P-256 with SHA-256 throughout is always a safe choice. - A component that validates SVIDs MUST validate every algorithm it accepts from peers. For RSA, this includes accepting both PSS and PKCS#1 v1.5 signatures on certificates. > **Crypto-agility (informative):** MIAF names its algorithms explicitly so the permitted set can evolve. The set above is classical; post-quantum signature suites are expected to arrive as additional permitted algorithms rather than a redesign of the framework. Defaulting the transport to TLS 1.3 (see [TLS requirements](./tls-requirements.md)) supports this: it is the version track on which post-quantum key exchange and authentication are being standardized.