Join our Newsletter — 33% off our NHI Course
Home› Guides› Self-Hosted OIDC Issuer Security Guide: Protecting the Keys…
Guide Cloud & Workload Identity

Self-Hosted OIDC Issuer Security Guide: Protecting the Keys and Discovery Documents Your Cloud Federation Trusts

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 11 November 2026 15 min read
On this page

When a self-managed Kubernetes cluster or an in-house token service is federated into a cloud, its signing key and the two small JSON files it publishes become a route to your cloud roles that nobody owns. Managed platforms run the issuer for you: you trust their URL and focus on subject and audience conditions. Self-managed clusters, on-premises distributions and home-built token services make you the identity provider, so you also own key custody, the hosting of the discovery document and JSON Web Key Set (JWKS), the issuer URL's lifetime and the evidence trail for every token. This guide covers those issuer-side duties for platform, cloud and identity engineers. It builds on the Workload Identity Federation Guide, which covers the cloud-side trust conditions, and does not repeat them.

Key takeaways

  • The signing key is a master credential. Anyone holding the private key behind your issuer can mint a token for any service account and namespace, which satisfies every cloud trust condition that names that issuer.
  • The hosting is part of the trust chain. Whoever controls the bucket, domain or web server that serves /.well-known/openid-configuration and the JWKS can publish their own key and be trusted by your cloud.
  • Uploading keys can replace public hosting. Google Cloud lets you upload a cluster's JWKS directly to a workload identity pool provider, so the cluster does not need to be reachable from the internet.
  • Issuer URLs must outlive the clusters. Deleted buckets and expired domains can be claimed by someone else while cloud providers still trust them, so retire the trust before you retire the URL.
  • Forged tokens leave a gap in your logs. A token minted with a stolen key has no matching TokenRequest in the Kubernetes audit log (where your audit policy records token requests), so correlate cloud token exchanges with cluster issuance events.

What you take on when you run the issuer

The Kubernetes API server is an OpenID Connect (OIDC) issuer for its workloads. The kube-apiserver reference states that --service-account-issuer sets the value asserted in the iss claim, that in practice it must be an https URL, and that it should be able to serve discovery documents at {issuer}/.well-known/openid-configuration. The signing key is set with --service-account-signing-key-file, and verification keys with --service-account-key-file. When the API server is not reachable by the cloud, --service-account-jwks-uri points the discovery document at a JWKS hosted elsewhere, which is why many self-managed clusters publish both files to object storage or a static web host.

A cloud security token service then trusts that URL. AWS IAM requires the issuer URL to begin with https:// and says it should not contain a port and that the token's iss claim should correspond to the provider URL; STS must be able to reach the discovery document and JWKS. The discovery document must name a jwks_uri, and AWS verifies the JWKS endpoint's TLS certificate against its trusted root certificate authorities, using configured thumbprints only in the cases its documentation lists. Microsoft Entra Workload ID for self-managed clusters relies on the same OIDC discovery pattern (general architecture; confirm paths in Microsoft's documentation). None of these services knows or cares which machine holds the private key: a valid signature from a key in the published JWKS is enough.

Issuer assetWhere it usually livesWhat an attacker gains with control of it
Private signing keyFile on control plane nodes (often /etc/kubernetes/pki), etcd backups, golden images, an HSM or KMS behind an external signerMint tokens for any subject and audience until the key leaves the JWKS
Discovery documentAPI server, object storage bucket, static website, CDNPoint jwks_uri at an attacker-held key set
JWKS fileSame location as the discovery document, or a separate path set by --service-account-jwks-uriAdd an attacker key that every relying cloud will accept
Issuer hostname and TLS certificatePublic DNS zone, registrar account, certificate authority accountServe both documents from attacker infrastructure under your name
Cloud-side provider objectsIAM OIDC provider, Entra federated credential, workload identity pool providerSwap in a different issuer or JWKS without touching the cluster

Custody of the signing key

Treat the service account signing key as a tier-zero secret, in the same class as a certificate authority key. Set the key class first, then the storage.

  • Prefer an external signer: Kubernetes documents --service-account-signing-endpoint, a Unix socket to a process that implements the ExternalJWTSigner gRPC service. The service-accounts admin page describes it as available from v1.32 and stable in v1.36, while the kube-apiserver flag reference still says the flag requires the ExternalServiceAccountTokenSigner feature gate, so confirm the status for your version. It is mutually exclusive with the file-based key flags, which lets the private key stay in an HSM or cloud KMS where the signer only calls a sign operation. See the HSM Deployment and Operations Guide.
  • If the key must be a file: restrict it to root on control plane nodes, exclude /etc/kubernetes/pki from general node backups and support bundles, and encrypt the backups that must hold it. Snapshot tools, troubleshooting archives and copied virtual machine images are the usual leak paths.
  • One key, one issuer: never reuse a signing key across clusters or environments. A shared key means a development cluster compromise yields production tokens, the same reason the JSON Web Key Sets (JWKS) and Signing Key Rotation Guide binds each key to a single issuer.
  • Limit who can read it: privileged pods with host path mounts on control plane nodes can read the key. Admission policy that blocks host path mounts of the PKI directory outside break-glass namespaces closes the easiest route. See the Kubernetes Admission Control Guide.
  • Cap token lifetime: --service-account-max-token-expiration sets the longest validity any TokenRequest can get. Forged tokens are not bound by it, because an attacker sets their own exp, but a short cap shortens the overlap period you need during rotation and narrows the window for stolen legitimate tokens.

Hosting the discovery document and JWKS safely

Pick the hosting pattern first, because it decides your takeover exposure.

PatternWhen it fitsMain exposureControls
API server serves discovery directlyThe API server endpoint is already public and the cloud must reach itExposes the API server; discovery read access is granted by a ClusterRole bindingNetwork restriction to the discovery paths at a proxy; keep anonymous access limited to those paths
Object storage bucket with public read on two objectsPrivate clusters; the documented pattern for self-hosted IRSABucket write access, bucket deletion and name reuseBucket in a dedicated account, write limited to the rotation pipeline, deletion protection, logging on writes
Static website or CDN on your own domainYou want a stable issuer URL that survives storage movesDNS records, registrar, CDN origin and certificate issuanceRegistrar lock, CAA records, monitored DNS changes, origin locked to the bucket
Key upload to the cloud provider (no public hosting)Google Cloud workload identity pools with self-hosted KubernetesThe provider configuration becomes the trust store; updates are manualTreat provider updates as change-controlled key rotations; automate the upload

Buckets. The AWS self-hosted setup for the pod identity webhook places the discovery document and keys file in an S3 bucket with public read, and its issuer URL includes the bucket name. Amazon S3 documentation warns that after a bucket in the shared global namespace is deleted, another AWS account can create a bucket with the same name and receive requests meant for the old one. AWS recommends account regional namespace buckets, which cannot be re-created by another account, or emptying and keeping a bucket instead of deleting it. For an issuer bucket, keeping it is the only safe choice until every provider that trusts its URL has been removed.

Domains. A custom issuer hostname moves the risk to DNS. Microsoft's subdomain takeover guidance describes how a record left pointing at a deprovisioned resource lets someone else provision that resource and serve content, with a valid certificate, under your name. For an issuer hostname that means serving a JWKS your cloud will accept. Remove or repoint DNS records before deprovisioning the target, and apply the controls in the Subdomain Takeover and Dangling DNS Guide and the Authoritative DNS and Registrar Account Security Guide.

Content integrity. The JWKS must contain public keys only. AWS documents that a JWKS carrying private or symmetric members (RSA d, p, q, dp, dq, qi, oth; EC d; symmetric k) is rejected, and that an exposed private key must be treated as compromised even after a clean JWKS is republished. Add a pipeline check that fails any publish where those members appear, and generate the JWKS from the public key files rather than editing JSON by hand.

Rotating keys without breaking federation

Rotation on a self-hosted issuer has more moving parts than on a managed one because the published JWKS and the cluster flags change separately. The Azure Workload Identity documentation for self-managed clusters gives a sequence that works for any cloud; the timing values below are ours.

  1. Publish first. Generate the new key pair, then publish a JWKS containing both the old and new public keys. Wait at least one full relying-party cache period (we use 24 hours) before signing with the new key.
  2. Switch the signer. Add the new public key with an extra --service-account-key-file, point --service-account-signing-key-file (and the controller manager's private key flag, if legacy tokens remain) at the new key, and restart control plane components one node at a time. Run this only on a highly available control plane.
  3. Verify. Create a test pod, decode its projected token and confirm the header kid matches the new key. Exchange it with each relying cloud before going further.
  4. Wait out old tokens. The Azure guide notes that the kubelet replaces projected tokens once they are older than 80 per cent of their lifetime or 24 hours, so wait at least the maximum token expiration.
  5. Retire. Publish a JWKS with only the new key, remove the old verification key flag, then destroy the old private key and any backup copies. Retired means removed.
  6. Update uploaded copies. Where a provider holds an uploaded JWKS (Google Cloud), add the new key before step 2 and remove the old one at step 5. The Google page notes the upload is not validated and that a malformed or expired JWKS causes authentication failures, so test after every change.

Rollback is simple only before step 5: the old key is still trusted, so pointing the signer back restores service. After step 5, roll forward with another new key.

Changing the issuer URL

Moving to a new bucket, domain or cluster changes the issuer, and every relying trust names the old one. The kube-apiserver accepts --service-account-issuer more than once: the first value is used to sign new tokens and all values are accepted for incoming tokens. That supports a staged move inside the cluster, but cloud providers still match the exact issuer string, so plan both sides.

  1. Stand up the new URL with the current JWKS and verify it from outside your network.
  2. Create the new cloud-side providers and federated credentials alongside the old ones. In AWS the issuer URL is the provider's identity, so a new URL means a new provider and updated role trust policies.
  3. Add the new issuer as the first --service-account-issuer value, keeping the old one second, and restart.
  4. Watch token exchanges move to the new provider. When the old provider has seen none for the maximum token lifetime plus a margin, delete the old cloud-side trusts.
  5. Only then retire the old hosting, and keep the old bucket or domain registered and empty for as long as any record of it might exist.

Detecting forged tokens and tampering

A forged token is cryptographically valid, so detection relies on context. The strongest signal is correlation: every legitimate projected token is created through the TokenRequest API, a create request on the serviceaccounts/token subresource that the Kubernetes audit log can record when your audit policy covers it (general architecture; check your policy level for that subresource). A cloud token exchange for a service account that has no matching issuance in the audit log, or for a pod that does not exist, needs investigation.

SignalSourceWhat it can indicate
Token exchange with no matching TokenRequest for that service account in the token's lifetimeCloud STS logs (AWS CloudTrail AssumeRoleWithWebIdentity, Entra workload identity sign-ins, Google Cloud STS audit logs) joined to Kubernetes audit logsToken minted outside the cluster with a stolen key
Token exp minus iat longer than your configured maximumToken claims where the cloud logs them, or a validating proxyToken not issued by your API server
Unknown kid, or a kid you retiredValidating proxy, JWKS publish historyKey confusion or replay of an old key
Write or delete on the discovery or JWKS objects outside the rotation pipelineStorage access logs, CloudTrail data events, web server logsAttempt to plant a key
Changes to OIDC provider objects (for example UpdateOpenIDConnectProviderThumbprint) or new federated credentialsCloud control-plane audit logsTrust redirected at the cloud end
DNS or certificate changes for the issuer hostnameDNS change logs, certificate transparency monitoringHostname takeover in progress

For alert content and log fields, see the Kubernetes Audit Log Detection Guide. Track these measures: share of cloud token exchanges matched to a cluster issuance event (target near 100 per cent); age of the active signing key against your rotation period; number of issuer URLs trusted by any cloud that have no live cluster; and time from JWKS change to alert.

If the issuer key is compromised

AWS documents the core sequence for an exposed key: generate a new key pair and sign with a new kid, remove the exposed key from the JWKS, review AssumeRoleWithWebIdentity events for the exposure period, and revoke active sessions for roles that trust the provider. For a cluster issuer, also remove the key from every uploaded JWKS copy, restart control plane components so they stop accepting it, and remember that cloud credentials already issued remain valid until they expire or are revoked on the cloud side. Kubernetes notes that individual TokenRequest tokens cannot be revoked and that offline validators do not check whether the bound pod still exists, which is why key removal, not token revocation, is the containment step. The Kubernetes Cluster Compromise Playbook covers the wider cluster response.

Common failure patterns

  • Deleting the bucket after migration: the cluster moves, the bucket is deleted, and a role trust still names its URL. The S3 name reuse warning applies directly.
  • A signing key in every backup: etcd and node snapshots carry the PKI directory to backup tiers with broad read access.
  • Shared keys across clusters: clusters built from one image or one installer run share a key, so one compromise mints tokens for all of them.
  • "Retired" keys left in the JWKS: the new key signs, but the old key stays published indefinitely because no one owns step 5.
  • Hand-edited JWKS files: a manual edit publishes a private key member or drops the active key, causing either exposure or an outage that leads someone to loosen trust conditions.
  • Issuer write access granted to the cluster: the cluster's own node role can write to the issuer bucket, so a node compromise lets an attacker add a key without touching the signing key.

Framework mapping

FrameworkReferenceHow it applies
MITRE ATT&CKT1552.004 Private Keys; T1606 Forge Web Credentials; T1584.001 DomainsTheft of the signing key, forging tokens with it, taking over the issuer hostname
NIST SP 800-53 Rev 5SC-12, SC-13Key generation, custody, rotation and destruction for the issuer key
NIST SP 800-53 Rev 5CM-3, SC-20Change control on JWKS, provider objects and issuer DNS
NIST SP 800-53 Rev 5AU-6, SI-4Correlating token exchanges with cluster issuance
NIST CSF 2.0PR.AA-01, ID.AM-08, DE.CM-09Managing workload credentials, issuer lifecycle and monitoring

Practitioner checklist

  • List every issuer URL trusted by any cloud account, tenant or project, with the cluster and owner for each.
  • Give each cluster its own signing key; move signing to an external signer backed by an HSM or KMS where your version supports it.
  • Keep the private key out of general backups, support bundles and images; block host path mounts of the PKI directory.
  • Host discovery documents in a dedicated location whose write access is limited to the rotation pipeline, with deletion protection and write logging.
  • Use account regional namespace buckets or a monitored domain with registrar lock and CAA records; never delete a bucket or domain an issuer URL used.
  • Validate every JWKS publish for private key members and an active kid before upload.
  • Run rotation as publish, switch, verify, wait, retire, including uploaded JWKS copies.
  • Correlate cloud token exchanges with Kubernetes TokenRequest audit events and alert on unmatched exchanges.
  • Alert on changes to OIDC provider objects, federated credentials, issuer DNS and certificates.
  • Keep a compromise runbook: new key, JWKS removal, session revocation in each cloud, log review for the exposure window.

Standards and references

Related NHI Mgmt Group resources: Workload Identity Federation Guide · Kubernetes Workload Identity Checklist · Workload Identity Federation Trust Review Checklist · Kubernetes Service Account Token Guide

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 11 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.