Issue identity with MeshIdentity Spire provider

Uses: Kong Mesh
Incompatible with
konnect
Related Documentation
Tags
Minimum Version
Kong Mesh - 2.12
TL;DR

Install Spire and configure it to issue SPIFFE identities, apply a MeshIdentity with the Spire provider, then apply a MeshTrafficPermission with SPIFFE ID prefix matching to allow traffic between workloads.

Prerequisites

You will need Helm, a package manager for Kubernetes.

This guide requires a running Kubernetes cluster. If you already have a Kubernetes cluster running, you can skip this step. It can be a cluster running locally, like Docker, or in a public cloud like AWS EKS, GCP GKE, etc.

For example, if you are using minikube:

minikube start -p mesh-zone
  1. Install Kong Mesh:

    helm repo add kong-mesh https://kong.github.io/kong-mesh-charts
    helm repo update
    helm upgrade \
      --install \
      --create-namespace \
      --namespace kong-mesh-system \
      kong-mesh kong-mesh/kong-mesh
    kubectl wait -n kong-mesh-system --for=condition=ready pod --selector=app=kong-mesh-control-plane --timeout=90s
    
  2. Apply the demo configuration:

    echo "
    apiVersion: v1
    kind: Namespace
    metadata:
      labels:
        kuma.io/sidecar-injection: enabled
      name: kong-mesh-demo
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: demo-app
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: demo-app
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: demo-app-v1
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: demo-app
        version: v1
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: demo-app-v2
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: demo-app
        version: v2
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: kv
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: kv
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: demo-app
        version: v1
      name: demo-app
      namespace: kong-mesh-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: demo-app
          version: v1
      template:
        metadata:
          labels:
            app: demo-app
            version: v1
        spec:
          containers:
          - env:
            - name: OTEL_SERVICE_NAME
              value: demo-app
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://opentelemetry-collector.mesh-observability:4317
            - name: KV_URL
              value: http://kv.kong-mesh-demo.svc.cluster.local:5050
            - name: APP_VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
            image: ghcr.io/kumahq/kuma-counter-demo:latest@sha256:daf8f5cffa10b576ff845be84e4e3bd5a8a6470c7e66293c5e03a148f08ac148
            name: app
            ports:
            - containerPort: 5050
              name: http
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: demo-app
        version: v2
      name: demo-app-v2
      namespace: kong-mesh-demo
    spec:
      replicas: 0
      selector:
        matchLabels:
          app: demo-app
          version: v2
      template:
        metadata:
          labels:
            app: demo-app
            version: v2
        spec:
          containers:
          - env:
            - name: OTEL_SERVICE_NAME
              value: demo-app
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://opentelemetry-collector.mesh-observability:4317
            - name: KV_URL
              value: http://kv.kong-mesh-demo.svc.cluster.local:5050
            - name: APP_VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
            image: ghcr.io/kumahq/kuma-counter-demo:latest@sha256:daf8f5cffa10b576ff845be84e4e3bd5a8a6470c7e66293c5e03a148f08ac148
            name: demo-app
            ports:
            - containerPort: 5050
              name: http
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: kv
      namespace: kong-mesh-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: kv
      template:
        metadata:
          labels:
            app: kv
        spec:
          containers:
          - env:
            - name: OTEL_SERVICE_NAME
              value: kv
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://opentelemetry-collector.mesh-observability:4317
            - name: APP_VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
            image: ghcr.io/kumahq/kuma-counter-demo:latest@sha256:daf8f5cffa10b576ff845be84e4e3bd5a8a6470c7e66293c5e03a148f08ac148
            name: app
            ports:
            - containerPort: 5050
              name: http
    ---
    apiVersion: kuma.io/v1alpha1
    kind: Mesh
    metadata:
      name: default
    spec:
      meshServices:
        mode: Exclusive
      mtls:
        backends:
        - name: ca-1
          type: builtin
        enabledBackend: ca-1
    ---
    apiVersion: kuma.io/v1alpha1
    kind: MeshTrafficPermission
    metadata:
      name: kv
      namespace: kong-mesh-demo
    spec:
      from:
      - default:
          action: Allow
        targetRef:
          kind: MeshSubset
          tags:
            app: demo-app
            k8s.kuma.io/namespace: kong-mesh-demo
      targetRef:
        kind: Dataplane
        labels:
          app: kv" | kubectl apply -f -
    kubectl wait -n kong-mesh-demo --for=condition=available --timeout=120s deployment --all
    
  1. Install the Spire CRDs:

    helm upgrade --install --create-namespace -n spire spire-crds spire-crds \
      --repo https://spiffe.github.io/helm-charts-hardened/
    
  2. Install Spire with the trust domain default.default.mesh.local. You’ll use this trust domain later when you configure MeshIdentity:

    helm upgrade --install -n spire spire spire \
      --repo https://spiffe.github.io/helm-charts-hardened/ \
      --set "global.spire.trustDomain=default.default.mesh.local" \
      --set "global.spire.tools.kubectl.tag=v1.31.11"
    kubectl wait -n spire --for=condition=ready pod --all --timeout=90s
    

This guide covers an experimental feature.

The MeshIdentity policy issues identities for selected data planes. This approach is SPIFFE-compliant. In this guide, you’ll issue identities using Spire as the identity provider, where Spire issues identities and manages the trust externally.

In Kong Mesh, there are two identity concepts:

  • Identity: A workload identity is the name encoded in its certificate, and this identity is valid only if the certificate is signed by a trust.
  • Trust: Trust defines which identities you accept as valid through trusted certificate authorities (CA) that issue those identities. Each trust belongs to a trust domain, and a mesh can contain multiple trusts.

Enable Spire injection on the control plane

The Kong Mesh sidecar injector adds the Spire workload API socket to data plane pods at creation time. This behavior is disabled by default. Enable it on the control plane:

kubectl set env -n kong-mesh-system deploy/kong-mesh-control-plane \
  KUMA_RUNTIME_KUBERNETES_INJECTOR_SPIRE_ENABLED=true
kubectl rollout status -n kong-mesh-system deploy/kong-mesh-control-plane

Configure Spire

Configure Spire to issue identities in the kong-mesh-demo namespace. This specifies a SPIFFE ID template that uses the configured trust domain, namespace, and service account name:

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:kong-mesh-demo'" | kubectl apply -f -

Issue identities

For MeshIdentity to work, meshServices.mode: Exclusive must be set on the Mesh resource. This value is already configured in the demo Mesh.

The roles are divided as follows:

  • MeshIdentity declares which identity provider the data planes should use and how.
  • Spire issues the identity and manages the trust.

This is why you create a MeshIdentity to configure the data planes and specify that Spire manages the identity.

To issue identities in a mesh using Spire, create this resource:

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

MeshIdentity uses selector to choose the data planes that receive identities. In this example, the selector issues identity to all data planes in the mesh.

spiffeID defines templates for workload SPIFFE IDs. The trust domain must match the trust domain you configured in Spire (default.default.mesh.local). The path template combines the namespace and service account. Example SPIFFE ID: spiffe://default.default.mesh.local/ns/kong-mesh-demo/sa/default.

The provider field contains identity provider-specific configuration. This guide uses the Spire provider.

Restart the demo application

Restart the demo pods so they’re recreated with the Spire workload API socket and pick up the Spire-issued identity:

kubectl rollout restart -n kong-mesh-demo deployment/demo-app deployment/kv

Wait until the Pods are restarted before moving on to the next step.

Test connectivity

  1. Port-forward the demo-app service on port 5050:

    kubectl port-forward svc/demo-app -n kong-mesh-demo 5050:5050
    
  2. In a new terminal, send a request to demo-app:

    curl -XPOST localhost:5050/api/counter
    

    You should see an error 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"
    }
    

Issuing identity to workloads enables mTLS. Zero trust is the default behavior, so without a MeshTrafficPermission to allow traffic, these errors are expected.

Allow traffic

Create a MeshTrafficPermission:

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

This MeshTrafficPermission uses SPIFFE ID matching to allow traffic from workloads whose SPIFFE ID starts with spiffe://default.default.mesh.local/ns/kong-mesh-demo.

Based on the template in the MeshIdentity, every workload has a SPIFFE ID with this prefix in:

  • The default mesh
  • The kong-mesh-demo namespace.

You can also allow only workloads matching their exact SPIFFE ID for more fine-grained control.

Validate

Send another request:

curl -XPOST localhost:5050/api/counter

You should see the following output:

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

If you still get the error, wait a few seconds and try again.

Cleanup

To clean up your environment, remove the Docker containers, network, temporary directory, and the control plane configuration. Run the following command:

kubectl config delete-context mesh-zone

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!