skip to content

In a Kyverno policy, what does the context block do and how do you use its value?

level: juniorimportance: must knowfreq 64%

answer

  1. the request does not carry it
  2. each entry gets a name
  3. ConfigMap, apiCall, imageRegistry
  4. referenced with double braces
  5. list lives outside the policy YAML

basics

~20 s

A Kyverno rule's context block fetches data the request itself does not carry - a ConfigMap, a live API call, or image registry metadata - binds each source to a name, and the rule then references it as a double-brace variable.

solid answer

~50 s

The `context` block is a list of named entries a rule resolves at admission time, before it decides. Each entry has a `name` and a source: `configMap` (a ConfigMap in a namespace), `apiCall` (a request against the cluster's own API), `imageRegistry` (metadata pulled for an image reference), or `variable` (a value or expression you compute). Kyverno binds the result to that name, and you reference it anywhere variables are allowed - preconditions, the `validate.pattern` or `deny` conditions, and the failure message - as `{{ placement.data.zones }}`. This is how a rule can enforce something the AdmissionReview does not contain: an allowed-placement list of node pools or zones lives in a platform-owned ConfigMap, so the platform team edits the list without editing or re-reviewing the policy. Two practical caveats: Kyverno's ServiceAccount needs RBAC to read whatever you point at, and ConfigMap values arrive as strings.

code

yaml · 29 lines
yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-placement
spec:
  rules:
    - name: zone-must-be-allowed
      match:
        any:
          - resources:
              kinds: [Pod]
      context:
        - name: placement
          configMap:
            name: allowed-placement        # data.zones = "euw1-a,euw1-b"
            namespace: platform-policy
      preconditions:
        all:
          - key: "{{ request.object.spec.nodeSelector.\"topology.kubernetes.io/zone\" || '' }}"
            operator: NotEquals
            value: ""
      validate:
        message: "zone must be one of {{ placement.data.zones }}"
        deny:
          conditions:
            all:
              - key: "{{ request.object.spec.nodeSelector.\"topology.kubernetes.io/zone\" }}"
                operator: AnyNotIn
                value: "{{ split(placement.data.zones, ',') }}"

go deeper

for a junior

Be ready to name the entry types - ConfigMap, API call, image registry - and to show the double-brace reference that pulls the value into a message or condition.

for a middle

Explain that resolution happens per request through the API server as Kyverno's own ServiceAccount, and that ConfigMap values are strings you often have to split.

for a senior

Show the judgment about what belongs in match versus preconditions versus context, and that a context lookup is a runtime dependency nothing validated when the policy was created.

for a principal

Own the tradeoff of putting decision data outside the policy: it buys the platform team a fast, low-ceremony edit and it moves part of the guardrail into something with its own access control.

## What the rule already knows, and what it does not When the API server calls Kyverno, it sends an AdmissionReview. That document carries the object being created or changed, the previous object on an update, the identity of the requester (user, groups, service account) and a `dryRun` flag. It carries **nothing else** - no other objects, no cluster state, no history. Any fact outside the request that your decision depends on has to be fetched, and in Kyverno that is what the `context` block is for. ## Shape of a context entry `context` sits inside a rule and is a list. Every entry has a `name` plus exactly one source: - **`configMap`** - `name` and `namespace` of a ConfigMap. The entry resolves to the object, so you read a key as `{{ <entryName>.data.<key> }}`. - **`apiCall`** - a `urlPath` against the cluster's own API (for example the Namespace of the request, whose labels the AdmissionReview does not include), usually narrowed with a `jmesPath` so you bind a small value rather than a whole list. - **`imageRegistry`** - a `reference` to a container image; Kyverno pulls the image's manifest and config metadata so a rule can assert on, say, a label baked into the image. - **`variable`** - a value or JMESPath expression, useful for naming an intermediate result so the rest of the rule reads cleanly. ## Referencing the value Anything Kyverno resolved is available as a `{{ }}` variable in the same rule: in `preconditions`, in `validate.deny` conditions or a `validate.pattern`, and in the `message` shown to the person whose request was refused. Putting the value in the message matters more than it looks - "zone must be one of euw1-a,euw1-b" tells a developer what to do next, where "placement policy violation" starts a support ticket. ## Why the indirection The worked example in the code block enforces that a workload only targets zones on a platform-owned allow list. The list is in a ConfigMap rather than in the policy, so adding a zone is a one-line data change owned by the platform team, and the policy YAML - which is the thing under change review - stays stable. Every team's Pods are governed by a rule none of them has to read. ## Three things that bite immediately **RBAC.** Kyverno reads the ConfigMap or makes the API call as its own controller ServiceAccount, through the API server, at admission time. Its shipped roles are deliberately narrow; if you point a context entry at something it may not read, the entry fails at runtime even though the policy was accepted at creation. You grant the extra read with an additional ClusterRole that Kyverno's own roles aggregate. **Types.** A ConfigMap `data` value is a string, always. `zones: "euw1-a,euw1-b"` is one string, not a list of two, so membership tests need `split(placement.data.zones, ',')` first. Storing the value as a JSON array in the ConfigMap is the other route. **Failure is not loud.** The policy is validated for structure when you create it; nothing checks that the ConfigMap exists or that the API path is readable. If the lookup fails at admission, that rule does not produce a violation - it produces an error result in the policy report, which a dashboard counting violations will happily show as zero problems. ## Preconditions and cost A context lookup happens per request on a matching object, so the cheapest control is a precise `match`/`exclude` block: kinds, namespace selectors and labels are evaluated before the rule body, and a rule that does not match is never processed at all. `preconditions` then express the cheap check over request fields - in the example, skip Pods that set no zone at all - so the rule only reaches its decision for requests it can actually judge. Getting the narrowing right in `match` first is what keeps an admission path predictable.

  • What does the AdmissionReview itself carry, and why does that force a context lookup?
    It carries the object under change, the old object on an update, the requesting user and groups, and a dryRun flag. It does not carry any other object, any cluster state or any history. So a rule that needs the namespace's labels, an allow list, or image metadata has to fetch it - through the context block - because that data is simply not in the request.
  • Does Kyverno need extra permissions to read that ConfigMap?
    Yes. The controller reads it through the API server as its own ServiceAccount, and Kyverno's shipped roles are narrow on purpose. You grant the extra read with an additional ClusterRole that Kyverno's roles aggregate. Nothing checks this when you create the policy - the entry simply fails at admission, so test the policy against a real cluster, not only in the CLI.
  • Where in a rule can you use a context variable?
    Anywhere variables are substituted in that rule: preconditions, validate.pattern or validate.deny conditions, and the message, as well as mutate and generate rule bodies. Resolution happens per request, so the value reflects the ConfigMap or API response as of that admission call, not as of when the policy was created.

saying these in an interview costs you the question

  • Thinks the AdmissionReview already contains the ConfigMap
  • Assumes a context entry needs no RBAC grant
  • Expects a ConfigMap value to arrive as a list
  • Uses a context variable without naming the entry
  • Believes policy creation verifies the ConfigMap exists

context