TemporalClusterClient — mTLS client credentials

A TemporalClusterClient issues mTLS client credentials for a Temporal cluster. The operator requests a cert-manager Certificate from the cluster’s mTLS CA issuer and writes the result (tls.crt, tls.key, ca.crt) into a Kubernetes Secret. Workers and client applications can then mount that Secret to connect to the cluster’s frontend over mTLS.

Requirement: this CRD only works against an mTLS-enabled TemporalCluster (mtls.provider: cert-manager). Against a non-mTLS TemporalCluster the controller reports a ClusterMTLSDisabled condition, and against a TemporalDevServer it reports DevServerUnsupported — in both cases it issues no certificate.

Prerequisites: cert-manager must be installed in the cluster.

Files #

FileDescription
issuer.yamlSelf-signed root Issuer, CA Certificate, and temporal-ca-issuer CA Issuer. Apply first.
temporalcluster.yamlmTLS-enabled TemporalCluster (temporal-mtls) that references temporal-ca-issuer.
clusterclient.yamlTemporalClusterClient that issues credentials from the cluster above.

Apply #

# 1. Bootstrap the cert-manager CA issuer
kubectl apply -f issuer.yaml

# 2. Create the mTLS-enabled cluster (adjust persistence config for your env)
kubectl apply -f temporalcluster.yaml

# 3. Issue client credentials
kubectl apply -f clusterclient.yaml

Wait for the cluster to become ready, then check the client:

kubectl get temporalclusterclients        # short name: tcc
kubectl describe tcc temporal-mtls-client

The READY column becomes True once cert-manager has issued the certificate (CertificateReady condition). Inspect the generated Secret:

kubectl get secret temporal-mtls-client -o yaml
# Data keys: tls.crt, tls.key, ca.crt

Using the credentials #

Mount the Secret in a worker or client deployment to connect over mTLS:

volumes:
  - name: temporal-client-tls
    secret:
      secretName: temporal-mtls-client
containers:
  - name: worker
    volumeMounts:
      - name: temporal-client-tls
        mountPath: /etc/temporal/tls
        readOnly: true

Pass the mounted paths to your Temporal SDK’s TLS configuration (ClientCertPath, ClientKeyPath, CACertPath) when dialing the cluster’s frontend (<cluster-name>-frontend.<namespace>:7233).

Caveats #

  • The cluster referenced by clusterRef.name must exist in the same namespace as the TemporalClusterClient.
  • If the cluster has no mTLS configured, the controller sets a ClusterMTLSDisabled condition and does not issue a certificate.
  • secretName defaults to the TemporalClusterClient resource name when omitted.

Manifests #

clusterclient.yaml #

apiVersion: temporal.bmor10.com/v1alpha1
kind: TemporalClusterClient
metadata:
  name: temporal-mtls-client
spec:
  # Must reference an mTLS-enabled TemporalCluster in the same namespace.
  clusterRef:
    name: temporal-mtls
  # Name of the Secret the operator writes the generated client credentials into.
  secretName: temporal-mtls-client

issuer.yaml #

# A self-signed root issuer and CA, then a CA issuer that the operator uses to
# mint internode and frontend certificates. Requires cert-manager installed.
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: temporal-selfsigned
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: temporal-ca
spec:
  isCA: true
  commonName: temporal-ca
  secretName: temporal-ca
  privateKey:
    algorithm: ECDSA
    size: 256
  issuerRef:
    name: temporal-selfsigned
    kind: Issuer
    group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: temporal-ca-issuer
spec:
  ca:
    secretName: temporal-ca

temporalcluster.yaml #

# An mTLS-enabled TemporalCluster backed by the cert-manager CA issuer above.
# TemporalClusterClient requires an mTLS-enabled cluster — the controller reads
# mtls.issuerRef to issue client certificates.
apiVersion: temporal.bmor10.com/v1alpha1
kind: TemporalCluster
metadata:
  name: temporal-mtls
spec:
  version: "1.31.1"
  numHistoryShards: 512
  mtls:
    provider: cert-manager
    issuerRef:
      name: temporal-ca-issuer
      kind: Issuer
  persistence:
    defaultStore:
      sql:
        pluginName: postgres12
        host: temporal-pg-rw
        port: 5432
        database: temporal
        user: temporal
        passwordSecretRef:
          name: temporal-pg-app
          key: password
    visibilityStore:
      sql:
        pluginName: postgres12
        host: temporal-pg-rw
        port: 5432
        database: temporal_visibility
        user: temporal
        passwordSecretRef:
          name: temporal-pg-app
          key: password