SPIFFE Filesystem Delivery

This document is part of the SPIFFE standard. The canonical version can be found at https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Filesystem_Delivery.md

Stability: Incubating - see STABILITY.md.

Status of this Memo

This document specifies an identity API standard for the internet community, and requests discussion and suggestions for improvements. Distribution of this document is unlimited.

Abstract

Many applications run in environments where SPIFFE certificates and trust bundles are more easily delivered via the filesystem, rather than the SPIFFE Workload API. This specification gives applications and identity provisioning systems a concrete target to aim for:

  • How identity provisioning systems should write SPIFFE SVIDs.
  • How identity provisioning systems should write SPIFFE trust bundles for the local and any federated trust domains.
  • How applications should read them.

As a concrete target, all of the features required by this specification are natively available in Kubernetes using the Pod Certificates and Cluster Trust Bundles features.

Table of Contents

1. Participants
2. Credential Folder
2.1. Credential Bundle
2.2. SPIFFE Trust Bundles
2.3. Redaction
3. Application Behavior
3.1. Loading and Refreshing the Credential Bundle
3.2. Validating Peer SPIFFE Certificates
Appendix A. Filesystem Delivery Versus the Workload API
Appendix B. Reference Example - Filesystem Delivery on Kubernetes

1. Participants

Provisioning System: The software stack responsible for distributing X.509 SVIDS and trust domain federation configuration to individual applications.

For applications running in a Kubernetes cluster, this might be composed of kubelet, kube-apiserver, and a SPIFFE signer plugin, working through the Pod Certificates mechanism to deliver certificates into individual application pod filesystems.

For applications running directly on a machine or VM, this may be a system daemon or node agent that writes X.509 SVIDs to a particular folder in the filesystem.

Application: An individual application that expects to:

  • Load an X.509 SVID from the filesystem, and use it to connect to other peers.
  • Verify X.509 SVIDs presented by peers, including peers in different trust domains.

2. Credential Folder

A credential folder contains:

  • A single credential bundle, named credential-bundle.private-key.x509.pem
  • One or more trust bundles, named <trust-domain>.spiffe-trust-bundle.x509.pem

A credential folder MAY contain other files and directories, used by the provisioning system for internal tracking or platform-specific features. The application SHOULD ignore this additional content unless it is coordinated with the provisioning system to understand the content’s meaning.

Note that a credential folder carries only a single SPIFFE X.509 SVID. If a provisioning system wants to provide multiple SPIFFE identities to an application, it must provide multiple SPIFFE credential folders. The provisioning system and application must be coordinated so that the application knows where to find the additional credential folders, and in which circumstances they should be used.

This revision of the specification covers a single X.509 SVID per credential folder. Multiple SVIDs within one folder, credential types other than X.509 SVIDs, the SPIFFE Workload API’s SVID hints, and its notion of a default identity are not supported. Because a credential folder MAY contain other files and directories, a provisioning system and application that are coordinated with each other MAY use additional files to cover these cases; this specification does not define how, and may define a standard mechanism in a future revision.

Because the credential folder contains sensitive credentials, provisioning systems SHOULD ensure that their contents do not appear on physical disks or in automatic backups.

2.1 Credential Bundle

A credential bundle file contains the application’s private key and certificate chain. Combining these together into one file ensures that the provisioning system can rotate the key and certificate atomically. (Note that, for safety, the application must also read this file atomically, as described in section 3.1).

The credential bundle consists of two or more PEM blocks. The first block MUST be of type PRIVATE KEY, and MUST contain a PKCS#8-serialized private key.

Compatibility Note: Some provisioning systems offer features for keeping the private key inaccessible to the application, only exposing RPCs for signing operations over the private key. When a feature like this is in use, the credential bundle might omit the private key. The provisioning system and application must be coordinated to make this happen. SPIFFE-compliant applications are not required to seamlessly handle this case.

The remaining PEM blocks MUST be of type CERTIFICATE, and contain the application’s certificate chain, in leaf-to-root order. The leaf certificate must be issued to the public key derived from the PRIVATE KEY block. The leaf certificate SHOULD be an X.509 SVID.

The certificate chain MUST NOT contain the trust anchor (“root certificate”) of the chain. It MUST stop at the certificate preceding the trust anchor.

The credential bundle:

  • MUST NOT contain any other PEM block types.
  • MUST NOT contain any inter-block data.
  • MUST NOT contain any PEM blocks that have PEM headers.

The provisioning system SHOULD update the credential bundle on the filesystem with a new key and certificate chain before the leaf certificate currently in the bundle expires, with enough additional time for the application to detect and reload the updated credential bundle. The provisioning system SHOULD make these updates atomically, so that an application reading the credential bundle reads either the complete old content or the complete new content, with no intermediate state.

The developer SHOULD NOT copy this file anywhere, since compromise of the credential-bundle.private-key.x509.pem file could compromise the security of TLS sessions. The provisioning system SHOULD be configured to limit access to the credential-bundle.private-key.x509.pem file to only the intended consumer(s).

2.2 SPIFFE Trust Bundles

The credential folder will contain one or more trust bundles, one for the workload’s own trust domain and one per trust domain that the provisioning system is configured to federate with.

Each trust bundle file MUST be named following the pattern “.spiffe-trust-bundle.x509.pem”. The file contents are one or more PEM CERTIFICATE blocks, each with a PKIX-serialized certificate that is a trust anchor for the trust domain. In most cases, a trust anchor is a root certificate, although in rare cases it may be an intermediate certificate.

A trust bundle file:

  • MUST NOT contain any other PEM block types.
  • MUST NOT contain any inter-block data.
  • MUST NOT contain any PEM blocks that have PEM headers.

The provisioning system SHOULD update the trust bundles on the filesystem as the system’s federation configuration changes. The provisioning system SHOULD make these updates atomically, so that an application reading a trust bundle reads either the complete old content, or the complete new content, with no intermediate state.

The certificates in a SPIFFE trust bundle file should only be used by SPIFFE-aware applications. It is not generally safe to attempt to use any of these certificates for standard server TLS certificate verification. In particular, it is never safe to combine the contents of more than one SPIFFE trust bundle into a single, undifferentiated bag of root certificates.

2.3 Redaction

Over time, a provisioning system may need to withdraw something it previously supplied: a trust domain may be de-federated, a trust anchor may be retired from a bundle, or an application may cease to be entitled to an SVID. This section defines how the provisioning system signals that, and how the application is expected to interpret it.

Any of the files defined by this specification MAY be present but empty. An empty file is a deliberate redaction: the provisioning system is indicating that the credential or trust bundle the file would otherwise hold is no longer available to the application. The application SHOULD interpret an empty file as a redaction rather than as malformed content. As an example, if an application has loaded a trust bundle for trust domain X, and X.spiffe-trust-bundle.x509.pem later becomes empty, then that bundle SHOULD be unloaded.

Redaction is not limited to whole files. If the provisioning system removes a trust anchor from a trust bundle, leaving the file in place and its remaining anchors intact, the application SHOULD stop using the removed anchor on the same terms as it would stop using a redacted file.

Provisioning systems SHOULD redact by writing an empty file, rather than by removing the file from the credential folder. A file that is absent from the folder may instead be an artifact of the folder being remounted or repopulated, so emptiness is a stronger signal of intent than absence.

3. Application Behavior

This specification does not define how an application discovers the path of its credential folder, and defines no default or well-known location for it. That path is agreed out-of-band between the provisioning system and the application, for example through application-specific configuration, as is any filesystem permission needed to keep a credential folder readable only by its intended workload.

3.1 Loading and Refreshing the Credential Bundle

Before the application can use its SPIFFE credentials, it needs to load the credential bundle from the filesystem.

When loading the credential bundle, the application SHOULD complete the load in a single open/read/close cycle. In particular, the application SHOULD NOT have one open/read/close cycle to read the private key, and another open/read/close cycle to read the certificate chain.

Note: If the application does use more than one open/read/close cycle, then it is possible that the application may load a mismatched private key and certificate, if the provisioning system rotated the credentials between the two open/read/close cycles. This is true even if the provisioning system atomically writes the updated content.

The provisioning system may periodically update the credential bundle on the filesystem. The application MUST reload the credential bundle as soon as reasonably possible after the provisioning system updates it. The application SHOULD NOT assume that the updated bundle will have any commonality with the previous bundle. For example, the type of the private key may be different, or the certificate may be issued from a different root, with different intermediates. The provisioning system may also redact the credential bundle entirely, as described in section 2.3.

3.2 Validating Peer SPIFFE Certificates

In order to verify a SPIFFE X.509 SVID presented by a peer, the application needs to determine the correct SPIFFE trust bundle for the peer’s trust domain.

Once the application has parsed the peer’s trust domain from the unverified peer X.509 SVID, the application should load the corresponding trust domain’s trust bundle from the credential folder. If no trust bundle file is present for the peer’s trust domain, the application SHOULD assume that SPIFFE federation has not been configured with the peer, and fail to verify the peer’s certificate.

The provisioning system may periodically update the number and contents of the per-trust-domain trust bundles in the filesystem. The application MAY cache the presence, absence, and contents of each trust domain’s trust bundle in memory, but it SHOULD reflect updates from the filesystem as soon as reasonably possible.

The application SHOULD handle de-federation; if it has loaded a trust bundle for trust domain X, and X’s trust bundle is subsequently redacted as described in section 2.3, the application SHOULD begin failing to verify certificates for trust domain X as soon as reasonably possible.

As mentioned in section 2.2, it is not safe to use the contents of a SPIFFE trust bundle file except by a SPIFFE-aware application performing SPIFFE X.509 SVID verification for a certificate from the corresponding trust domain. Applications should NEVER unify the contents of multiple *.spiffe-trust-bundle.x509.pem files into a single root store for certificate verification, including by treating the entire SPIFFE credential folder as a root certificate store. Doing this allows for workloads in one trust domain to impersonate workloads in another trust domain.

Appendix A: Filesystem Delivery Versus the Workload API

There are currently two standards for SPIFFE-conforming applications to retrieve credentials and trust bundles: the Workload API, and Filesystem Delivery. These standards, broadly speaking, cover the same functionality. The Workload API predates Filesystem Delivery, and is the preferred solution, all else equal.

Filesystem Delivery exists because, in certain computing environments, such as Kubernetes, it is easier for the environment to furnish files to the workload, rather than making a TCP or Unix socket service available. It’s also lower effort to read files from workloads, but, as described in section 3.1, it still requires care.

Given these two choices, which should you use?

If you are writing or maintaining an application that needs to load and use SPIFFE credentials, you should ideally support both the Workload API and Filesystem Delivery. This specification does not define how an application selects between them; that selection is part of the same out-of-band configuration that supplies the credential folder path.

Appendix B: Reference Example - Filesystem Delivery on Kubernetes

This appendix is a non-normative example. It adds no requirements to this specification, and a conforming implementation may arrange these primitives differently. In particular, the ClusterTrustBundle label scheme below is illustrative only; this specification does not standardize it.

Kubernetes provides two built-in mechanisms that can be used together to implement the SPIFFE Filesystem Delivery API:

  • Pod Certificates are a pluggable mechanism to issue and automatically refresh certificates to application pods. Each pod gets an independent private key, which never leaves the node where the pod is running. The private key and certificate chain can be written into a credential bundle in the pod’s filesystem.
  • Cluster Trust Bundles are bags of root certificates with a unique name. They can be mounted into a pod’s filesystem, and the content in the filesystem automatically updates as the Cluster Trust Bundles are updated.

For example, we could implement a controller that handles the signer name spiffe.example/identity. This controller would have two responsibilities:

  • Respond to PodCertificateRequests from pods that want a certificate from the spiffe.example/identity. The precise details of the issued certificate are up to the controller. In this case, assume that it is configured to issue certificates with SPIFFE IDs like spiffe://a.myorg.example/ns/<namespace>/sa/<service-account>.
  • Maintain a set of ClusterTrustBundles associated with spiffe.example/identity. There are many potential ways to structure the set of ClusterTrustBundles, but one that would work is to use labels to divide them:
    • k8s.spiffe.io/canarying: Possible values of live or preview. Most application pods would always select ClusterTrustBundles with the value live, but some may be configured to select preview, so that actions like rotating a CA or federating with a new trust domain can be previewed on some fraction of your application fleet.
    • k8s.spiffe.io/workload-trust-domain: The label’s value is a trust domain X; workloads that are part of X should load this trust bundle.
    • k8s.spiffe.io/peer-trust-domain: The label’s value is a trust domain Y; workloads that are communicating with a peer in Y should load this trust bundle.

Note: Drawing a distinction between a workload’s trust domain and its peer’s trust domain is necessary (but non-obvious) when workloads from multiple trust domains can be mixed together in one cluster. Each trust domain should have its own federation configuration; given three trust domains A, B, and C, if A is federated with C, workloads in B should not automatically also be federated just because they happen to run in the same Kubernetes cluster.

This information can then be mounted into an application pod in a way that forms a valid SPIFFE credential folder, with an environment variable that the application understands. That variable is specific to the application; this specification does not define one.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: client
  namespace: spiffe-example
  labels:
    app: client
spec:
  replicas: 1
  selector:
    matchLabels:
      app: client
  template:
    metadata:
      labels:
        app: client
    spec:
      restartPolicy: Always
      containers:
      - name: client
        image: debian
        command: ["sleep", "infinity"]
        env:
        - name: SPIFFE_CREDENTIAL_FOLDER
          value: /run/workload-spiffe-credentials
        volumeMounts:
        - name: spiffe-credentials
          mountPath: /run/workload-spiffe-credentials
          readOnly: true
      volumes:
      - name: spiffe-credentials
        projected:
          sources:
          - clusterTrustBundle:
              signerName: spiffe.example/identity
              labelSelector:
                matchLabels:
                  "k8s.spiffe.io/canarying":             "live"
                  "k8s.spiffe.io/workload-trust-domain": "a.myorg.example"
                  "k8s.spiffe.io/peer-trust-domain":     "a.myorg.example"
              path: a.myorg.example.spiffe-trust-bundle.x509.pem
          # This workload needs to federate with b.myorg.example, so it needs the appropriate bundle.
          - clusterTrustBundle:
              signerName: spiffe.example/identity
              labelSelector:
                matchLabels:
                  "k8s.spiffe.io/canarying":             "live"
                  "k8s.spiffe.io/workload-trust-domain": "a.myorg.example"
                  "k8s.spiffe.io/peer-trust-domain":     "b.myorg.example"
              path: b.myorg.example.spiffe-trust-bundle.x509.pem
          - podCertificate:
              signerName: spiffe.example/identity
              keyType: ECDSAP256
              credentialBundlePath: credential-bundle.private-key.x509.pem