Skip to content
Kubernetes · OIDC authentication proxy

OIDC login for the clusters that won't let you set --oidc flags.

A reverse proxy in front of the API server. It validates bearer tokens from one or many OIDC issuers, then impersonates the user. Your RBAC stays authoritative on every request.

Apache-2.0OpenSSF ScorecardSigned OCI chartTested on 3 Kubernetes minorsArtifact Hub
Built for EKS, GKE, AKS and any cluster where the API server flags are out of reach.
Not for deciding what an identity may do. Authorization never leaves RBAC.
request flow · one token, one requestkubectl → proxy → kube-apiserver
OIDC issuersgoogle · github-actions · dexJWKS (cached)kubectlor CI jobkube-oidc-proxyverify sig · iss · aud · expmap claims → user, groupsown ServiceAccount + Impersonate-*kube-apiserverRBAC ✓Bearer ID tokenImpersonate-Userjwtno --oidc-* flags on the API server · no credential sharing · impersonation is gated by SubjectAccessReview
01 tokenkubectl sends the request with an OIDC ID token.
02 verifyProxy checks signature, issuer, audience, expiry.
03 impersonateClaims become a username and groups.
04 authorizeAPI server evaluates RBAC for that user.
How it works

Authentication moves to the proxy. Authorization never moves.

  1. Point kubectl at the proxyClients get a kubeconfig whose server is the proxy. The API server's own endpoint keeps working for everyone else.
  2. The proxy validates the tokenSignature, issuer, audience and expiry are checked against cached JWKS. Non-OIDC tokens can pass through via TokenReview.
  3. Claims become a Kubernetes identityUsername, groups and extras are mapped by claim or by CEL expression, with a prefix per issuer.
  4. The API server decidesThe request is forwarded with the proxy's own ServiceAccount plus Impersonate headers. RBAC runs for the mapped user, and the audit log records them.
What it changes: the proxy's ServiceAccount may impersonate any user, so its Deployment and RBAC are privileged. Treat it like the API server itself.
authentication-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AuthenticationConfiguration
jwt:
  - issuer:
      url: https://accounts.google.com
      audiences: [my-google-client]
    claimMappings:
      username: { claim: email, prefix: "google:" }
  - issuer:
      url: https://token.actions.githubusercontent.com
      audiences: [kube-oidc-proxy.example.com]
    claimMappings:
      username:
        expression: '"gha:" + claims.repository + ":" + claims.ref'
      groups:
        expression: '["gha:org:" + claims.repository_owner]'
What you get

More than a login. Identity, evidence and operations in one proxy.

Each capability is documented, tested end to end and exposed through the chart.

One proxy, many issuers, distinct identities

A Kubernetes AuthenticationConfiguration lists every issuer with its own audiences, CEL claim mappings and validation rules. The same proxy accepts tokens from all of them.

  • Per-issuer username and group prefixes keep alice from Google and alice from Dex apart.
  • Several audiences per issuer with audienceMatchPolicy: MatchAny.
  • Readiness you choose: ready on the first issuer, or wait for all with readinessRequireAllIssuers.
  • Refuses to start when two issuers share a host, so every log line and metric names its issuer unambiguously.
Multi-issuer guide →
token from GoogleATTRIBUTE  VALUE
Username   google:alice@example.com
Groups     [google:platform system:authenticated]
token from GitHub ActionsATTRIBUTE  VALUE
Username   gha:rafpe/kube-oidc-proxy:refs/heads/main
Groups     [gha:org:rafpe system:authenticated]
issuer readiness · log
# one record per change, not per scrape
{"event_type":"oidc.issuer.pending","issuer_name":"dex-b",
 "issuer_state":"pending","pending_reason":"discovery",
 "ready_issuers":2,"total_issuers":3}
Quickstart

Install the chart, ask the cluster who you are.

A smoke test, not a production install. The full first installation with ingress, TLS and RBAC is in Getting started.

A cluster, kubectl and Helm 3 or newer.
An OIDC issuer and an ID token from it in $ID_TOKEN.
The chart grants the proxy's ServiceAccount cluster-wide impersonation. Review the RBAC it renders before installing on a shared cluster.
No identity provider at hand? The kind demo stands up two Dex issuers and the proxy locally with one command.
ATTRIBUTE   VALUE
Username    google:alice@example.com
Groups      [system:authenticated]
Helm (OCI)
# install from the signed OCI chart
helm install kube-oidc-proxy oci://ghcr.io/rafpe/charts/kube-oidc-proxy \
  --namespace kube-oidc-proxy --create-namespace \
  --set oidc.clientId=<client-id> \
  --set oidc.issuerUrl=https://<issuer-url> \
  --set oidc.usernameClaim=email \
  --set oidc.usernamePrefix=google:

# verify: port-forward and ask the cluster who you are
kubectl -n kube-oidc-proxy port-forward svc/kube-oidc-proxy 8443:443 &
kubectl --server=https://127.0.0.1:8443 --insecure-skip-tls-verify \
  --token="$ID_TOKEN" auth whoami