Skip to main content
4 min read·784 words

Structured Authorization Configuration in Kubernetes

Required knowledge for the CKS certification.

Kubernetes 1.32 graduated the AuthorizationConfiguration API to stable. It replaces the --authorization-mode and --authorization-webhook-* command-line flags with a declarative YAML file that lets you chain multiple authorizers in sequence, attach CEL match conditions to each webhook to restrict which requests it evaluates, and set per-webhook failure policies. Authorization configuration stored in a versioned file is auditable and reviewable in ways that scattered command-line flags are not.

This mechanism is distinct from admission control: AuthorizationConfiguration governs whether a request is allowed to proceed at all, before any admission webhook or ValidatingAdmissionPolicy runs. It does not replace ValidatingWebhookConfiguration or MutatingWebhookConfiguration.


1. The Legacy Flag Approach

Before AuthorizationConfiguration, the API server's authorization chain was specified entirely via command-line flags:

kube-apiserver \
--authorization-mode=Node,RBAC,Webhook \
--authorization-webhook-config-file=/etc/kubernetes/webhook-config.yaml \
--authorization-webhook-cache-authorized-ttl=5m \
--authorization-webhook-cache-unauthorized-ttl=30s

Before: All authorization settings live in API server flags — no versioned format, no per-webhook tuning, and only one webhook can be configured. Changing the authorization chain requires restarting the API server with different flags.
After: A single --authorization-config file replaces all those flags, supports multiple webhook entries, and can be managed as a Kubernetes configuration object in version control.

You cannot use both approaches simultaneously. Setting --authorization-config alongside --authorization-mode or --authorization-webhook-config-file causes the API server to exit on startup with an error.


2. The AuthorizationConfiguration File

Enable structured authorization by passing the file path to the API server:

kube-apiserver --authorization-config=/etc/kubernetes/authorization-config.yaml

The file uses apiVersion: apiserver.config.k8s.io/v1 and kind: AuthorizationConfiguration:

apiVersion: apiserver.config.k8s.io/v1
kind: AuthorizationConfiguration
authorizers:
- type: Node
name: node
- type: RBAC
name: rbac
- type: Webhook
name: custom-authz
webhook:
timeout: 3s
subjectAccessReviewVersion: v1
failurePolicy: Deny
connectionInfo:
type: KubeConfigFile
kubeConfigFile: /etc/kubernetes/webhook-authz.kubeconfig
authorizedTTL: 5m
unauthorizedTTL: 30s

Evaluation order: Authorizers run in the order listed. Each returns Allow, Deny, or NoOpinion. The first Allow or Deny terminates evaluation. If every authorizer returns NoOpinion, the request is denied (HTTP 403).


3. Authorizer Types and Webhook Fields

Authorizer entry fields

Every entry in authorizers requires type and name. The name field is used in metrics and must be a DNS label.

FieldDescription
typeNode, RBAC, Webhook, AlwaysAllow, or AlwaysDeny
nameDNS label, unique across the list, used in monitoring metrics
webhookRequired when type: Webhook; must be omitted for all other types

Webhook-specific fields

FieldDefaultDescription
timeoutMax 30 s. Required.
subjectAccessReviewVersionv1 or v1beta1. Required.
failurePolicyDeny or NoOpinion on webhook error or timeout. Required.
connectionInfo.typeKubeConfigFile or InClusterConfig. Required.
connectionInfo.kubeConfigFilePath to kubeconfig file. Required when type: KubeConfigFile.
authorizedTTL5mHow long to cache allowed decisions.
unauthorizedTTL30sHow long to cache denied decisions.
matchConditionsCEL conditions for selective webhook invocation. Optional.
matchConditionSubjectAccessReviewVersionRequired when matchConditions is non-empty; valid value: v1.

connectionInfo.type: InClusterConfig selects the in-cluster configuration to call the SubjectAccessReview API hosted by kube-apiserver; this mode is not allowed for kube-apiserver, so authorization webhook entries must use KubeConfigFile.


4. CEL Match Conditions

matchConditions attaches CEL expressions to a webhook entry. All expressions must evaluate to true for the webhook to be called; if any expression evaluates to false, the webhook is skipped and the next authorizer in the chain is tried.

webhook:
timeout: 3s
subjectAccessReviewVersion: v1
matchConditionSubjectAccessReviewVersion: v1
failurePolicy: Deny
connectionInfo:
type: KubeConfigFile
kubeConfigFile: /etc/kubernetes/webhook-authz.kubeconfig
matchConditions:
- expression: "has(request.resourceAttributes)"
- expression: "request.resourceAttributes.resource == 'secrets'"
- expression: "request.resourceAttributes.verb in ['get', 'list', 'watch']"

The CEL expressions evaluate the fields of a SubjectAccessReview object:

VariableAvailable forFields
request.userAll requestsUsername string
request.groupsAll requestsList of group strings
request.uidAll requestsUser UID string
request.extraAll requestsMap of extra user attributes
request.resourceAttributesResource requests.namespace, .name, .resource, .verb, .group, .version, .subresource
request.nonResourceAttributesNon-resource requests (e.g., /healthz).path, .verb

Use has(request.resourceAttributes) before accessing its sub-fields; it is unset for non-resource requests.

Security benefit: A webhook that only authorizes access to secrets does not need to see every pod list or node get request. CEL match conditions prevent unrelated request metadata from reaching external services, reducing the attack surface if the webhook service is compromised.

If a CEL expression itself errors and failurePolicy is Deny, the request is rejected. If failurePolicy is NoOpinion, the webhook is skipped.


5. Failure Policy

failurePolicy controls behavior when the webhook is unreachable, times out, or returns a malformed response:

Deny — Reject the request immediately without consulting remaining authorizers. Use for webhooks that are the authoritative gate for a specific resource type.

NoOpinion — Skip this webhook and continue to the next authorizer. Use for supplementary webhooks where unavailability should not block access.

Issue: A webhook with failurePolicy: NoOpinion that is the sole authorization control for a sensitive resource class allows those resources to remain accessible if the webhook fails.
Fix: Set failurePolicy: Deny for webhooks that enforce mandatory policy on sensitive resources. Deploy webhook services with sufficient replicas and PodDisruptionBudgets to prevent accidental lockout.


6. Migrating from Legacy Flags

To migrate from --authorization-mode flags to a configuration file:

  1. Create the file replicating the existing authorizer order:
apiVersion: apiserver.config.k8s.io/v1
kind: AuthorizationConfiguration
authorizers:
- type: Node
name: node
- type: RBAC
name: rbac
- type: Webhook
name: legacy-webhook
webhook:
timeout: 10s
subjectAccessReviewVersion: v1
failurePolicy: NoOpinion
connectionInfo:
type: KubeConfigFile
kubeConfigFile: /etc/kubernetes/webhook-config.yaml
authorizedTTL: 5m
unauthorizedTTL: 30s
  1. Write the file to each control-plane node (e.g., /etc/kubernetes/authorization-config.yaml).
  2. Update the static pod manifest for kube-apiserver (typically /etc/kubernetes/manifests/kube-apiserver.yaml): remove --authorization-mode, --authorization-webhook-config-file, --authorization-webhook-cache-authorized-ttl, and --authorization-webhook-cache-unauthorized-ttl, then add --authorization-config=/etc/kubernetes/authorization-config.yaml.
  3. The kubelet detects the manifest change and restarts the API server. Verify authorization behavior before considering the migration complete.

Note: Managed Kubernetes offerings (EKS, GKE, AKS) control the API server flags on your behalf. On those platforms, AuthorizationConfiguration adoption timelines depend on the provider's version rollout — check provider documentation before relying on this flag.


7. Verifying Authorization

After applying the configuration, use kubectl auth can-i to confirm expected behavior:

# Verify a service account still has a required permission
kubectl auth can-i get secrets \
--namespace=production \
--as=system:serviceaccount:production:app-sa

# Verify an unauthorized action is still denied
kubectl auth can-i delete nodes \
--as=system:serviceaccount:production:app-sa

# Verify a user permission across namespaces
kubectl auth can-i list pods \
--as=developer@example.com \
--namespace=staging

Expected output for an allowed action: yes. For a denied action: no.

If authorization behavior changes unexpectedly after migration, check that the authorizers order in the configuration file matches the previous --authorization-mode sequence — evaluation order is significant.



References

This article is based on information from the following official sources:

  1. Authorization - Kubernetes - Kubernetes
  2. Webhook Authorization - Kubernetes - Kubernetes