Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SIG Auth: Right-sized Access Control & Pull wit...

SIG Auth: Right-sized Access Control & Pull with Proof

Presented during KubeCon Amsterdam 2026.
Abstract:
Crank those propeller beanie‑caps to maximum lift, because once we breeze past the latest SIG Auth KEPs, we're zooming straight into two high‑octane technical adventures: the mysteries of ensure secret‑pulled images and the intricacies of conditional authorization. Prepare for turbulence, nerdy delight, and a guided tour through truly unruly authorization air currents.

Constrained Impersonation and Conditional Authorization finally allow you to escape "all-or-nothing" semantics in favor of right-sized access control policies. These features make it possible to define things like "only allow getting pods when impersonating a node", "Alice cannot set the 'sensitive' label on write requests" or "a controller can only add or remove its own finalizer, not others'".

Next, we'll explore "Ensure Secret Pulled Images," a long-awaited fix for a 10-year-old security gap. Learn how Kubelet now verifies credentials for cached images, ensuring that IfNotPresent doesn't mean "IfPresentForAnyone."

Whether you're managing access control policies, building controllers or securing multi-tenant clusters, this session will equip you with the knowledge to lock down your environment like never before.

Sched link: https://kccnceu2026.sched.com/event/4d59f77c5425685d3ed868124c70fd79
Recording: TBA
Location: Europaplein 24, 1078 GZ Amsterdam, Netherlands

Avatar for Lucas Käldström

Lucas Käldström

March 25, 2026

More Decks by Lucas Käldström

Other Decks in Technology

Transcript

  1. #KubeCon #CloudNativeCon SIG Auth: Right-sized Access Control & Pull with

    Proof Stanislav Láznička, Microsoft Lucas Käldström, Upbound
  2. KEP updates in 1.36 ❏ GA ❏ KEP 740 External

    signing for SA tokens ❏ KEP-2862 Fine-grained Kubelet API Authorization ❏ KEP-5018 DRA: AdminAccess ❏ KEP-5538 CSI driver: opt-in for service account tokens via secrets field ❏ Beta ❏ KEP 4317 Pod Certificates ❏ KEP-3104 Kuberc allowlist for client-go credential plugins ❏ KEP-5284 Constrained impersonation ❏ Secrets Store Sync Controller, in progress ❏ Alpha ❏ KEP-3926 Handling undecryptable resources ❏ KEP-5681 Conditional Authorization - KEP merged, implementation in review ❏ KEP-5917 (Still in review) Authenticator Provided Authorization Constraints
  3. Auth changes to image pulling • Kube 1.32 • Authentication

    ◦ Service Account Tokens for Kubelet Credential Providers (KEP-4412) ▪ Continues push for ephemeral credentials ▪ Cloud providers - workload identities ▪ IfNotPresent/Never pull policy unsafe! • Authorization ◦ Ensure Secret Pulled Images (KEP-2535) ▪ Authorize access to images on nodes
  4. Current solution • AlwaysPullImages admission controller ◦ Users must be

    aware of the issue ◦ Puts cluster egress and remote registries in the critical path ◦ Adds network overhead ◦ Requires unnecessary authentications
  5. What can we do? What information does Kubelet have when

    pulling images? type ImageManager interface { // EnsureImageExists ensures that image specified by `requestedImage` exists. EnsureImageExists(ctx context.Context, objRef *v1.ObjectReference, pod *v1.Pod, requestedImage string, pullSecrets []v1.Secret, podSandboxConfig *runtimeapi.PodSandboxConfig, podRuntimeHandler string, pullPolicy v1.PullPolicy, ) (imageRef, message string, err error) }
  6. The How • Don’t require pulling unless really necessary •

    Build image ⇐⇒ pull credentials mapping ◦ Persist it between kubelet restarts ◦ Query it for each IfNotPresent/Never pull ◦ Handle kubelet restarts mid-pull i. Pulling records ii. Pulled records • Preloaded images? ◦ 4 different policies: i. AlwaysVerify ii. NeverVerifyPreloadedImages iii. NeverVerifyAllowlistedImages iv. NeverVerify
  7. Things users see apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration imagePullCredentialsVerificationPolicy: NeverVerifyAllowlistedImages preloadedImagesVerificationAllowlist:

    ["test.test/repo/*"] /var/lib/kubelet/image_manager pulling/sha256-aef2af226629a35d5f3ef0fdbb29fdbebf038d0acd8850590e8c48e1e283aa56: {"kind":"ImagePullIntent","apiVersion":"kubelet.config.k8s.io/v1beta1", "image":"docker.io/testing/test:latest"} pulled/sha256-917e8b3439bf8a7a6f37ffd2d2ddfdfafac8a251bf214a0be39675742b420b1a: {"kind":"ImagePulledRecord","apiVersion":"kubelet.config.k8s.io/v1alpha1", "lastUpdatedTime":"2024-10-21T12:26:40Z","imageRef":"testimageref-sa", "credentialMapping":{"docker.io/testing/test":{ "kubernetesServiceAccounts":[ {"uid":"test-sa-uid","namespace":"default","name":"test-sa"}]}}}
  8. Read all about it • KEP 2535 design document •

    Kubernetes.io documentation page on images • Kubernetes.io blog post: Kubernetes v1.33: Image Pull Policy the way you always thought it worked!
  9. Current problem: “Over-grant” in RBAC, deny in VAP RBAC Role

    Allow in authorization RBAC Role Binding Allow in authorization CEL Policy Deny in admission create, update, delete gateways — object=* oldobject=* .class != ‘test-gateway’ .class == ‘test-gateway’ Kubernetes RBAC CEL Rule Amount of permissions for lucas Desired permissions
  10. Related: KEP-5284 Constrained Impersonation • Constrained Impersonation allows for a

    way to restrict what an impersonator can do. • Can be used for “on behalf of” semantics • Feature owners: @qiujian16, @enj Your permissions Impersonator’s permissions
  11. Kubernetes Enhancement Proposal 5681: Conditional Authorization Image credit Howdy! Is

    the request with <metadata> authorized? Authorizer new It depends. Only if secret.type == “tls”
  12. Use-cases - Allow a principal to only read Secrets with

    a given label - Allow a principal to update an object only when a sensitive field is unchanged
  13. Use-cases - Allow a principal to only read Secrets with

    a given label - Allow a principal to update an object only when a sensitive field is unchanged - Allow a principal to create objects only with certain names
  14. Use-cases - Allow a principal to only read Secrets with

    a given label - Allow a principal to update an object only when a sensitive field is unchanged - Allow a principal to create objects only with certain names - Allow a node agent to only access objects referring to them
  15. Use-cases - Allow a principal to only read Secrets with

    a given label - Allow a principal to update an object only when a sensitive field is unchanged - Allow a principal to create objects only with certain names - Allow a node agent to only access objects referring to them - Allow a controller to only add/remove its own finalizer
  16. Use-cases - Allow a principal to only read Secrets with

    a given label - Allow a principal to update an object only when a sensitive field is unchanged - Allow a principal to create objects only with certain names - Allow a node agent to only access objects referring to them - Allow a controller to only add/remove its own finalizer - Deny everyone except admins to set a sensitive field (e.g. GPU adminAccess)
  17. Recall admission control for write requests Authenticators Authorizers Metadata: API

    Group Resource Type Namespace Name Username User Groups User UID User Extra 401 403 Webhook OIDC CA Webhook RBAC
  18. Mutating/Validating Admission Controllers 40X CEL Webhook Metadata: API Group Resource

    Type Namespace Name Username User Groups User UID User Extra + Data: Payload/Body Previous Object Recall admission control for write requests Authenticators Authorizers Metadata: API Group Resource Type Namespace Name Username User Groups User UID User Extra 401 403 Webhook OIDC CA Webhook Storage 200 RBAC
  19. Recall admission control for read requests Authenticators Authorizers Metadata: API

    Group Resource Type Namespace Name Username User Groups User UID User Extra 401 403 Webhook OIDC CA Webhook Storage 200 RBAC
  20. Validating Admission Control For a request to succeed, all admission

    controllers must consider the object “valid” Admission control never gives permissions, but can only remove (deny policy) Webhook apiVersion: admission.k8s.io/v1 kind: AdmissionReview request: operation: CREATE name: foo-pod object: kind: Pod … response: allowed: true CEL
  21. Unified policies through Partial Evaluation What if it was possible

    to declare in one place that “lucas can create gateways when class=’test-gateway’”?
  22. Unified policies through Partial Evaluation What if it was possible

    to declare in one place that “lucas can create gateways when class=’test-gateway’”? In pseudo-code, it could look something like: “request.verb == ‘create’ && request.apiGroup == ‘gateway.networking.k8s.io’ && request.resource == ‘gateways’ && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  23. Unified policies through Partial Evaluation One trick is to use

    partial evaluation. Mark request object as “unknown”, but evaluate the expression as much as possible. If lucas does a kubectl create -f gateway.yaml, the following happens: “request.verb == ‘create’ && request.apiGroup == ‘gateway.networking.k8s.io’ && request.resource == ‘gateways’ && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  24. Unified policies through Partial Evaluation One trick is to use

    partial evaluation. Mark request object as “unknown”, but evaluate the expression as much as possible. If lucas does a kubectl create -f gateway.yaml, the following happens: “true && request.apiGroup == ‘gateway.networking.k8s.io’ && request.resource == ‘gateways’ && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  25. Unified policies through Partial Evaluation One trick is to use

    partial evaluation. Mark request.object as “unknown”, but evaluate the expression as much as possible. If lucas does a kubectl create -f gateway.yaml, the following happens: “true && true && request.resource == ‘gateways’ && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  26. Unified policies through Partial Evaluation One trick is to use

    partial evaluation. Mark request.object as “unknown”, but evaluate the expression as much as possible. If lucas does a kubectl create -f gateway.yaml, the following happens: “true && true && true && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  27. Unified policies through Partial Evaluation One trick is to use

    partial evaluation. Mark request.object as “unknown”, but evaluate the expression as much as possible. If lucas does a kubectl create -f gateway.yaml, the following happens: “true && true && true && true && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  28. Unified policies through Partial Evaluation One trick is to use

    partial evaluation. Mark request.object as “unknown”, but evaluate the expression as much as possible. If lucas does a kubectl create -f gateway.yaml, the following happens: “true && true && true && true && true && request.object.v1.spec.gatewayClassName == ’test-gateway’” Condition on the request object, defer evaluation to when decoded!
  29. Unified policies through Partial Evaluation Note that even though the

    object is unknown, in some cases partial evaluation can evaluate to a concrete true or false: For example, if user hacker executes kubectl create -f gateway.yaml “request.verb == ‘create’ && request.apiGroup == ‘gateway.networking.k8s.io’ && request.resource == ‘gateways’ && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  30. Unified policies through Partial Evaluation Note that even though the

    object is unknown, in some cases partial evaluation can evaluate to a concrete true or false: For example, if user hacker executes kubectl create -f gateway.yaml “true && request.apiGroup == ‘gateway.networking.k8s.io’ && request.resource == ‘gateways’ && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  31. Unified policies through Partial Evaluation Note that even though the

    object is unknown, in some cases partial evaluation can evaluate to a concrete true or false: For example, if user hacker executes kubectl create -f gateway.yaml “true && true && request.resource == ‘gateways’ && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  32. Unified policies through Partial Evaluation Note that even though the

    object is unknown, in some cases partial evaluation can evaluate to a concrete true or false: For example, if user hacker executes kubectl create -f gateway.yaml “true && true && true && request.userInfo.username == “lucas” && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’”
  33. Note that even though the object is unknown, in some

    cases partial evaluation can evaluate to a concrete true or false: For example, if user hacker executes kubectl create -f gateway.yaml “true && true && true && false && has(request.object.v1) && request.object.v1.spec.gatewayClassName == ’test-gateway’” Unified policies through Partial Evaluation Expression is always false, no matter what the object is! Don’t decode, instead Deny
  34. Kubernetes Authorization Kubernetes considers an ordered list (chain) of authorizers.

    Today, there are three decision modes: Allow, Deny, or NoOpinion. If Allow or Deny, enforce decision. If NoOpinion, consult next authorizer.
  35. Kubernetes Authorization with Conditions Kubernetes considers an ordered list (chain)

    of authorizers. Today, there are three decision modes: Allow, Deny, or NoOpinion. If Allow or Deny, enforce decision. If NoOpinion, consult next authorizer. Conditional Authorization adds a new decision mode: Conditional, along with a set of conditions. The request can proceed to the admission stage if the decision is Allow or Conditional with at least one condition that can make the request get authorized.
  36. Conditional Authorization: Data model A condition has the following properties:

    - id: Authorizer-unique identifier (for reason and error messages) - effect: Allow, Deny, or NoOpinion. How to treat “true”. - condition: String with some predicate on the request/stored objects. - type: What language the condition is written in (e.g. CEL, OPA, or Cedar)
  37. Conditional Authorization flow Authorization RequestInfo UserInfo RequestInfo UserInfo Body New!

    Conditions Kubernetes PR#137633 Authorizer Partial Evaluation Allow, Deny, or Conditions 403 Authentication
  38. Conditional Authorization flow Authorization RequestInfo UserInfo Admission Control Built-in condition

    enforcer RequestInfo UserInfo Body New! Conditions Body Kubernetes PR#137633 Authorizer Partial Evaluation Allow, Deny, or Conditions 403 403 Authentication Storage *If conditions are expressed in CEL, no need to ask the authorizer again Evaluate Conditions on New/Old Object *
  39. “Let lucas only read TLS secrets” *Image adapted from my

    and Micah’s KubeCon talk lucas lucas tls tls lucas
  40. Before KEP-4601, there was no way to say “authorize a

    read only with labelSelector ingress=true”. KEP-4601 Authorize with Selectors RBAC Authorization CEL/Webhook Admission Metadata Data Reads Writes
  41. With KEP-4601, expressing “authorize a read only with labelSelector ingress=true”

    is possible using a webhook authorizer (not builtin RBAC). KEP-4601 Authorize with Selectors RBAC Authorization CEL/Webhook Admission Metadata Data Reads Writes Field- and Label Selector Webhook Authorization
  42. Utilizing both KEPs together, makes it possible to express all

    types of authorization using the same, unified interface for the user. KEP-4601 Authorize with Selectors Unified Policy Authoring Interface For example, using CEL or Cedar Metadata Data Reads Writes