Beyond the SPIRE config file: managing trust domains at scale
Every time a new Kubernetes cluster needs to join a SPIRE trust domain, someone files a ticket. The platform team picks it up between other work. The cluster waits. If the config entry goes in wrong — wrong namespace, wrong cluster name — the SPIRE agent fails attestation, the application team escalates, and the round-trip starts again.
This isn't a flaw in SPIRE — it was never designed to be a multi-cluster control plane. But as deployments grow, the gap becomes operational drag. Cofide's Connect picks up where SPIRE's scope ends: moving the cluster registry out of a static config file and into a purpose-built control plane built to manage it and provide control and governance of workload identity at scale.
SPIFFE, SPIRE, and trust domains
If you're already running SPIRE in production, skip ahead. For everyone else: SPIFFE is the open standard for workload identity, where each workload gets a cryptographic identity (a SPIFFE ID) that lives within a trust domain (e.g. spiffe://prod.example.com). SPIRE is the reference implementation, running as a server-agent pair that handles attestation and certificate issuance.
When workloads in different Kubernetes clusters need to mutually authenticate, one option is to federate separate trust domains together, but that adds operational complexity and works best when there's a genuine organisational or security boundary between them. If you want a simpler model where multiple clusters share a single trust domain, you give them a common trust root. That's the scenario this post covers, and it's where the SPIRE server needs to know about all of them.
What "knowing about" a cluster actually means
SPIRE uses the Projected Service Account Token (PSAT) node attestor to verify that a node joining a cluster is who it claims to be. The server cross-references attestation requests against a static list of clusters it trusts:
NodeAttestor "k8s_psat" {
plugin_data {
clusters = {
"cluster-west" = {
service_account_allow_list = ["spire:spire-agent"]
kube_config_file = "/run/spire/kubeconfigs/cluster-west.conf"
}
"cluster-east" = {
service_account_allow_list = ["spire:spire-agent"]
kube_config_file = "/run/spire/kubeconfigs/cluster-east.conf"
}
}
}
}That clusters stanza (sitting in a ConfigMap) is the source of truth for which clusters can participate in the trust domain. There's no scoped API for it: you edit the ConfigMap directly, which means any permission to add a cluster is also permission to change everything else the SPIRE server depends on.
Each cluster entry also references a kubeconfig file that the SPIRE server uses to reach that cluster's API server during attestation. In open-source SPIRE, managing those files is left to the operator: create them, mount them into the SPIRE server, and keep their credentials current. Those credentials — a static service account token, a client certificate, or a PSAT from the server's own cluster — all need rotating. That's a second operational surface on top of the config management problem.
Adding a cluster means editing that ConfigMap. Using the official Helm chart, the edit triggers a rolling replacement of SPIRE server pods. Ten minutes of work, if you know what you're doing. The problem isn't the ten minutes — it's who those ten minutes belong to.
The ownership problem
In most organisations, a SPIRE deployment is typically owned by a platform team. Application teams don't have write access to it, and shouldn't. So every new cluster — a new region, a staging environment, an ephemeral CI cluster — starts with a ticket. Two clusters: fine. Ten: a queue. Twenty: a process that makes the platform team a bottleneck for work that has nothing to do with their actual responsibilities.
How Cofide Connect changes the ownership model
The core architectural shift: the SPIRE server no longer owns its cluster list. Connect does.
The server queries Connect for its configuration at runtime. When a cluster needs to join the trust domain, it's registered in Connect; the server picks it up without a restart, without a ConfigMap edit, without anyone needing privileged access to the SPIRE deployment.
Credential management is also handled automatically. The kubeconfig files the SPIRE server needs to reach remote clusters are generated from registration data, and credentials are minted on demand. No static tokens or rotation schedule to maintain.
The setup flow looks like this:
- The platform security team creates the trust zone in Connect once, defining the trust domain, attestation policies, and which teams or service accounts are permitted to register clusters against it.
- Platform / application teams (or CI pipelines) register new clusters directly using the Connect CLI, API, or Terraform provider, within whatever constraints the platform security team established.
- Connect propagates the updated cluster list to the SPIRE server. The cluster is attesting workloads within minutes.
No tickets, ConfigMap edits or SPIRE server restarts required. As an added bonus, every cluster membership change flows through Connect, so there's a full audit trail of who registered what and when.
The scaling problem nobody plans for
This matters most when clusters come and go regularly: multiple regions, CI/CD environments that spin up and tear down, or cluster management shifting closer to application teams.
If you're already feeling the friction — or running more than five clusters and expecting that number to grow — book a 30-minute architecture call to talk through how Connect fits your setup.