You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #8798 added +kubebuilder:validation:Minimum=1 to RateLimitValue.Requests to fix silent uint32 truncation for large values. The Maximum=4294967295 bound from that PR is correct and should stay. However, Minimum=1 is too restrictive, it breaks a legitimate use case for global rate limiting that relies on requests: 0.
Setting requests: 0 on a global rate limit rule causes the rate limit service to return "over limit" for every matching request (0 requests per unit allowed = unconditional block). This made it possible to use the full clientSelector expressiveness (header matches, source CIDR, path, methods, invert) as targeted block rules without needing a separate mechanism. For example, blocking traffic matching a specific User-Agent or source CIDR in response to
an active vulnerability, without touching routing config:
After #8798, this is rejected at admission with a validation error.
Note: why requests: 0 is valid for global but not local
The two limit types have different underlying semantics:
Global: requests: 0 maps to requests_per_unit: 0 in the rate limit service config, which the service treats as "always over limit." This is well-defined and works correctly as a block.
Local: requests: 0 maps to MaxTokens: 0 in Envoy's TokenBucket proto, which has a > 0 constraint enforced by go-control-plane's proto validation (token_bucket.pb.validate.go: if m.GetMaxTokens() <= 0 { ... }). Sending this to Envoy would cause a NACK. Minimum=1 is therefore correct for local rate limits.
Proposed fix
Change +kubebuilder:validation:Minimum=1 → +kubebuilder:validation:Minimum=0 on RateLimitValue.Requests.
Add a CEL validation rule on LocalRateLimit rejecting requests: 0 with a clear message, e.g.: // +kubebuilder:validation:XValidation:rule="self.all(r, r.limit.requests > 0)",message="requests must be greater than 0 for local rate limits"
Description
PR #8798 added
+kubebuilder:validation:Minimum=1toRateLimitValue.Requeststo fix silent uint32 truncation for large values. TheMaximum=4294967295bound from that PR is correct and should stay. However,Minimum=1is too restrictive, it breaks a legitimate use case for global rate limiting that relies onrequests: 0.Setting
requests: 0on a global rate limit rule causes the rate limit service to return "over limit" for every matching request (0 requests per unit allowed = unconditional block). This made it possible to use the fullclientSelectorexpressiveness (header matches, source CIDR, path, methods, invert) as targeted block rules without needing a separate mechanism. For example, blocking traffic matching a specificUser-Agentor source CIDR in response toan active vulnerability, without touching routing config:
After #8798, this is rejected at admission with a validation error.
Note: why
requests: 0is valid for global but not localThe two limit types have different underlying semantics:
Proposed fix
+kubebuilder:validation:Minimum=1→+kubebuilder:validation:Minimum=0on RateLimitValue.Requests.requests: 0with a clear message, e.g.:// +kubebuilder:validation:XValidation:rule="self.all(r, r.limit.requests > 0)",message="requests must be greater than 0 for local rate limits"equests: 0to be rejected globally, it should now only be rejected on the local path.