Skip to content

fix: reject rate limit rules with multiple sourceCIDR selectors - #10061

Open
vishals-3 wants to merge 1 commit into
envoyproxy:mainfrom
vishals-3:ratelimit-multi-sourcecidr
Open

vishals-3 wants to merge 1 commit into
envoyproxy:mainfrom
vishals-3:ratelimit-multi-sourcecidr

Conversation

@vishals-3

Copy link
Copy Markdown

Fixes #10025

Root cause

translateRateLimitRule loops over rule.ClientSelectors. Header, method and query parameter matches append to slices on the IR rule, but ir.RateLimitRule.CIDRMatch is a single pointer:

HeaderMatches     []*StringMatch      // slice
MethodMatches     []*StringMatch      // slice
QueryParamMatches []*QueryParamMatch  // slice
CIDRMatch         *CIDRMatch          // single

so irRule.CIDRMatch = cidrMatch inside the loop overwrites on every iteration and only the last selector survives. Nothing surfaced the loss — no error, no status condition, and the policy still reported Accepted: True.

That is why the reporter's rule enforced only half of itself: a Distinct selector followed by an inverted one kept only the inversion.

What this changes

Rejects the configuration instead of silently dropping part of it:

Accepted: False, reason: Invalid
message: RateLimit: unable to translate rateLimit: only one clientSelector
         per rule may specify sourceCIDR

Why not support multiple CIDRs

I looked at doing it properly and it is a bigger change than the API shape suggests. Each sourceCIDR becomes a masked_remote_address entry in the rate limit service descriptor chain, the chain is nested rather than flat, and the Distinct arm adds a further remote_address level beneath it. Supporting N CIDRs means deciding what the nesting order means when several are present, and what an inverted CIDR nested under a distinct one should do — semantics worth agreeing on before writing code.

So this PR fails closed rather than guessing. It makes the limitation visible today, and does not foreclose full support later. Happy to do that follow-up if you would rather have it, and happy to close this in favour of it if you think rejecting is the wrong call.

Testing

go test ./internal/gatewayapi/... ./internal/xds/translator/... ./internal/ir/... ./api/...

All nine packages pass. Added the backendtrafficpolicy-with-ratelimit-invalid-multiple-source-cidr fixture covering a Distinct selector followed by an inverted one, which is the shape from the issue. Existing fixtures using a single sourceCIDR are unchanged.

🤖 Generated with Claude Code

A global rate limit rule that specified sourceCIDR in more than one
clientSelector silently kept only the last one. translateRateLimitRule loops
over rule.ClientSelectors and appends header, method and query parameter
matches to slices on the IR rule, but ir.RateLimitRule.CIDRMatch is a single
pointer, so each selector overwrote the previous one. Nothing surfaced the
loss: no error, no status condition, and the policy still reported
Accepted: True.

That made a rule written as "limit each client individually, except this CIDR"
enforce only half of itself, which is the case in the issue: a Distinct
selector followed by an inverted one kept only the inversion.

Reject the configuration rather than silently dropping part of it. Supporting
several CIDRs per rule is a larger change than it looks, because each one
becomes a masked_remote_address entry in the rate limit service descriptor
chain and the nesting order carries meaning, so that is left for a follow-up if
maintainers want it. Failing closed at least makes the limitation visible
instead of producing a policy that quietly enforces something other than what
it says.

Fixes envoyproxy#10025

Test Plan:

```
go test ./internal/gatewayapi/... ./internal/xds/translator/... ./internal/ir/... ./api/...
```

All nine packages pass. Added the
backendtrafficpolicy-with-ratelimit-invalid-multiple-source-cidr fixture, which
renders the policy with

  Accepted: False, reason: Invalid
  message: RateLimit: unable to translate rateLimit: only one clientSelector
           per rule may specify sourceCIDR

Existing fixtures that use a single sourceCIDR are unchanged.

This change was authored with the assistance of Claude, an AI assistant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: vishals-3 <105816654+vishals-3@users.noreply.github.com>
@vishals-3
vishals-3 requested a review from a team as a code owner September 21, 2026 04:36
@netlify

netlify Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for cerulean-figolla-1f9435 ready!

Name Link
🔨 Latest commit 526fbf2
🔍 Latest deploy log https://app.netlify.com/projects/cerulean-figolla-1f9435/deploys/6ab0b4673851a70008956a80
😎 Deploy Preview https://deploy-preview-10061--cerulean-figolla-1f9435.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@devilleweppenaar

Copy link
Copy Markdown

Is this not a duplicate of #10046?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rate limit: Only the last sourceCIDR in clientSelectors takes effect

2 participants