Join our Newsletter — 33% off our NHI Course
Home› Guides› Kubernetes Structured Authentication Configuration Guide: Multiple JWT Issuers,…
Guide Cloud & Workload Identity

Kubernetes Structured Authentication Configuration Guide: Multiple JWT Issuers, CEL Claim Rules and CI Identity

PDF for newsletter subscribers · free to read online

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

The legacy --oidc-* flags let a Kubernetes API server trust exactly one JWT issuer with one client ID and one claim for the username, so most clusters either push every human through a single IdP or hand CI pipelines long-lived kubeconfig credentials. Structured authentication configuration replaces those flags with a versioned AuthenticationConfiguration file that can trust up to 64 JWT issuers, validate claims with CEL expressions, map identities precisely and reload without restarting the API server. This guide explains the file field by field, shows how to use it to let GitHub Actions and GitLab CI jobs authenticate with short-lived OIDC tokens, and covers rollout, detection and the mistakes that turn a flexible authenticator into a cluster-wide bypass. It is written for platform engineers and cluster operators who run their own control plane or a distribution that exposes API server flags.

Key takeaways

  • It is stable and the flags are now the legacy path. The StructuredAuthenticationConfiguration feature was alpha in v1.29, beta in v1.30 and stable in v1.34, with the gate locked on; the file is passed with --authentication-config.
  • You cannot mix the two models. Setting --authentication-config together with any --oidc-* flag makes the API server report an error and exit, so migration is a cut-over, not a blend.
  • CEL gives you policy at the front door. claimValidationRules reject tokens before mapping, claimMappings build username, groups, UID and extra attributes, and userValidationRules reject the final identity, for example any username starting with system:.
  • CI identity becomes keyless. A pipeline's OIDC token can be trusted directly by the API server and mapped to a narrow Kubernetes identity, replacing static service account tokens stored as CI secrets.
  • Most risk sits in the mapping, not the crypto. A missing audience check, a username claim that a tenant controls, or a CI rule that accepts any branch gives an attacker an authenticated identity with whatever RBAC you bound to it.

What changed from the --oidc flags

KEP-3331, which introduced the feature, describes the flag-based configuration as growing without bounds and lists what operators kept asking for: multiple client IDs, identifying users by more than one claim, mapping arbitrary claims to extra attributes, requiring group membership and running more than one provider without an external multiplexing proxy. The structured file answers each of these.

NeedLegacy flag behaviourStructured configuration
Number of issuersOne (--oidc-issuer-url)Up to 64 entries under jwt, each with a unique issuer.url
AudiencesOne exact match (--oidc-client-id)audiences list with audienceMatchPolicy: MatchAny
Required claims--oidc-required-claim key=valueclaimValidationRules with claim/requiredValue or a boolean CEL expression
UsernameOne claim plus prefixClaim plus prefix, or a CEL expression combining claims
GroupsOne claim plus prefixClaim plus prefix, or CEL returning a string or list
UID and extra attributesNot availableuid and extra[] with domain-prefixed keys
Final identity checksNot availableuserValidationRules evaluated against the user object
Change without restartNoFile is re-read automatically when it changes
Anonymous access--anonymous-auth on or off for all pathsanonymous.conditions limits anonymous requests to listed paths such as /livez, /readyz, /healthz

The file uses apiVersion: apiserver.config.k8s.io/v1 and kind: AuthenticationConfiguration. If you set anonymous in the file you cannot also set --anonymous-auth. Configurable anonymous endpoints (AnonymousAuthConfigurableEndpoints) also reached stable in v1.34. Managed services generally do not expose API server flags, so check your provider's own documentation for what it supports before planning around this file.

The issuer block, field by field

  • url: Required. Must match the token's iss claim, must be unique across all authenticators and must not conflict with the issuer set in --service-account-issuer.
  • discoveryURL: Optional override for {url}/.well-known/openid-configuration. The exact value is used, so include the well-known path. It must differ from url and be unique, and the issuer field returned by discovery must still match url.
  • certificateAuthority: PEM CA bundle for fetching discovery and keys, the same content as --oidc-ca-file. Leave it unset only when the IdP uses a publicly trusted certificate.
  • audiences and audienceMatchPolicy: At least one listed audience must appear in the token's aud. With more than one audience the policy must be MatchAny. Use a dedicated audience per cluster, never a generic value shared with other relying parties.
  • egressSelectorType: Optional; controlplane or cluster, matching your --egress-selector-config-file, for routing discovery, JWKS and distributed-claim traffic. It depends on the StructuredAuthenticationConfigurationEgressSelector gate, beta in v1.34.

The minimum token the API server will accept carries iss, aud, exp and the claim used for the username. When several authenticators are configured, the first one to succeed wins and the documentation states that order is not guaranteed, so never rely on ordering to resolve overlapping issuers.

CEL claim rules and identity mapping

CEL expressions in claimValidationRules, claimMappings and extra[].valueExpression see a claims map; expressions in userValidationRules see a user object (the resulting UserInfo). Distributed claims are not available to CEL.

StageFieldMust returnTypical use
1. Token checksclaimValidationRules[].expressionBoolean; all must be trueTenant or hosted-domain check, maximum token lifetime (claims.exp - claims.nbf <= 86400 in the upstream example), CI branch or environment restrictions
2. UsernameclaimMappings.usernameString; empty fails authenticationPrefixed subject, for example "gha:" + claims.sub
2. GroupsclaimMappings.groupsString or list of stringsPrefixed group names for RBAC bindings
2. UIDclaimMappings.uidStringImmutable subject identifier for audit correlation
2. ExtraclaimMappings.extra[]String or listCarry repository ID, environment or tenant to authorisation webhooks and audit logs
3. Identity checksuserValidationRules[].expressionBoolean; all must be trueBlock system: usernames and groups

Three rules in the reference documentation catch people out:

  • Prefixes are mandatory with claim mapping: when username.claim or groups.claim is set, a prefix is required; set it to an empty string deliberately if you mean none. Use a distinct prefix per issuer (corp:, gha:, gitlab:) so two issuers can never produce the same username.
  • Email needs verification: if the username expression uses claims.email, the configuration must also reference claims.email_verified somewhere (username expression, an extra value or a validation rule).
  • Extra keys are namespaced: keys must be lowercase, unique and domain-prefixed (example.com/tenant); k8s.io, kubernetes.io and their subdomains are reserved.

Treat userValidationRules as a mandatory backstop. The upstream example uses !user.username.startsWith('system:') and user.groups.all(group, !group.startsWith('system:')); without them, a mapping mistake or an IdP group an administrator can rename could yield membership of system:masters, which bypasses RBAC entirely.

Keyless CI identity: GitHub Actions and GitLab CI

CI platforms already issue OIDC tokens to jobs. Trusting them directly removes the most common cluster credential leak: a service account token or kubeconfig stored as a pipeline secret. The key design decision is which claims prove "this exact pipeline, on this protected ref, deploying to this environment".

PropertyGitHub ActionsGitLab CI
Issuer (issuer.url)https://token.actions.githubusercontent.comYour GitLab instance URL, for example https://gitlab.example.com
AudienceDefault is the owner URL; set a cluster-specific value with core.getIDToken(audience)Set per token in the job's id_tokens keyword (aud)
Subject formatrepo:ORG/REPO:environment:NAME, ...:ref:refs/heads/BRANCH, ...:pull_requestproject_path:GROUP/PROJECT:ref_type:branch:ref:main
Immutable identifiersrepository_id, repository_owner_id; repositories created after 15 July 2026 default to a subject containing owner and repository IDs (not on GitHub Enterprise Server)project_id (stable for the project's lifetime), namespace_id
Protection signalsenvironment, ref, job_workflow_ref, runner_environmentref_protected, environment, environment_protected, runner_environment
Token lifetimeShort-lived; not stated on the claims reference pageJob timeout if set, otherwise 5 minutes after issue

A defensible pattern for a production deploy job from GitHub Actions is a dedicated jwt entry for the GitHub issuer with a cluster-specific audience, and claim rules such as:

  • Pin the repository by ID, not name: claims.repository_id == "123456789", so a renamed or recreated repository cannot inherit trust.
  • Require a protected environment: claims.environment == "production", with required reviewers configured on that environment in GitHub.
  • Pin the workflow: compare claims.job_workflow_ref to the approved reusable deploy workflow at a protected ref, so a new workflow file in the same repository cannot mint a usable token.
  • Exclude pull requests: reject subjects ending in :pull_request.

Map the username to something like "gha:" + claims.repository + ":" + claims.environment, the UID to the repository ID, and add an extra attribute carrying the workflow ref for audit. Then bind that username to a namespaced Role with only the verbs the deploy needs. For GitLab, the equivalent rules are claims.project_id, claims.ref_protected == "true" and claims.environment_protected == "true"; check the claim types your instance actually emits before writing the comparison. See the GitHub Actions OIDC Federation Hardening Guide for the same claim choices applied to cloud trust policies, and the Kubernetes RBAC Guide for binding design.

Where identity fits

Every external issuer you add is a new population of non-human identities that can reach the API server. Record each jwt entry as an identity source in your NHI inventory with an owner, the RBAC it can reach and a review date, exactly as you would a cloud workload identity federation trust. The Workload Identity Federation Trust Review Checklist applies almost unchanged, and the Kubernetes NHI Security Guide covers the service account side.

Rollout and rollback

  1. Inventory current flags: capture every --oidc-* value, --anonymous-auth and --service-account-issuer from each control plane node, and list the RBAC bindings that reference the current username and group prefixes.
  2. Translate one to one first: write a file with a single jwt entry that reproduces the existing flags exactly (same issuer, audience, claim and prefixes), so existing bindings keep matching. Add userValidationRules blocking system: identities.
  3. Test in a non-production cluster: remove all --oidc-* flags and add --authentication-config. Confirm the server starts, then have test users run kubectl auth whoami to see the username, groups and extra values actually produced.
  4. Write negative tests: tokens with the wrong audience, an expired exp, a missing claim, a CI token from a pull request and from an unprotected branch must all return 401 Unauthorized.
  5. Cut over production node by node: on a multi-node control plane, update one API server at a time and watch authentication errors before continuing.
  6. Add new issuers one at a time: append a CI issuer, deploy the file atomically (write a temporary file, then rename it over the original) and let automatic reload apply it. Check the reload metrics before granting any RBAC.
  7. Roll back: keep the previous file under version control. For a bad rule, restore the previous file and let reload apply it; to return to flags, remove --authentication-config and restore the --oidc-* flags with a restart, since the two cannot coexist.

Detection and measurement

KEP-3331 defines metrics for the reload controller and each JWT authenticator. Alert on these and on API server audit events.

SignalSourceWhat it tells you
apiserver_authentication_config_controller_automatic_reload_failures_totalAPI server metricsA changed file failed to load; the server keeps the last good config, so a failed change can go unnoticed
apiserver_authentication_config_controller_automatic_reload_last_config_hashAPI server metricsCompare across control plane nodes; differing hashes mean nodes enforce different rules
apiserver_authentication_jwt_authenticator_latency_secondsAPI server metricsCEL or JWKS latency per issuer
apiserver_authentication_jwks_fetch_last_timestamp_seconds and ..._last_keyset_hashAPI server metricsStale key fetches or unexpected key rotation at an issuer
Audit events with external usernamesKubernetes audit log (user.username, user.extra)Which CI identity did what; unexpected verbs or namespaces
Validation failure messagesAPI server logs (the rule's message)Repeated failures from one issuer or subject, a sign of probing or misconfigured pipelines

Useful KPIs: number of static service account tokens or kubeconfigs stored in CI systems (target zero for clusters with structured authentication); percentage of jwt entries with a userValidationRules backstop (target 100%); percentage of CI issuer entries pinned by immutable repository or project ID; reload failures in the last 30 days; and time from an issuer being retired to its removal from the file. Audit log detection patterns are in the Kubernetes Audit Log Detection Guide.

Decision guidance

  • Structured file versus an authentication proxy: if you run your own control plane on v1.30 or later, the file removes the need for a proxy that impersonates users. Keep a proxy only when you need features outside JWT authentication, such as session recording.
  • Direct CI trust versus a broker: trusting CI issuers directly is simplest for a few clusters. With dozens of clusters and many teams, a central token broker that exchanges CI tokens for cluster-scoped tokens may be easier to govern, at the cost of a new critical component.
  • Claim rules versus RBAC: use CEL to decide who is allowed to authenticate at all (tenant, repository, protected ref), and RBAC to decide what they can do. Do not encode permissions in CEL.
  • Small versus large estates: a small team can keep one file in Git per cluster, with peer review and sample-token tests. Large estates should template the file from a central list of approved issuers so a retired issuer is removed everywhere at once.

Common failure patterns

  • Trusting the repository, not the workflow: a rule that only checks the repository name lets any workflow, branch or compromised third-party action in that repository mint a usable token. The tj-actions/changed-files compromise showed how a poisoned action runs inside trusted workflows; pin environment and job_workflow_ref so such code cannot reach production.
  • Shared or generic audience: accepting the GitHub default audience or a value used by other relying parties means a token minted for one service can be replayed against the cluster.
  • No prefix separation: two issuers mapped with empty prefixes can produce the same username, and RBAC cannot tell them apart.
  • Missing system: backstop: without userValidationRules, a group claim an IdP administrator controls can be mapped into system:masters.
  • Silent reload failure: an edited file that fails validation leaves the old rules in force while the change ticket says otherwise. Alert on the reload failure counter.
  • Leaving the old credentials in place: teams add keyless CI but never delete the stored kubeconfig, so the static secret remains the weakest path.

Framework mapping

FrameworkReferenceHow this guide applies
NIST CSF 2.0PR.AA-01, PR.AA-03Managing identities for services and authenticating them to the API server
NIST SP 800-53 Rev. 5IA-5, IA-8, AC-2, CM-6, AU-2Short-lived authenticators, external identities, account management, configuration settings and audit
CIS Controls v8.1Control 5 (Account Management), Control 6 (Access Control Management)Inventory of CI identities and least-privilege bindings
ISO/IEC 27001:2022A.5.16, A.5.17, A.8.5, A.8.9Identity management, authentication information, secure authentication, configuration management
MITRE ATT&CKT1078.004 Cloud Accounts, T1550.001 Application Access TokenAbuse of valid cloud identities and replay of stolen tokens that strict audience and claim rules limit

Practitioner checklist

  • Confirm the cluster version (v1.30 or later; stable from v1.34) and whether your platform exposes --authentication-config.
  • Translate existing --oidc-* flags one to one before adding anything new, and remove the flags entirely at cut-over.
  • Give every issuer a distinct username and group prefix and a cluster-specific audience.
  • Add userValidationRules rejecting system: usernames and groups to every configuration.
  • Pin CI identities by immutable repository or project ID, protected environment or ref, and workflow ref; reject pull request subjects.
  • Restrict anonymous access to health endpoints with anonymous.conditions.
  • Deploy the file atomically, keep it in version control with negative test tokens, and alert on reload failures and config hash drift between nodes.
  • Delete stored kubeconfigs and service account tokens from CI once keyless authentication is proven.
  • Review every jwt entry and its RBAC bindings at least quarterly.

Standards and references

Related NHI Mgmt Group resources: Kubernetes Security Guide · Kubernetes Service Account Token Guide · Self-Hosted OIDC Issuer Security Guide · Workload Identity Federation 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.