In a Kyverno rule, how do the match and exclude blocks decide which resources it applies to?
answer
- two blocks, one selects and one subtracts
- any is OR, all is AND
- inside one entry, conditions are ANDed
- you can match the requester, not just the object
- requester identity forces background off
basics
~10 sThe match block selects which resources and requesters a Kyverno rule applies to; the exclude block subtracts from that set. Anything matching both is skipped, so exclude always wins.
solid answer
~40 sBoth blocks take an `any` list (at least one entry must match) or an `all` list (every entry must match). Each entry is a filter that can select on `resources` — `kinds`, `names`, `namespaces`, a label `selector`, annotations, `operations` — and on the requester via `subjects`, `roles` or `clusterRoles`. Conditions inside a single entry are ANDed, so an entry naming a kind and a namespace means that kind *in* that namespace. `exclude` is subtractive and takes precedence: a resource that satisfies both blocks is not processed by that rule. The everyday shape is a broad match plus a narrow exclude — match Deployments cluster-wide, exclude `kube-system` and one named controller ServiceAccount that legitimately creates non-conforming objects. Matching on the requester is admission-only information, so a policy that uses it must set `background: false`.
code
yaml · 17 linesmatch:
any:
- resources:
kinds:
- Deployment
namespaces:
- "team-*"
exclude:
any:
- resources:
namespaces:
- kube-system
- subjects:
- kind: ServiceAccount
name: platform-migrator
namespace: platform-system
# ... validate block omittedgo deeper
Be ready to say that match picks the resources a rule applies to and exclude carves out exceptions, and that anything hitting both is skipped. Recognising kinds, namespaces and label selectors in a match block is enough at this level.
Explain the mechanics precisely: any is an OR across entries, all is an AND, and conditions inside one entry are ANDed. Be able to write the two-entry form for a selection that a single entry cannot express.
Show the judgment behind the shape: broad match plus explicit exclude so new namespaces stay covered, short and specific subject exclusions, and awareness that a rule using requester identity gives up background evaluation of existing objects.
Own the exclusion register. Every excluded namespace and ServiceAccount is a standing bypass someone must review; argue for who approves them, how they are kept short, and why a stale namespace allow-list is a worse risk than an explicit exception.
## What the two blocks are for Every Kyverno rule answers two questions before it decides anything: *does this rule apply here?*, then *does the object satisfy it?* `match` and `exclude` answer the first. Getting them wrong is the most common cause of both false positives (a rule firing on objects you never meant) and silent gaps (a rule that never fires at all). ## Structure Both `match` and `exclude` take one of two list forms: - **`any`** — the block matches if **at least one** entry matches (a logical OR); - **`all`** — the block matches only if **every** entry matches (a logical AND). Each entry in the list is a filter that may carry: - **`resources`** — `kinds`, `names`, `namespaces`, a label `selector`, `annotations`, and `operations` (CREATE, UPDATE, DELETE, CONNECT). Wildcards are allowed in names and namespaces, so `team-*` selects a family of namespaces. - **`subjects`** — the identity making the request, as a Kubernetes subject: a User, Group or ServiceAccount. - **`roles`** / **`clusterRoles`** — the roles bound to that identity. Conditions **within a single entry are ANDed**. An entry listing `kinds: [Deployment]` and `namespaces: [team-a]` means *Deployments in team-a*, not *Deployments anywhere, or anything in team-a*. To express the OR you need two entries under `any`. ## Exclude subtracts, and it wins `exclude` is evaluated against the same object and the same requester. If both blocks match, the rule is skipped for that request. There is no ordering subtlety to reason about: exclude is not "applied first" or "applied last", it is a subtraction from whatever `match` selected. The practical shape almost every real policy takes is a **broad match plus a narrow exclude**. Take the availability guardrail — a Deployment with more than one replica must declare a readiness probe. You match `Deployment` everywhere, because the point is that it holds everywhere. Then you carve out: - system namespaces such as `kube-system`, whose objects you do not own and cannot fix; - one named controller ServiceAccount that legitimately creates Deployments the rule would otherwise reject. Writing that as a narrow match instead — listing the namespaces you *do* want — looks equivalent and is not: every namespace created after you wrote the policy silently escapes the guardrail. Broad match plus explicit exclude fails in the safe direction, and the exclude list doubles as a readable, reviewable record of every hole in the rule. ## Matching on who is asking `subjects`, `roles` and `clusterRoles` match on the identity in the admission request rather than on the object. This is how you exempt a controller: a platform migration job running as `system:serviceaccount:platform-system:platform-migrator` is excluded, while a human applying the same manifest is not. Two things follow, and interviewers probe both. First, **identity is admission-only information**. Kyverno's background scan re-evaluates existing objects, and there is no requester to inspect at that point — the object was created hours ago by someone the API server is no longer telling anyone about. A policy whose match or exclude uses `subjects`, `roles` or `clusterRoles` therefore has to declare `background: false`; Kyverno rejects the policy otherwise. The practical cost is that the rule no longer contributes results for pre-existing resources: it only guards new and updated ones. Second, **a subject exclusion is a standing bypass**. Anything acting as that identity skips the rule, not just the workflow you had in mind. Keep the excluded subject list short, name specific ServiceAccounts rather than broad groups, and prefer excluding a namespace you control over excluding an identity that can act anywhere. ## Failure modes worth naming - **Kind without group/version ambiguity.** If two API groups serve the same kind name, spell the kind out fully so the rule binds to the one you meant. - **Assuming `any` when you wrote `all`.** An `all` block with two entries that can never both be true matches nothing, and a rule that matches nothing looks exactly like a rule that passes. - **Forgetting `operations`.** A rule that should only guard creation will also fire on every update unless you say so, which turns unrelated edits to an old object into a blocked request. - **Excluding by namespace when you meant by identity, or the reverse.** Excluding `kube-system` does not exempt a controller that creates objects in tenant namespaces; excluding the controller's ServiceAccount does not exempt objects a human creates in `kube-system`.
- Why does a policy that excludes a ServiceAccount subject have to set background to false?Because the requester's identity only exists in the admission request. A background scan re-evaluates objects that already exist, with no user info to compare against, so Kyverno cannot honour a subject-based match or exclude there and refuses to run the policy in background mode. The rule then only covers new and updated objects.
- You want a rule to fire on Deployments in team-a and on StatefulSets anywhere. How do you express that?Two entries under `match.any`: one with `kinds: [Deployment]` and `namespaces: [team-a]`, another with `kinds: [StatefulSet]`. Conditions inside a single entry are ANDed, so putting both kinds and the namespace in one entry would instead mean *Deployments and StatefulSets, only in team-a*.
- Is it better to list the namespaces you want in match, or to match broadly and exclude?Match broadly and exclude. A namespace list goes stale the moment someone creates a namespace, and the guardrail silently stops covering it. A broad match with an explicit exclude fails in the safe direction, and the exclude list is a reviewable record of every exemption you granted.
saying these in an interview costs you the question
- Thinks conditions inside one match entry are ORed
- Believes match is evaluated after exclude and overrides it
- Lists wanted namespaces in match instead of excluding unwanted ones
- Does not know a rule can match on the requesting identity
- Expects subject-based rules to work during background scans