Kubernetes
This setup guide is for making Kubernetes service account tokens verifiable by external platforms. You set the cluster's issuer to your oidc.pub URL, then publish the discovery document and JWKS through oidc.pub so cloud IAM can validate those tokens.
Kubernetes: Service Account Issuer Discovery
Prerequisites
- A Kubernetes cluster with
ServiceAccountTokenVolumeProjectionenabled (default since 1.20) - An oidc.pub service (e.g.
k8s-prod.oidc.pub) - Admin access to the cluster's API server configuration
Step 1: Configure the API server issuer
Set the service account issuer to your oidc.pub subdomain. This must be done before uploading the configuration so that service account tokens carry the correct iss claim. Select your distribution below.
Edit the ClusterConfiguration to set the issuer in the API server extra args. If the cluster is already running, update /etc/kubernetes/manifests/kube-apiserver.yaml directly — the kubelet will restart the API server automatically.
# ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
extraArgs:
service-account-issuer: https://k8s-prod.oidc.pub
service-account-key-file: /etc/kubernetes/pki/sa.pub
service-account-signing-key-file: /etc/kubernetes/pki/sa.key# ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
extraArgs:
service-account-issuer: https://k8s-prod.oidc.pub
service-account-key-file: /etc/kubernetes/pki/sa.pub
service-account-signing-key-file: /etc/kubernetes/pki/sa.keyFor existing clusters, you can also edit the flags directly in the API server static pod manifest at /etc/kubernetes/manifests/kube-apiserver.yaml.
k3s accepts API server flags via its config file or command line. Add the issuer flag to your k3s server configuration.
# /etc/rancher/k3s/config.yaml
kube-apiserver-arg:
- "service-account-issuer=https://k8s-prod.oidc.pub"# /etc/rancher/k3s/config.yaml
kube-apiserver-arg:
- "service-account-issuer=https://k8s-prod.oidc.pub"Alternatively, pass it on the command line:
k3s server --kube-apiserver-arg="service-account-issuer=https://k8s-prod.oidc.pub"k3s server --kube-apiserver-arg="service-account-issuer=https://k8s-prod.oidc.pub"Restart k3s after making this change: sudo systemctl restart k3s
OpenShift manages the API server configuration through the Authentication cluster resource. The Machine Config Operator rolls the change out to all control plane nodes.
oc patch authentication cluster --type=merge \
-p '{"spec":{"serviceAccountIssuer":"https://k8s-prod.oidc.pub"}}'oc patch authentication cluster --type=merge \
-p '{"spec":{"serviceAccountIssuer":"https://k8s-prod.oidc.pub"}}'This triggers a rolling restart of the API servers. Monitor the rollout with oc get clusteroperators authentication. The cluster may take several minutes to stabilize.
Minikube passes extra flags to the API server via --extra-config. Set the issuer when starting the cluster.
minikube start \
--extra-config=apiserver.service-account-issuer=https://k8s-dev.oidc.pub \
--extra-config=apiserver.service-account-key-file=/var/lib/minikube/certs/sa.pub \
--extra-config=apiserver.service-account-signing-key-file=/var/lib/minikube/certs/sa.keyminikube start \
--extra-config=apiserver.service-account-issuer=https://k8s-dev.oidc.pub \
--extra-config=apiserver.service-account-key-file=/var/lib/minikube/certs/sa.pub \
--extra-config=apiserver.service-account-signing-key-file=/var/lib/minikube/certs/sa.keyFor an already running cluster, delete and recreate it with the flags above, or pass them via minikube start --extra-config which triggers an API server restart.
kind uses a cluster configuration file to pass API server flags. Create a config with the issuer set before creating the cluster.
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
service-account-issuer: https://k8s-dev.oidc.pub# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
apiServer:
extraArgs:
service-account-issuer: https://k8s-dev.oidc.pubkind create cluster --config kind-config.yamlkind create cluster --config kind-config.yamlkind clusters are ephemeral — if you need to change the issuer, delete and recreate the cluster with the updated config.
Step 2: Sync OIDC config to oidc.pub
Kubernetes exposes its OIDC discovery document and JWKS at well-known paths on the API server. You can sync these to oidc.pub manually or run the sync worker to keep them up to date automatically.
Run the sync worker directly in your cluster. It fetches the OIDC config from the API server and pushes it to oidc.pub on a regular interval. Obtain an API key from the oidc.pub dashboard under your service settings.
kubectl run oidcpub-sync \
--namespace=kube-system \
--image=registry.gitlab.com/oidc.pub/cli:latest-sync \
--env="OIDCPUB_SERVICE_ID=<your-service-subdomain>" \
--env="OIDCPUB_API_KEY=<your-api-key>" \
--env="SYNC_INTERVAL=300"kubectl run oidcpub-sync \
--namespace=kube-system \
--image=registry.gitlab.com/oidc.pub/cli:latest-sync \
--env="OIDCPUB_SERVICE_ID=<your-service-subdomain>" \
--env="OIDCPUB_API_KEY=<your-api-key>" \
--env="SYNC_INTERVAL=300"The worker syncs on startup and then every SYNC_INTERVAL seconds (default 300). It discovers the API server address and credentials from the in-cluster service account automatically.
# Get the API server URL
API_SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
# Fetch openid-configuration
OIDC_CONFIG=$(curl -sk $API_SERVER/.well-known/openid-configuration)
# Fetch JWKS
JWKS_URI=$(echo $OIDC_CONFIG | jq -r .jwks_uri)
JWKS=$(curl -sk $JWKS_URI)
# Upload to oidc.pub
curl -X PUT https://oidc.pub/api/services/$SERVICE_SUBDOMAIN/config \
-H "Authorization: Bearer $OIDCPUB_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--argjson oidc "$OIDC_CONFIG" \
--argjson jwks "$JWKS" \
'{ openidConfiguration: $oidc, jwks: $jwks }'
)"
# SERVICE_SUBDOMAIN is the preferred public reference. Service IDs remain
# accepted for compatibility.# Get the API server URL
API_SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
# Fetch openid-configuration
OIDC_CONFIG=$(curl -sk $API_SERVER/.well-known/openid-configuration)
# Fetch JWKS
JWKS_URI=$(echo $OIDC_CONFIG | jq -r .jwks_uri)
JWKS=$(curl -sk $JWKS_URI)
# Upload to oidc.pub
curl -X PUT https://oidc.pub/api/services/$SERVICE_SUBDOMAIN/config \
-H "Authorization: Bearer $OIDCPUB_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--argjson oidc "$OIDC_CONFIG" \
--argjson jwks "$JWKS" \
'{ openidConfiguration: $oidc, jwks: $jwks }'
)"
# SERVICE_SUBDOMAIN is the preferred public reference. Service IDs remain
# accepted for compatibility.Step 3: Verify
Within 60 seconds, your OIDC discovery endpoint is publicly reachable.
curl https://k8s-prod.oidc.pub/.well-known/openid-configuration | jq .curl https://k8s-prod.oidc.pub/.well-known/openid-configuration | jq .curl https://k8s-prod.oidc.pub/.well-known/jwks.json | jq .curl https://k8s-prod.oidc.pub/.well-known/jwks.json | jq .Next steps
- Sync Worker — configuration reference, authentication options, and one-shot mode
- Key Rotation — how OIDC providers rotate signing keys and what it means for your sync interval
- Check out one of the integration guides, for example the AWS Integration Guide — a concrete relying-party setup for identity published through oidc.pub