skip to content

In a Kyverno validate.deny block, how do the any and all condition lists decide whether a request is blocked?

level: middleimportance: must knowfreq 55%

answer

  1. all is AND, any is OR
  2. true means blocked, not allowed
  3. partial match decides the two apart
  4. both keys present means both satisfied
  5. bare list behaves like all

basics

~20 s

Under all, every condition must be true; under any, at least one must be true. When the block comes out true the request is denied with the rule's message. If both lists appear in the same block, both have to be satisfied.

solid answer

~50 s

`deny.conditions` accepts two grouping keys. `all` is a logical AND — every `key`/`operator`/`value` condition must hold before the request is denied. `any` is a logical OR — one true condition is enough. Both are then read as a deny, so a true result means blocked with `validate.message`, not allowed. If a block carries both `any` and `all`, both groups must be satisfied for the deny to fire. The practical consequence is what happens to a partially-matching change: a resource that trips one of three conditions is blocked under `any` and sails through under `all`. That is why the grouping choice, not the operators, is usually where a rule is wrong — an author who means "this is bad only when all of these hold together" and writes `any` blocks half the estate, and the reverse mistake produces a policy that never fires and looks healthy.

code

yaml · 13 lines
yaml
validate:
  message: "PersistentVolumeClaims must use an approved encrypted StorageClass."
  deny:
    conditions:
      all:
      - key: "{{ request.operation }}"
        operator: Equals
        value: CREATE
      - key: "{{ request.object.spec.storageClassName }}"
        operator: AnyNotIn
        value:
        - encrypted-gp3
        - encrypted-io2

go deeper

for a junior

Memorise the two words and the polarity: all needs every condition true, any needs one, and a true block rejects the request. Being able to say which of the two a plain-English requirement calls for is enough at this level.

for a middle

Be ready to walk a partially-matching resource through a three-condition block and say whether it is blocked under each grouping, and to name the operator you would use for set membership rather than hand-waving at equality.

for a senior

Show that you treat the inverted rule as the default suspicion: explain how you prove a deny rule still fires, since a silent policy and a compliant estate produce the same reports.

for a principal

Speak to how the organisation avoids shipping inverted rules at all — required fixtures beside every policy, a second reviewer for grouping-key changes, and a house preference for splitting a nested block into two plain rules.

## The grouping keys are the rule's logic A Kyverno `validate.deny` block holds `conditions`, and `conditions` is a map with two possible keys, `any` and `all`. Each holds a list of conditions of the form: ``` - key: "{{ <JMESPath over the admission request> }}" operator: <Equals | NotEquals | AnyIn | AnyNotIn | AllIn | AllNotIn | GreaterThan | ...> value: <scalar or list> ``` Kyverno substitutes the `{{ }}` expressions from the admission request first, then evaluates each condition, then combines them: - **`all`** — logical AND. Every condition in the list must be true. - **`any`** — logical OR. At least one condition must be true. - **Both present** — both groups must be satisfied: every `all` condition true, and at least one `any` condition true. - A bare list of conditions with no grouping key is the older form and behaves as `all`. And then the crucial step: the combined result is a **deny**. True means the request is rejected with `validate.message`. False means the rule stays silent and the request proceeds. ### Reading a rule out loud Take a rule that keeps PersistentVolumeClaims on an approved encrypted StorageClass. Written under `all`, it says: *deny when this is a CREATE **and** the named class is not one of the approved ones.* Both facts must hold together, which is what you want — an update to an existing claim, or a claim naming an approved class, is left alone. Swap `all` for `any` without touching a single condition and the meaning collapses: *deny when this is a CREATE **or** the class is not approved.* Every CREATE of every PVC is now blocked, including the compliant ones. Nothing about the conditions themselves changed; the grouping key did all the damage. This is why the grouping key deserves as much review attention as the operators. ### The mirror-image failure The opposite mistake is quieter and therefore worse. Suppose you want to block a Service that is internet-facing **or** that names a forbidden port range — genuinely an OR. Written under `all`, the rule fires only when both hold at once, so an internet-facing Service on an ordinary port passes. Nothing errors. The policy shows as installed, the reports look green, and the control simply is not doing its job. A deny rule is silent unless it fires, so "no denials this week" is equally consistent with "everyone is compliant" and "the rule is inverted". The only way to tell the two apart is a fixture: keep a resource beside the policy that **must** be blocked, and one that **must** pass, and check both whenever the rule changes. ### Partial matches are the interview scenario Interviewers phrase this as a scenario rather than a definition: *three conditions, a change satisfies two of them — blocked or not?* The answer is "under `any`, blocked; under `all`, admitted", and the follow-up is which one the stated intent needs. Work from the sentence the security requirement is written in: "and" between the facts means `all`, "or" means `any`. When the requirement contains both — say, only on CREATE or UPDATE, and only when the class is unapproved — that is exactly the case for putting the operation check in `any` and the class check in `all` within the same block. ### Polarity when you move to the CEL form Kyverno's validate rule also accepts a CEL form, whose `expressions` list holds an `expression` and a `message` each. The polarity there is the **opposite** of a deny block: a CEL expression that evaluates to **true** means the resource is **allowed**, and false is what rejects it, matching the upstream ValidatingAdmissionPolicy convention. Authors who move a rule from one form to the other and forget to negate it produce a policy that does precisely the wrong thing, so it is worth saying the polarity out loud every time you write one: *deny conditions true blocks; a CEL expression true admits.* ### Operators still matter The operator vocabulary covers equality (`Equals`, `NotEquals`), set membership (`AnyIn`, `AllIn`, `AnyNotIn`, `AllNotIn`) and numeric comparison (`GreaterThan`, `LessThan` and their or-equals forms). Set operators are where the second inversion hides: `AnyIn` against the list of *approved* values, used inside a deny, blocks the approved resources. Inside a deny block the list you write is nearly always the forbidden set, or the approved set reached through a `NotIn`-style operator.

  • If a block carries both an any list and an all list, when does it deny?
    Only when both groups are satisfied — every condition in `all` true, and at least one in `any` true. It is the natural way to write "on CREATE or UPDATE, and only when the value is unapproved": the operation alternatives go under `any`, the value check under `all`. If you find yourself needing anything more nested than that, it is usually a sign the rule should be split into two rules.
  • How does the polarity change if the same rule is written in Kyverno's CEL form?
    It inverts. A deny block blocks when its conditions are true; a CEL expression in a validate rule allows when it is true and rejects when it is false, following the upstream ValidatingAdmissionPolicy convention. Translating a rule between the two forms therefore means negating it, and forgetting that is one of the easiest ways to ship a policy that does exactly the opposite of its message.
  • Your deny rule has not blocked anything in a month. How do you tell a working rule from an inverted one?
    You cannot tell from the absence of denials — a silent rule and a compliant estate look identical. Keep a fixture pair checked in beside the policy: a resource that must be rejected and one that must pass, both evaluated whenever the rule changes. That turns an inverted `any`/`all` or a flipped set operator into a failing check rather than a control that quietly stopped working.

saying these in an interview costs you the question

  • Says any means AND because the list is longer
  • Reads a true condition block as an allow
  • Puts the approved value list under AnyIn inside a deny
  • Assumes zero denials proves the rule works
  • Thinks both any and all present means either one suffices

context