In a Kyverno pattern, why does an anchored (resources) key let every container through?
answer
- the anchor is an if, not a check
- peers carry the assertion
- a block with only an anchor asserts nothing
- mismatch skips silently, it does not deny
- anchor the selector, not the field you require
basics
~10 sA conditional anchor is a condition, not an assertion. (resources) only says: if resources matches, check my peer keys. With no peer keys there is nothing to check, so every container is admitted.
solid answer
~40 sIn a Kyverno pattern, `(key): value` is the conditional anchor and it works like an `if`. If the key exists on the resource and its value matches, Kyverno evaluates the **peer** keys in that same block; if the key is absent or the value does not match, that block is skipped and no violation is recorded. So `(resources): {limits: {memory: "?*"}}` is read as "if this container has a memory limit, then check my peers" — and if there are no peers, the rule asserts nothing at all and admits everything, including containers with no `resources` block. The fix is to move the assertion out of the anchor: anchor the thing that *selects* elements, such as `(name): "!istio-proxy"` to skip an injected sidecar, and leave `resources.limits.memory` unanchored so it is genuinely required.
code
yaml · 17 lines# BROKEN - the anchored key is the only key, so the rule asserts nothing
pattern:
spec:
containers:
- (resources):
limits:
memory: "?*"
---
# FIXED - condition in the anchor, assertion as its unanchored peer
pattern:
spec:
containers:
- (name): "!istio-proxy" # skip the injected sidecar only
resources:
limits:
memory: "?*"
ephemeral-storage: "?*"go deeper
Recall that parentheses around a key make it a condition rather than a requirement, and that a condition which does not hold means the check is skipped, not failed.
Explain the if/then structure precisely: the anchored key gates its peer keys, so a block containing only an anchor can never produce a violation. Be able to point at the peer that carries the assertion.
Diagnose a rule that passes everything, and pick between the conditional, equality and global anchors by blast radius: skip a list element, fail on a mismatched value, or skip the whole rule.
Set the expectation that every anchor in the shared policy library is a deliberate exemption with a written reason, since an anchor is where a guardrail quietly stops applying to somebody.
## The anchor is the `if`, the peers are the `then` The conditional anchor `()` is the anchor authors reach for first and misread most often. Its contract is: > If the anchored key is present on the resource **and** its value matches the pattern, then the sibling (peer) keys in the same block must be satisfied. Otherwise, skip this block without failing. Read that twice, because two things follow that are not obvious. First, the anchored key is never itself an assertion — it can only enable or disable one. Second, the assertion has to live *next to* the anchor, as a peer, not *inside* it. The canonical shape is a pair: ```yaml containers: - (name): "!istio-proxy" # condition: any container that is not the sidecar resources: # assertion: ...must have these limits: memory: "?*" ``` ## The failure in the question Now look at the broken version. `(resources)` is anchored and it is the only key in the block. Kyverno evaluates the condition — does this container have `resources.limits.memory` set to a non-empty value? — and then looks for peers to check. There are none. Whether the condition held or not, the outcome is the same: nothing was asserted, no violation was produced, the container is admitted. A container with no `resources` at all sails through, and so does one with `resources.requests` but no limits. This is worse than a rule that is merely wrong, because the rule *looks* right. It mentions the field, the value expression is correct, the message is well written, and every deploy passes. Kyverno will not warn you that a rule can never produce a violation; a pattern with a top-level anchor and no peers is syntactically fine. ## The three gating anchors, and how they differ - `()` **conditional** — key absent: skip. Key present, value mismatched: **skip**, silently. Key present, value matched: evaluate the peers. - `=()` **equality** — key absent: skip. Key present, value mismatched: **fail**. This is the anchor for "optional, but if it is set it must look like this". The difference from `()` is entirely about what a mismatched value means: a silent skip versus a violation. - `<()` **global** — its condition is evaluated for the rule as a whole. When the condition is not met, Kyverno skips the **entire rule**, not just the block the anchor sits in. Use it when a whole rule should apply only to resources meeting some condition; use `()` when only a block or a list element should be skipped. Choosing between them is a matter of blast radius. A `()` on a container name skips one element of a list. A `<()` skips every check in the rule for that object. ## Why the sidecar case needs an anchor at all A reasonable objection: the policy already has a `match` block, so why not exclude the sidecar there? Because `match` and `exclude` select **objects** — kinds, namespaces, labels, subjects — not elements inside an object's arrays. An injected proxy container lives inside a pod you very much want the rule to cover; you only want to spare that one array element. That is exactly what a conditional anchor on a peer key of the list element does, and the `!` operator in the value is what turns a name match into a name exclusion. Be honest about the cost of that exemption when you write it. `(name): "!istio-proxy"` means anything a team names `istio-proxy` is now outside the rule. If that matters, narrow it further with a second condition, or accept it and write the reason in a comment above the anchor so the next reviewer does not have to guess. ## The habit that prevents this Before you commit a pattern, read it aloud as an if/then sentence, naming the peers. "If the container is not the sidecar, then it must have a memory limit and an ephemeral-storage limit." If the sentence has no *then* — if the only thing in the block is the anchored key — the rule asserts nothing, and you have written a comment, not a policy. And when a rule does fire, the rejection message carries the path it failed at, such as `/spec/containers/0/resources/limits/memory/`, which is what you hand to the developer whose deploy just stopped.
- How does the equality anchor =() differ from the conditional anchor ()?Both skip when the key is absent. They diverge when the key is present but its value does not match: `()` silently skips the peer checks, while `=()` records a violation. So `()` means "only check other things when this holds", and `=()` means "this is optional, but if it is set it must look like this" — the right anchor for an optional sub-object you still want validated.
- What does the global anchor <() change about the skip?It widens it. A conditional anchor skips only the block or list element it sits in; when a global anchor's condition is not met, Kyverno skips the whole rule for that resource. Reach for `<()` when the entire rule should not apply to a class of objects, and for `()` when you only want to spare one part of the object.
- Why not exclude the sidecar in the policy's exclude block instead?Because `match` and `exclude` select whole objects — kinds, namespaces, labels, subjects — not elements inside an array. The sidecar is a container inside a pod the rule should still cover; excluding the pod would drop the check for its application containers too. Skipping one list element is a job for a conditional anchor on that element.
saying these in an interview costs you the question
- Reads () as this field is required
- Puts the assertion inside the anchor's own value
- Thinks a failed anchor condition denies the request
- Expects Kyverno to warn that a rule asserts nothing
- Confuses the exclude block with skipping a list element