KubernetesLVII · AuthenticationAuthentication
Webhook authentication — TokenReview and external identity
What you'll learn
- Explain how the API server calls a webhook service for TokenReview
- Identify the use cases where webhook authentication is the right choice
- Implement a webhook authenticator (HTTPS service, TokenReview request/response)
- Operate the webhook with the right `failurePolicy` and observability
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
Webhook authentication is the most general authentication primitive Kubernetes supports. Instead of recognising credentials itself, the API server forwards the token to an external service and asks: is this token valid? The service returns either an authenticated UserInfo or an error. This lesson covers the implementation, the use cases, and the operational risks.
How webhook authentication works
The API server is configured with a webhook endpoint:
# AuthenticationConfiguration
apiVersion: apiserver.config.k8s.io/v1
kind: AuthenticationConfiguration
webhook:
- cacheTTL: 5m
service:
url: https://auth.example.com/tokenreview
caBundle: <base64-encoded CA>
failurePolicy: Deny
audience: kubernetes
anonymous: false
For each bearer token presented to the API server, the authenticator POSTs a TokenReview to the webhook:
POST https://auth.example.com/tokenreview
Content-Type: application/json
{
"apiVersion": "authentication.k8s.io/v1",
"kind": "TokenReview",
"spec": {
"token": "eyJhbGciOiJSUzI1NiIs...",
"audiences": ["kubernetes"]
}
}
The service responds with:
{
"apiVersion": "authentication.k8s.io/v1",
"kind": "TokenReview",
"status": {
"authenticated": true,
"user": {
"username": "alice@example.com",
"groups": ["dev", "oncall"]
}
}
}
Or, if the token is invalid:
{
"status": {
"authenticated": false,
"error": "token expired"
}
}
sequenceDiagram
participant W as Workload
participant AS as API server
participant WH as Webhook service
W->>AS: GET /api (Authorization: Bearer JWT)
AS->>AS: Try each authenticator
AS->>WH: POST /tokenreview (TokenReview spec)
WH->>WH: Validate JWT signature + audience + claims
WH->>AS: TokenReview status (authenticated: true, UserInfo)
AS->>AS: Apply RBAC
AS->>W: 200 OK (RBAC allowed) or 403 (denied)
The failurePolicy controls what happens when the
webhook is unreachable. Deny (the safe default)
means a webhook failure blocks authentication; Allow
means a webhook failure accepts the token as
authenticated (with system:anonymous UserInfo). Most
production clusters use Deny.
Use cases
Three places webhook authentication is the right choice:
- Custom IdP — the cluster is in an environment with a non-OIDC IdP (e.g., a custom SAML implementation, an internal Vault, or a homegrown token issuer). The webhook service translates the IdP’s tokens into UserInfo.
- Vault — the cluster authenticates with Vault’s service account tokens. Vault issues a JWT, the webhook validates the JWT against Vault’s public key, and returns UserInfo.
- SPIFFE / SPIRE — workloads have SPIFFE identities (SVIDs). The webhook validates the SVID and returns UserInfo based on the SPIFFE ID. This is the canonical pattern for zero-trust workload identity.
A standard OIDC integration does not need a webhook — the API server handles OIDC natively. The webhook is for the cases that OIDC cannot express.
Implementing a webhook
The webhook is an HTTPS service that accepts TokenReview. A minimal implementation in Go:
func tokenReview(w http.ResponseWriter, r *http.Request) {
var tr authv1.TokenReview
if err := json.NewDecoder(r.Body).Decode(&tr); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Validate the token against the IdP's JWKS
userInfo, err := validateToken(tr.Spec.Token, tr.Spec.Audiences)
if err != nil {
json.NewEncoder(w).Encode(authv1.TokenReview{
Status: authv1.TokenReviewStatus{
Authenticated: false,
Error: err.Error(),
},
})
return
}
json.NewEncoder(w).Encode(authv1.TokenReview{
Status: authv1.TokenReviewStatus{
Authenticated: true,
User: userInfo,
Audiences: tr.Spec.Audiences,
},
})
}
The webhook must:
- Verify the TLS certificate of the API server (the CA bundle is in the configuration).
- Validate the JWT against the IdP’s JWKS.
- Check the audience claim.
- Check the expiry.
- Return a UserInfo with a unique username (or
[email protected]-style) and the relevant groups.
Operating the webhook
Operational discipline:
- The webhook must be highly available. A single webhook Pod is a single point of failure. Run 3+ replicas behind a Service.
- The webhook must be observable. Every TokenReview should be logged with latency, success rate, and error reason. A slow webhook adds latency to every API request.
- The webhook must have a cache. A webhook that
re-validates every JWT on every request is a
bottleneck. The API server caches for
cacheTTL, but the webhook itself may need an upstream cache. - The webhook must be auditable. Every authentication decision should be logged. The IdP’s audit log is the source of truth for who has access.
Production failure modes
failurePolicy: Allow. A webhook outage makes the cluster fully open. The fix isDeny.- Webhook slow or unreachable. The API server times out the TokenReview, the request fails, the cluster is unavailable. The fix is HA + SLO on the webhook.
- Cache too long. A 5-minute cache means a
revoked token is accepted for up to 5 minutes. The
fix is to reduce
cacheTTLfor high-value tokens. - Webhook returns wrong username. A user is
authenticated but their username is
system:anonymous(the default fallback). The fix is to validate the response shape in the webhook.
Cross-course references
- The Observability course covers the audit log entries for webhook authentication failures.
- The Linux course covers TLS and JWKS that the webhook relies on.
Quiz
Knowledge check · 4 questions
Q1. Which `failurePolicy` value for webhook authentication is the dangerous default that makes the cluster fully open when the webhook is unreachable?
Q2. The API server's webhook cache (`cacheTTL`) means a token revoked in the IdP is rejected immediately by the cluster.
Q3. Your cluster uses webhook authentication against an internal Vault. A developer leaves the company and HR revokes their Vault token at 14:00. The developer attempts `kubectl get pods` at 14:03 and is authenticated. Why, and what is the revocation window?
The webhook cache `cacheTTL` is set to 5 minutes. The developer authenticated at 13:58 (before revocation). The cache for that token has 4 minutes remaining. The developer attempts requests at 14:02, 14:03, 14:04, 14:05 — all succeed until the cache expires at 14:03.
Q4. Name three use cases where webhook authentication is the right choice over OIDC or projected tokens.
Passing score: 75%. Answers are checked in this browser.
Production discipline
Webhook authentication is the right primitive when OIDC
is insufficient — when the identity source is Vault,
SPIFFE, a custom IdP, or a token-exchange system. The
discipline is: failurePolicy: Deny, HA replicas,
observable with latency + error metrics, audited
through the SIEM, and a cache tuned to the revocation
SLA. A cluster that uses webhook authentication with
failurePolicy: Allow is fully open to any attacker
who can disrupt the webhook. A cluster that uses
webhook authentication with failurePolicy: Deny and
a 1-minute cache has a revocation window of 1 minute,
which is the standard production target.