Kyverno generator.apply() cross-namespace authority — operator validation¶
Date reviewed: 2026-08-27
Advisory: GHSA-79gf-7frw-68m9 / CVE-2026-54523
Severity: Critical
Affected: github.com/kyverno/kyverno >= 1.18.0, <= 1.18.1 (patched 1.18.2)
Boundary class: tenant CEL generator.apply(namespace, …) → unvalidated namespace → background-controller RoleBinding creation in any namespace including kube-system
What is durable here¶
Kyverno is a Kubernetes admission-control / policy engine that many clusters run with per-tenant policy scopes. This advisory exposes a tenant-to-cluster authority escalation through the policy DSL itself:
A tenant who can create a NamespacedMutatingPolicy in their own namespace reaches a CEL expression that can call generator.apply("<target-namespace>", [...]). The namespace string argument is not validated against the tenant's own namespace. The Kyverno background controller then generates the requested resources — including a RoleBinding — in the attacker-named namespace, which can be kube-system or any other privileged namespace.
The affected code path is pkg/cel/libs/context.go:177 (GenerateResources(namespace string, dataList []map[string]any)): the namespace arrives straight from the CEL expression with no containment check. In 1.18.0/1.18.1 the nmpol CEL compiler unintentionally exposes the generator library to NamespacedMutatingPolicy bodies, so the namespace argument is reachable by a tenant policy author.
This is a clean, replayable cluster tenancy boundary break: the primitive is "who can write a policy that tells the cluster to create a RoleBinding where?" The durable operator lesson is that a DSL function's namespace argument is an authority surface, not a routing hint — if a tenant expression can name any namespace, the tenant has a path into cluster-privileged namespaces.
Recon¶
- Identify the Kyverno version on the cluster. Only 1.18.0 / 1.18.1 are in range; 1.18.2 fixes it. Confirm via the kyverno controller deployment image or
kubectl get pods -n kyverno -o jsonpath='{.items[*].spec.containers[*].image}'. - Determine which namespaces can create
NamespacedMutatingPolicyobjects (thenamespacedmutatingpoliciesresource). That is the tenant surface. - Note whether the cluster enforces a global
namespacedmutatingpolicies.kyverno.ioRBAC scope or relies on per-namespace policy authoring.
Validation workflow (authorized lab / customer-approved)¶
Prove the namespace-argument boundary (no privileged writes)¶
- In an isolated cluster with Kyverno 1.18.0/1.18.1, create a throwaway tenant namespace with a policy-authoring service account.
- As that tenant, create a
NamespacedMutatingPolicywhose CEL body callsgenerator.apply()with a namespace string different from the tenant's own namespace (e.g. a second disposable namespace). - Observe the Kyverno background controller. The expected secure result is that the generated resources are confined to the tenant's namespace or the call is rejected. The vulnerable result is a generated resource — in the real flaw, a
RoleBinding— appearing in the attacker-named namespace. - To bound the proof, name a disposable second namespace in the
generator.apply()argument, notkube-system. The demonstration is "a tenant can direct generation to a namespace they do not own," not "we bound a cluster-admin role inkube-system."
Negative evidence to record¶
- Generation confined to the tenant namespace → namespace argument is validated; the boundary holds.
- A CEL/permission error on a foreign-namespace
generator.apply()→ the library is not exposed or the argument is gated.
Reporting heuristic¶
- Lead with the tenant policy →
generator.apply(namespace, …)→ cross-namespaceRoleBindinggeneration transition. Name the exact CEL call and the exact unvalidated argument (context.go:177). - Distinguish this from the cluster-scope policy cases: here a namespaced policy author reaches a cluster-wide generation target.
- Do not demonstrate a real
kube-systemRoleBinding. The proof is the foreign-namespace generation differential in a lab.
Safety constraints¶
- No
RoleBindingor RBAC object inkube-systemor any real namespace. - Use two disposable namespaces and a disposable policy-authoring identity.
- Do not grant or mutate cluster roles in a customer cluster.
- No policy changes beyond the lab
NamespacedMutatingPolicy.