temporalio / temporalio/temporal-worker-controller

Support Kubernetes Service Account Tokens as an auth mode for Connection (controller + worker pods)

Open
#515 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

rbac
Dominant language
Go
Stars
187
Forks
70
Avg merge
4d 1h
Merged PRs (30d)
31

Description

Is your feature request related to a problem? Please describe.

Today Connection supports exactly two mutually-exclusive auth modes: mutualTLSSecretRef
and apiKeySecretRef. Both require a long-lived, static Kubernetes Secret — a client cert/key
pair for mTLS, or a bearer token for API keys. For self-hosted Temporal running on Kubernetes,
this means:

  • The controller itself holds a static credential to make control-plane calls (register worker
    deployment versions, check drainage, etc.).
  • Worker pods get that same static credential injected as an env var at pod creation time, with
    no rotation until the pod restarts.

For teams already running Workload Identity / IRSA / OIDC-federated auth elsewhere in their
Kubernetes platform (no static keys, short-lived tokens, automatic rotation), this is a step
backward — it reintroduces exactly the kind of long-lived Secret that Workload Identity patterns
are meant to eliminate. It also means every rotation of the underlying cert/key requires updating
a Secret and waiting for the controller/pods to notice, rather than tokens refreshing themselves
transparently.

This isn't hypothetical — worker-side JWT auth against Temporal keeps coming up in the community:

Describe the solution you'd like

Add a third auth mode, e.g. serviceAccountToken, to ConnectionSpec, alongside the existing
mutualTLSSecretRef / apiKeySecretRef. This would rely on projected Kubernetes SA tokens
(TokenRequest API) rather than a static Secret, and splits cleanly into two independent pieces:

1. Controller's own connection (control plane)
The controller already runs as a pod with its own ServiceAccount. Instead of reading a static
Secret, it would read its own pod's projected SA token (refreshed automatically by kubelet) and
use it as a bearer token via sdkclient.NewAPIKeyDynamicCredentials, the same mechanism already
used for the apiKeySecretRef path in clientpool.go. This is a self-contained change — no new
pod-injection logic needed, since the controller's own pod spec is under the cluster operator's
control already.

2. Worker pods (data plane)
When Connection.spec.authMode: ServiceAccountToken is set, the controller would inject into
every worker pod template it generates:

  • a projected volume with a serviceAccountToken source (configurable audience,
    expirationSeconds)
  • a volumeMount at a well-known path
  • an env var pointing at that path (e.g. TEMPORAL_API_KEY_FILE) instead of the current
    TEMPORAL_API_KEY=<value> injection

Example spec:

apiVersion: temporal.io/v1alpha1
kind: Connection
metadata:
  name: my-temporal-connection
spec:
  hostPort: temporal-frontend.temporal-system:7233
  authMode: ServiceAccountToken
  serviceAccountToken:
    audience: temporal
    expirationSeconds: 3600

Worker pods would need no manual volume/env config — just a serviceAccountName on the pod
template, same as today.

Additional context

  • This mode would only be meaningful for self-hosted Temporal — it requires the server to be
    configured with a JWT ClaimMapper validating tokens against the cluster's OIDC issuer/JWKS.
    Temporal Cloud's auth model doesn't support this today, so this would not replace
    apiKeySecretRef as the recommended mode there.
  • The controller can guarantee "a fresh token is on disk," but whether the worker process
    actually re-reads that file per-connection (vs. caching it once at startup) is up to the
    worker's own SDK client construction — e.g. Go SDK's NewAPIKeyDynamicCredentials callback
    pattern.
  • For multi-cluster setups (workers on EKS/AKS/GKE all pointing at one Temporal cluster), the
    server-side ClaimMapper would need to trust multiple clusters' OIDC issuers — worth surfacing
    in docs rather than assuming a single-issuer setup.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ConnectionSpec and clientpool.go, especially the existing apiKeySecretRef path using sdkclient.NewAPIKeyDynamicCredentials. Trace the controller and worker pod-template generation entry points to understand where projected service-account volumes, mounts, and environment variables are configured. Done means controller authentication uses a projected token and generated worker pods receive configurable TokenRequest settings without static credentials.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, kubernetes
Domain
authentication, backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.