Issuing Identity with MeshIdentity Spire provider

This is a guide for experimental feature.

The MeshIdentity allows you to manage identity for selected data planes. In this guide we will take a look on how to issue identities using Spire provider and how to use them encrypt traffic in mesh.

Prerequisites

Before you begin, make sure you have the following tools installed:

  • Helm – used to install and manage Kubernetes applications
  • minikube – used to run a local Kubernetes cluster for testing

Start a Kubernetes cluster

Start a local Kubernetes cluster using minikube. The -p flag creates a new profile named mesh-zone:

minikube start -p mesh-zone

If you already have a running Kubernetes cluster, either locally or in the cloud (for example, EKS, GKE, or AKS), you can skip this step.

Install Kuma

Install Kuma control plane with Helm by executing:

helm repo add kuma https://kumahq.github.io/charts
helm repo update
helm install --create-namespace --namespace kuma-system \
  --set "controlPlane.envVars.KUMA_RUNTIME_KUBERNETES_INJECTOR_SPIRE_ENABLED=true" \
  kuma kuma/kuma

We need to enable Kubernetes Spire injector on control plane for Spire support to work.

Install Spire

Install Spire CRDs:

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

Install Spire with custom trust domain default.local-zone.mesh.local. We will use this trust domain in next steps to configure MeshIdentity

helm upgrade --install -n spire spire spire \
 --repo https://spiffe.github.io/helm-charts-hardened/ \
 --set "global.spire.trustDomain=default.local-zone.mesh.local" \
 --set "global.spire.tools.kubectl.tag=v1.31.11"

Configure Spire to issue identities in kuma-demo namespace

We need to configure Spire to issue identities in kuma-demo namespace.

echo "apiVersion: spire.spiffe.io/v1alpha1
kind: ClusterSPIFFEID
metadata:
  name: spire-registration
spec:
  podSelector:
    matchLabels:
      kuma.io/mesh: default
  spiffeIDTemplate: 'spiffe://{{ .TrustDomain }}/ns/{{ .PodMeta.Namespace }}/sa/{{ .PodSpec.ServiceAccountName }}'
  workloadSelectorTemplates:
    - 'k8s:ns:kuma-demo'" | kubectl apply -f -

We specify SpiffeID template that will use our configured trust domain, namespace and service account name.

Deploy demo application

  1. Deploy the application
    kubectl apply -f https://raw.githubusercontent.com/kumahq/kuma-counter-demo/refs/heads/main/k8s/000-with-kuma.yaml
    kubectl wait -n kuma-demo --for=condition=ready pod --selector=app=demo-app --timeout=90s
    

For the MeshIdentity to work you need to have meshServices.mode: Exclusive set on Mesh resource. It is already configured in demo.

  1. Port-forward the service to the namespace on port 5000:

    kubectl port-forward svc/demo-app -n kuma-demo 5050:5050
    
  2. Try making requests to demo-app

    curl -XPOST localhost:5050/api/counter
    

    You should see similar results:

    {"counter":1,"zone":""}
    

Concepts

In Kuma we define two concepts around identity that need to be well understood:

  • Identity - Who a workload is - A workload’s identity is the name encoded in its certificate, and this identity is considered valid only if the certificate is signed by a Trust.
  • Trust - Who to believe - Trust defines which identities you accept as valid, and is established through trusted certificate authorities (CA) that issue those identities. Trust is attached to trust domain, and there can be multiple Trusts in the cluster.

Issuing Identity

In Kuma we have MeshIdentity resource responsible for managing identity. In our scenario Spire is responsible for issuing identity and managing trust, but we still need to create MeshIdentity to configure data plane to use identity managed by Spire.

echo "apiVersion: kuma.io/v1alpha1
kind: MeshIdentity
metadata:
  name: identity-spire
  namespace: kuma-system
  labels:
    kuma.io/mesh: default
    kuma.io/origin: zone
spec:
  selector:
    dataplane:
      matchLabels: {}
  spiffeID:
    trustDomain: default.local-zone.mesh.local
    path: '/ns/{{ .Namespace }}/sa/{{ .ServiceAccount }}'
  provider:
    type: Spire
    spire: {}" | kubectl apply -f -

Let’s take a closer look at resource we’ve just applied. MeshIdentity uses selector field to select data planes for which identity should be issued. In our example, identity will be issued for all data planes in Mesh.

Next is spiffeID field. This field contains templates for building spiffeID for our workloads. In this example we need to use the same trust domain that we configured in Spire: default.local-zone.mesh.local with path that will be dynamically created from namespace and service account name. Example spiffeID will look like this spiffe://default.local-zone.mesh.local/ns/kuma-demo/sa/default.

Last thing we see in this example is provider field. This field contains configuration specific to identity provider. We just need to specify Spire as a provider type.

MeshIdentity in GUI

In the GUI when you open Mesh view, you will see new sections with MeshIdentity and MeshTrust, where you can inspect these resources.

Data Plane Proxies Stats metric for inbound_POD_IP_6379.rbac.allowed

Testing connectivity with MeshIdentity

We can now make some requests to our demo-app:

curl -XPOST localhost:5050/api/counter

We should see errors like this:

{"instance":"d11ee97a4b45ff3a7b59091d1612b7f7","status":500,"title":"failed to retrieve zone","type":"https://github.com/kumahq/kuma-counter-demo/blob/main/ERRORS.md#INTERNAL-ERROR"}

Since we issued identity for our workloads, mTLS was also configured. Zero trust is default behavior in this situation, and because we don’t have any MeshTrafficPermission configured we see these errors.

Allowing traffic in kuma-demo namespace

To allow traffic in kuma-demo we need to create MeshTrafficPermission. Apply this:

echo "apiVersion: kuma.io/v1alpha1
kind: MeshTrafficPermission
metadata:
  name: mtp
  namespace: kuma-demo
  labels:
    kuma.io/mesh: default
spec:
  rules:
    - default:
        allow:
          - spiffeID:
              type: Prefix
              value: spiffe://default.local-zone.mesh.local/ns/kuma-demo" | kubectl apply -f -

This MeshTrafficPermission uses rules API with spiffeID matching. This policy will allow all traffic from workloads which spiffeID starts with: spiffe://default.local-zone.mesh.local/ns/kuma-demo. This spiffeID is based on template from MeshIdentity we’ve created earlier, every workload in default Mesh. and in kuma-demo namespace will have spiffeID with this prefix. In the future if you want to be more specific you can allow only workloads matching its exact spiffeID.

We can now try if traffic works. Run:

curl -XPOST localhost:5050/api/counter

You should see something similar to:

{"counter":3,"zone":""}

What you’ve learned

We’ve learned how to issue identity with Spire and MeshIdentity. Also, we’ve seen how to allow traffic using MeshTrafficPermission with spiffeID matchers.

Next steps