Federation

How to setup a SPIRE Federation using the SPIRE Helm charts

The typical architecture for Federated SPIRE using the Helm charts uses a 1:1 relationship between SPIRE instances and Kubernetes Clusters, as well as multiple Kubernetes clusters.

Federation Configuration

There are 4 pieces of configuration related to Federation.

Enabling Federation

Set spire-server.federation.enabled=true.

your-values.yaml snippet:

spire-server:
  federation:
    enabled: true

Exposing Federation Bundle Endpoint outside the Kubernetes Cluster

Set spire-server.federation.ingress.enabled=true. Futher customization can be made as described in Exposing Services

your-values.yaml snippet:

spire-server:
  federation:
    ingress:
      enabled: true

Configure local SPIRE to talk to and trust other SPIRE Instances

your-values.yaml snippet:

spire-server:
  controllerManager:
    identities:
      clusterFederatedTrustDomains:
        b:
          bundleEndpointProfile:
            endpointSPIFFEID: spiffe://b-org.local/spire/server
            type: https_spiffe
          bundleEndpointURL: https://spire-server-federation.b-org.local
          trustDomain: b-org.local

Configure local workloads to trust the other SPIRE

your-values.yaml snippet:

spire-server:
  controllerManager:
    identities:
      clusterSPIFFEIDs:
        default:
          federatesWith:
          - b-org.local

Example Federated Installation

Kubernetes Cluster A

your-values.yaml

global:
  openshift: false
  spire:
    recommendations:
      enabled: true
    namespaces:
      create: true
    ingressControllerType: ingress-nginx
    clusterName: a
    trustDomain: a-org.local

spire-server:
  ca_subject:
    country: US
    organization: A
    common_name: a.local
  federation:
    enabled: true
    ingress:
      enabled: true
  controllerManager:
    identities:
      clusterSPIFFEIDs:
        default:
          federatesWith:
          - b-org.local
      clusterFederatedTrustDomains:
        b:
          bundleEndpointProfile:
            endpointSPIFFEID: spiffe://b-org.local/spire/server
            type: https_spiffe
          bundleEndpointURL: https://spire-server-federation.b-org.local
          trustDomain: b-org.local

spiffe-oidc-discovery-provider:
  ingress:
    enabled: true

Install on Cluster A

helm upgrade --install --create-namespace -n spire-server spire-crds spire-crds \
 --repo https://spiffe.github.io/helm-charts-hardened/

helm upgrade --install -n spire-server spire spire \
 --repo https://spiffe.github.io/helm-charts-hardened/

Kubernetes Cluster B

your-values.yaml

global:
  openshift: false
  spire:
    recommendations:
      enabled: true
    namespaces:
      create: true
    ingressControllerType: ingress-nginx
    clusterName: b
    trustDomain: b-org.local

spire-server:
  ca_subject:
    country: US
    organization: B
    common_name: b.local
  federation:
    enabled: true
    ingress:
      enabled: true
  controllerManager:
    identities:
      clusterSPIFFEIDs:
        default:
          federatesWith:
          - a-org.local
      clusterFederatedTrustDomains:
        a:
          bundleEndpointProfile:
            endpointSPIFFEID: spiffe://a-org.local/spire/server
            type: https_spiffe
          bundleEndpointURL: https://spire-server-federation.a-org.local
          trustDomain: a-org.local

spiffe-oidc-discovery-provider:
  ingress:
    enabled: true

Install on Cluster B

helm upgrade --install --create-namespace -n spire-server spire-crds spire-crds \
 --repo https://spiffe.github.io/helm-charts-hardened/

helm upgrade --install -n spire-server spire spire \
 --repo https://spiffe.github.io/helm-charts-hardened/

DNS

Ensure that the bundleEndpointURL’s specified are valid and in DNS. Use the External IP Address of the Ingress Controller service.

The hostnames can be adjusted if desired, as described in Exposing Services

Bootstrapping the Federation

When using bundleEndpointProfile’s of type https_spiffe, both instances will need to have trust bundles from the other instance loaded in manually. Once they are loaded. The instances can automatically renew them once trust is established.

On Cluster A, retrieve the trust bundle

kubectl exec -it -n spire-server spire-server-0 -c spire-server -- spire-server bundle show -format spiffe > cluster-a.bundle

Then send cluster-a.bundle to the admin of Cluster B via a secure means. (Signed email, scp, etc)

On Cluster B, retrieve the trust bundle

kubectl exec -it -n spire-server spire-server-0 -c spire-server -- spire-server bundle show -format spiffe > cluster-b.bundle

Then, send cluster-b.bundle to the admin of Cluster A via a secure means. (Signed email, scp, etc)

On Cluster A, load in Cluster Bs trust bundle

cat cluster-b.bundle | kubectl exec -i -n spire-server spire-server-0 -c spire-server -- spire-server bundle set -format spiffe -id spiffe://b-org.local

On Cluster B, load in Cluster As trust bundle

cat cluster-a.bundle | kubectl exec -i -n spire-server spire-server-0 -c spire-server -- spire-server bundle set -format spiffe -id spiffe://a-org.local