[{"data":1,"prerenderedAt":482},["ShallowReactive",2],{"blog-en-kyverno-validatingadmissionpolicy-cel-migration-silent-bypass":3,"blog-related-en-kyverno-validatingadmissionpolicy-cel-migration-silent-bypass":424,"blog-en-kyverno-validatingadmissionpolicy-cel-migration-silent-bypass-alt":412},{"id":4,"title":5,"author":6,"body":7,"date":406,"description":407,"extension":408,"image":409,"locale":410,"meta":411,"navigation":412,"path":413,"seo":414,"stem":415,"tags":416,"__hash__":423},"blog\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass.md","Is the CEL Rewrite of Your Policy Really the Same? How Migrating from Kyverno to ValidatingAdmissionPolicy Let Some Resources Silently Slip Through","Kubo Team",{"type":8,"value":9,"toc":391},"minimark",[10,27,34,39,69,95,101,104,108,126,133,142,148,152,171,201,214,220,229,238,251,255,258,263,273,277,286,290,317,321,328,334,342,351,355,367,379],[11,12,13,14,18,19,22,23,26],"p",{},"A team running wildcard policies across a K3s\u002FKubernetes fleet ran into a problem while migrating to ValidatingAdmissionPolicy (CEL). Some time after the migration, they noticed that requests for one specific resource type had been slipping past validation entirely. The root cause was a combination of a CEL runtime error and the ",[15,16,17],"code",{},"failurePolicy"," setting. When you rewrite a Kyverno ",[15,20,21],{},"ClusterPolicy"," as a ",[15,24,25],{},"ValidatingAdmissionPolicy",", does it really carry the same meaning? This article walks through the concrete pitfall and a safer way to migrate.",[11,28,29],{},[30,31],"img",{"alt":32,"src":33},"section01","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass\u002Fsection01.webp",[35,36,38],"h2",{"id":37},"_1-clusterpolicy-is-going-away-why-you-need-to-decide-on-a-migration-path-now","1. ClusterPolicy Is Going Away. Why You Need to Decide on a Migration Path Now",[11,40,41,42,44,45,59,60,63,64,68],{},"Kyverno has long handled Kubernetes policy management through ",[15,43,21],{},", a YAML-based pattern-matching mechanism. But ",[46,47,51,52,54,55,58],"a",{"href":48,"rel":49},"https:\u002F\u002Fnirmata.com\u002F2026\u002F07\u002F26\u002Fkyverno-policy-migration-to-cel-based-policies\u002F",[50],"nofollow","Kyverno 1.17 flagged ",[15,53,21],{}," and ",[15,56,57],{},"CleanupPolicy"," as deprecated",", and subsequent minor releases now ship only critical fixes for them. The designated migration target is a new CEL (Common Expression Language)-based ",[15,61,62],{},"ValidatingPolicy",", implemented as a superset of Kubernetes' own ",[46,65,25],{"href":66,"rel":67},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Freference\u002Faccess-authn-authz\u002Fvalidating-admission-policy\u002F",[50],", which reached GA in 1.30.",[11,70,71,72,76,77,82,83,88,89,94],{},"The biggest strength of ValidatingAdmissionPolicy is that it ",[73,74,75],"strong",{},"evaluates CEL expressions directly inside the kube-apiserver process",". Unlike Kyverno or OPA Gatekeeper, there's no external webhook controller to deploy and operate, and no network hop. In real-world benchmarks, running the same policy logic as CEL versus as a webhook showed a median added latency of ",[46,78,81],{"href":79,"rel":80},"https:\u002F\u002Fwww.techplained.com\u002Fkubernetes-mutating-admission-policy",[50],"about 0.3ms for CEL versus about 1.65ms for the webhook",". From ",[46,84,87],{"href":85,"rel":86},"https:\u002F\u002Fkubernetes.io\u002Fblog\u002F2022\u002F12\u002F20\u002Fvalidating-admission-policies-alpha\u002F",[50],"Kubernetes introducing it as an alpha feature in 2022"," through ",[46,90,93],{"href":91,"rel":92},"https:\u002F\u002Fnirmata.com\u002F2023\u002F09\u002F08\u002Fwhat-is-kubernetes-validating-admission-policy\u002F",[50],"its GA in 1.30",", the design philosophy has been consistent: simple validation shouldn't require the operational overhead of running a webhook server.",[11,96,97],{},[30,98],{"alt":99,"src":100},"section02","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass\u002Fsection02.webp",[11,102,103],{},"Because of these performance and operational advantages, many teams are moving their Kyverno wildcard policies — configurations that validate multiple resource kinds at once — to CEL-based policies. But this migration involves pitfalls that go beyond a simple syntax conversion.",[35,105,107],{"id":106},"_2-what-happened-when-a-wildcard-policy-was-ported-as-is","2. What Happened When a Wildcard Policy Was Ported As-Is",[11,109,110,111,116,117,120,121,125],{},"Kyverno provides a ",[46,112,115],{"href":113,"rel":114},"https:\u002F\u002Fkyverno.io\u002Fblog\u002F2024\u002F02\u002F26\u002Fgenerate-vaps",[50],"feature that auto-generates a ValidatingAdmissionPolicy from an existing ClusterPolicy",". Simply enabling the generation flag on the Kyverno admission controller converts resource selectors like ",[15,118,119],{},"match.any.resources.kinds"," into CEL expressions, and keeps the resulting ",[46,122,124],{"href":66,"rel":123},[50],"ValidatingAdmissionPolicy definition"," automatically in sync.",[11,127,128,129,132],{},"However, this auto-conversion has a clear limitation. Because ValidatingAdmissionPolicy is, by Kubernetes' own design, a ",[73,130,131],{},"validation-only"," mechanism, Kyverno's mutate, generate, and image-verification capabilities simply cannot be targeted for conversion. The more rule types a single ClusterPolicy bundled together, the more likely functionality gets dropped during migration.",[11,134,135,136,141],{},"What's trickier still is how a single CEL expression behaves once you port a wildcard policy that validates multiple resource kinds (Deployment, DaemonSet, StatefulSet, and so on) together. Kyverno's own ",[46,137,140],{"href":138,"rel":139},"https:\u002F\u002Fmain.kyverno.io\u002Fdocs\u002Ftroubleshooting\u002Fcel-expressions",[50],"CEL expression troubleshooting guide"," explicitly notes that unchecked access to optional fields is a classic source of incidents.",[11,143,144],{},[30,145],{"alt":146,"src":147},"section03","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass\u002Fsection03.webp",[35,149,151],{"id":150},"_3-the-root-cause-cel-field-missing-errors-combined-with-failurepolicy-ignore","3. The Root Cause: CEL Field-Missing Errors Combined with failurePolicy: Ignore",[11,153,154,155,158,159,162,163,166,167,170],{},"This is the crux of the matter. The ",[15,156,157],{},"object"," variable referenced inside a CEL expression has different available fields depending on resource kind. For example, ",[15,160,161],{},"object.spec.replicas"," exists on a Deployment but not on a DaemonSet. If a CEL expression written to validate multiple kinds at once lacks a ",[15,164,165],{},"has()"," guard, a request for a DaemonSet triggers a ",[73,168,169],{},"runtime error"," the moment it's evaluated.",[11,172,173,174,177,178,181,182,185,186,188,189,192,193,196,197,200],{},"Here's how ",[15,175,176],{},"matchConditions"," evaluation works: if any condition evaluates to ",[15,179,180],{},"FALSE",", the policy is skipped. If any condition instead returns an ",[73,183,184],{},"error",", behavior follows the ",[15,187,17],{}," setting. With ",[15,190,191],{},"failurePolicy: Fail"," (the default), the request fails safe and is rejected. But during migration, it's common to set ",[15,194,195],{},"failurePolicy: Ignore"," out of a desire not to break existing behavior. In that case, ",[73,198,199],{},"the policy evaluation itself is silently skipped, and a request that should have been blocked passes through unchanged",".",[11,202,203,208,209,213],{},[46,204,207],{"href":205,"rel":206},"https:\u002F\u002Fwww.chainguard.dev\u002Funchained\u002Fare-kubernetes-validating-admission-policies-the-end-of-admission-controllers",[50],"Chainguard's analysis"," likewise points out that while ValidatingAdmissionPolicy works well for simple validation, more complex requirements — like image signature verification or referencing external data — still need a traditional admission controller, underscoring that migrating doesn't solve everything. ",[46,210,212],{"href":79,"rel":211},[50],"The same benchmark report"," also documents a case where a policy with an integer value set on a label field passed policy-creation-time type checking, only to have every request rejected at runtime with a type error. Incomplete type checking combined with how runtime errors are handled is one of the most commonly overlooked aspects of CEL-based validation design.",[11,215,216],{},[30,217],{"alt":218,"src":219},"section04","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass\u002Fsection04.webp",[11,221,222,223,228],{},"This issue is a continuation of the theme explored in an earlier article comparing ",[46,224,227],{"href":225,"rel":226},"https:\u002F\u002Fkubo.hexabase.io\u002Fblog\u002Fen\u002Fkyverno-opa-gatekeeper-k3s-wildcard-policy-overhead",[50],"the wildcard-policy overhead of Kyverno and OPA Gatekeeper"," — a risk that was invisible at the tool-selection stage only surfaced once teams moved to CEL.",[11,230,231,232,237],{},"An incident where \"something that should have been validated silently passes through\" also resembles, structurally, ",[46,233,236],{"href":234,"rel":235},"https:\u002F\u002Fkubo.hexabase.io\u002Fblog\u002Fen\u002Fk3s-pod-security-admission-restricted-servicelb-outage",[50],"the configuration gap that broke Pod Security Admission on K3s"," in an earlier case. Admission control mechanisms stay invisible when working correctly — but get the boundary conditions wrong, and they fail silently with real consequences.",[11,239,240,241,244,245,250],{},"Eliminating this kind of invisible risk entirely is hard in day-to-day K8s operations, but ",[73,242,243],{},"visibility into policy enforcement status and Deny\u002FAudit outcomes"," goes a long way toward catching anomalies faster. ",[46,246,249],{"href":247,"rel":248},"https:\u002F\u002Fkubo.hexabase.io\u002F",[50],"Kubo","'s Captain UI is designed to provide exactly this kind of operational transparency by default.",[35,252,254],{"id":253},"_4-a-safe-migration-path-has-guards-audit-first-staged-rollout","4. A Safe Migration Path — has() Guards, Audit First, Staged Rollout",[11,256,257],{},"The following steps reduce risk when migrating to CEL-based policies in production.",[259,260,262],"h3",{"id":261},"always-add-guard-clauses-to-cel-expressions","Always add guard clauses to CEL expressions",[11,264,265,266,268,269,272],{},"Anywhere an optional field is referenced, add a guard using ",[15,267,165],{}," or the ",[15,270,271],{},"?"," optional-chaining syntax. Rather than assuming a field exists on a given resource kind, check for it explicitly — this is the most basic defense against runtime errors in the first place.",[259,274,276],{"id":275},"default-failurepolicy-to-fail","Default failurePolicy to Fail",[11,278,279,280,282,283,285],{},"Early in a migration it's tempting to set ",[15,281,195],{}," to avoid breaking existing behavior, but that's equivalent to tolerating validation gaps. For any policy that matters, ",[15,284,191],{}," should be the default, and if you deliberately choose Ignore, document the reasoning and the risk.",[259,287,289],{"id":288},"validate-with-audit-before-switching-to-deny","Validate with Audit before switching to Deny",[11,291,292,310,311,313,314,316],{},[46,293,295,298,299,302,303,306,307],{"href":66,"rel":294},[50],[15,296,297],{},"validationActions"," supports three modes — ",[15,300,301],{},"Deny",", ",[15,304,305],{},"Warn",", and ",[15,308,309],{},"Audit"," — and using ",[15,312,309],{}," lets you see how a policy evaluates without actually blocking requests. Observing production traffic for days or weeks before flipping to ",[15,315,301],{}," is the safer, staged way to roll out.",[259,318,320],{"id":319},"split-policies-by-resource-kind","Split policies by resource kind",[11,322,323,324,327],{},"Bundling multiple resource kinds into one wildcard policy looks efficient at first, but the more complex the CEL expression gets, the higher the risk of missing a guard somewhere. Splitting ",[15,325,326],{},"matchConstraints.resourceRules"," by resource kind, and writing a tailored CEL expression for each, ends up being both more maintainable and more thoroughly validated.",[11,329,330],{},[30,331],{"alt":332,"src":333},"section05","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass\u002Fsection05.webp",[11,335,336,337,341],{},"Ideally, an infrastructure team can run this staged migration on its own, but in practice, knowing what to check in Audit mode and when it's safe to flip to Deny takes experience. With ",[46,338,340],{"href":247,"rel":339},[50],"Kubo Cloud","'s Captain UI, Audit results and Deny outcomes show up on the same screen, making it easier for a team to share the evidence behind a migration decision.",[11,343,344,345,350],{},"In industries like finance and healthcare, where a policy violation can immediately become a compliance issue, there's also strong demand to work through policy migration step by step with outside support. For those requirements, ",[46,346,349],{"href":347,"rel":348},"https:\u002F\u002Fwww.hexabase.com\u002Fproduct\u002Fkubo\u002Fon-premise",[50],"Kubo On-Premise"," — which lets you run policy operations entirely within your own managed environment — is an option worth considering.",[35,352,354],{"id":353},"summary-cel-migration-is-faster-but-it-can-also-fail-silently","Summary — CEL Migration Is Faster, But It Can Also Fail Silently",[11,356,357,358,360,361,366],{},"ValidatingAdmissionPolicy offers clear benefits — no external controller, low latency — but depending on how CEL runtime errors and ",[15,359,17],{}," are configured, it also carries the risk that validation silently disappears for certain resources. ",[46,362,365],{"href":363,"rel":364},"https:\u002F\u002Fkubernetes.io\u002Fdocs\u002Fconcepts\u002Fcluster-administration\u002Fadmission-webhooks-good-practices\u002F",[50],"Kubernetes' own guidance on admission webhooks"," keeps coming back to the same point: decide explicitly, at design time, which way a failure should fall. With Kyverno's ClusterPolicy heading toward removal, many teams are about to face this migration.",[11,368,369,370,372,373,375,376,378],{},"As a checklist: ① enforce ",[15,371,165],{}," guards everywhere, ② default to ",[15,374,191],{},", ③ validate with ",[15,377,309],{}," first, and ④ split policies by resource kind. Whether a CEL rewrite of your policy is \"really the same\" isn't something you can tell from reading the syntax alone — you need to build actual request-based verification into the migration process itself.",[11,380,381,382,385,386,200],{},"To keep decisions like this from becoming tribal knowledge, ",[46,383,249],{"href":247,"rel":384},[50]," ships AI-Driven Deployment and Captain UI visibility as standard. Work like policy migration — where success goes unnoticed and failure causes quiet damage — is exactly where platform-level transparency pays off. If that's useful to you, feel free to reach out via ",[46,387,390],{"href":388,"rel":389},"https:\u002F\u002Fwww.hexabase.com\u002Fcontact-us\u002F",[50],"Contact Us",{"title":392,"searchDepth":393,"depth":393,"links":394},"",2,[395,396,397,398,405],{"id":37,"depth":393,"text":38},{"id":106,"depth":393,"text":107},{"id":150,"depth":393,"text":151},{"id":253,"depth":393,"text":254,"children":399},[400,402,403,404],{"id":261,"depth":401,"text":262},3,{"id":275,"depth":401,"text":276},{"id":288,"depth":401,"text":289},{"id":319,"depth":401,"text":320},{"id":353,"depth":393,"text":354},"2026-10-18","As Kyverno's ClusterPolicy heads toward deprecation, teams migrating to ValidatingAdmissionPolicy (CEL) have found that validation silently skipped for certain resource types. Here's the CEL runtime error and failurePolicy trap behind it, and a safe migration path.","md","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass\u002Ftitle.webp","en",{},true,"\u002Fblog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass",{"title":5,"description":407},"blog\u002Fen\u002Fkyverno-validatingadmissionpolicy-cel-migration-silent-bypass",[417,418,419,420,421,422],"k3s","kubernetes","validatingadmissionpolicy","kyverno","cel","admission-control","zIoTHmlZ-XfALdMg1uizlcxbg6noiyfL1QUZilavjSQ",[425,433,441,450,460,472],{"path":426,"title":427,"description":428,"date":429,"tags":430},"\u002Fblog\u002Fen\u002Fkyverno-opa-gatekeeper-k3s-wildcard-policy-overhead","Kyverno vs OPA Gatekeeper: What Actually Broke in Our K3s Production Cluster Was a Wildcard Policy","Which should you choose for K3s production, Kyverno or OPA Gatekeeper? We break down the real-world gap: wildcard policy overhead and measured p99 latency across replica configurations.","2026-10-11",[417,418,420,431,422,432],"opa-gatekeeper","policy-as-code",{"path":434,"title":435,"description":436,"date":437,"tags":438},"\u002Fblog\u002Fen\u002Fshadow-ai-kubernetes-admission-control-governance","Rogue Deployments Are Wrecking Your Company: The Shadow AI Problem Inside Kubernetes Clusters, and Admission Control as the Fix","Shadow AI isn't just unauthorized SaaS tools. It's happening inside your Kubernetes clusters too. Here's the risk it creates, and how Admission Control turns detection into real governance.","2026-07-17",[418,417,439,422,420,440],"shadow-ai","security",{"path":442,"title":443,"description":444,"date":445,"tags":446},"\u002Fblog\u002Fen\u002Fcicd-pipeline-devsecops-gate-design-four-layers","Still Bolting Security Checks Onto Your CI\u002FCD Pipeline Without Deciding Where They Belong? A Four-Layer Model for DevSecOps Gate Design","You've adopted SAST, DAST, and container scanning — yet incidents still slip into production. This article reframes CI\u002FCD pipeline security as a four-layer design problem and walks through concrete implementation patterns for K3s\u002FKubernetes environments.","2026-08-28",[417,418,447,448,422,449],"ci-cd","devsecops","gitops",{"path":451,"title":452,"description":453,"date":454,"tags":455},"\u002Fblog\u002Fen\u002Fk3s-hpa-containerresource-sidecar-utilization-dilution","HPA Was Scaling on the Sidecar's Share Too. The ContainerResource Blind Spot in Kubernetes","Kubernetes HPA sums resource usage across every container in a Pod, so Istio sidecars and logging agents dilute the scaling decision. Here's how the ContainerResource metric fixes it, in a K3s\u002FKubo context.","2026-10-21",[417,418,456,457,458,459],"hpa","containerresource","autoscaling","service-mesh",{"path":461,"title":462,"description":463,"date":464,"tags":465},"\u002Fblog\u002Fen\u002Fk3s-vs-kubernetes-cni-flannel-default-comparison","K3s vs Standard Kubernetes: How Different Their CNI Starting Points Really Are","K3s ships with Flannel built in, while standard Kubernetes starts with no CNI selected at all. This comparison, grounded in the official K3s, Kubernetes, Calico, and Cilium docs, explains how that difference affects NetworkPolicy enforcement and network performance.","2026-10-20",[417,418,466,467,468,469,470,471],"cni","flannel","calico","cilium","networkpolicy","networking",{"path":473,"title":474,"description":475,"date":476,"tags":477},"\u002Fblog\u002Fen\u002Fk3s-topology-spread-constraints-poddisruptionbudget-node-drain-pending","We Drained a Node and a Pod Never Came Back. Why Topology Spread Constraints and PodDisruptionBudget Collide on K3s","Draining a node in production K3s left a Pod stuck Pending. The cause: Topology Spread Constraints' maxSkew colliding with PodDisruptionBudget. We verify how draining itself creates a node shortage, based on official documentation.","2026-10-19",[417,418,478,479,480,481],"topology-spread-constraints","poddisruptionbudget","scheduling","node-drain",1792592647603]