xds: convert rds header exact/prefix/suffix/contains matches to StringMatcher - #9223
Conversation
|
What do you think about using a StringMatcher from HeaderMatcher wherever possible, so that we can consolidate all these checks in one place. The StringMatcher type already includes a bunch of checks. See: |
|
Makes sense. Routing the exact/prefix/suffix arms through matcher.StringMatcher would let StringMatcherFromProto own the empty checks, and it also lets RouteToMatcher drop its per-field One thing worth confirming before I refactor: StringMatcherFromProto rejects an empty prefix/suffix (and contains), but it accepts an empty exact. So prefix/suffix stay a NACK as they are now, but an empty exact_match would go from the current panic to an accepted matcher that matches an empty header value, rather than being rejected. Are you fine with empty exact_match being accepted that way, or would you rather keep rejecting it? Happy to fold the change into this PR once you confirm which behavior you want for exact. |
|
An empty exact matcher being accepted is the correct behavior. So, I'm fine with changing it. I'm also fine with adding support for the Contains matcher in HeaderMatcher, and also routing it through the StringMatcher. |
|
Done. The parser now converts exact/prefix/suffix/contains to the equivalent StringMatcher and runs them through StringMatcherFromProto, so the checks are all in one place: empty exact is accepted, empty prefix/suffix/contains are NACKed, and contains_match is now supported. RouteToMatcher drops its per-field special casing and the ExactMatch/PrefixMatch/SuffixMatch fields are gone from the internal HeaderMatcher type. Updated the PR title and description to match. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #9223 +/- ##
==========================================
- Coverage 83.33% 83.27% -0.07%
==========================================
Files 420 421 +1
Lines 34028 34087 +59
==========================================
+ Hits 28358 28386 +28
- Misses 4253 4270 +17
- Partials 1417 1431 +14
🚀 New features to boost your workflow:
|
|
The static checks are failing with the following error: Could you please add Line 166 in 75f3c0b |
|
Added .ContainsMatch to the ignored deprecation list in vet.sh, so the SA1019 failure should be gone. Also picked up the two test nits: a suffix_match case and the compacted single-entry slice literals. |
|
Moving to @mbissa for second set of eyes |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request refactors the handling of deprecated header matchers (ExactMatch, PrefixMatch, SuffixMatch, and ContainsMatch) in the xDS client. These fields are now converted to equivalent StringMatcher proto messages during unmarshaling, allowing StringMatcherFromProto to handle validation consistently. Consequently, the corresponding fields have been removed from the HeaderMatcher struct, and the associated tests and vet scripts have been updated to reflect this change. There are no review comments, and I have no feedback to provide.
…gMatcher (grpc#9223) The RDS parser stored a header matcher's exact_match, prefix_match, or suffix_match without checking for the empty string, so a route carrying `exact_match: ""` (a valid proto oneof selection) was accepted. RouteToMatcher later tested those fields with `*h.X != ""` and treated an empty value as unset, so it fell through every arm of its header switch to `panic("illegal route: missing header_match_specifier")`. That runs when the client resolver builds a config selector and when the server processes an inline route config, neither behind a recover, so a management server could crash the client or an xds-enabled server with a single route. Per review feedback, instead of adding per-field empty checks the parser now converts the deprecated exact_match, prefix_match, suffix_match, and contains_match fields to the equivalent StringMatcher and runs them through matcher.StringMatcherFromProto, so validation for all of them is consolidated in one place. As a result: * an empty prefix_match, suffix_match, or contains_match is rejected and the resource is NACKed * an empty exact_match is accepted and matches an empty header value, per StringMatcher semantics, instead of panicking * contains_match is now supported for route header matching * RouteToMatcher drops its per-field `!= ""` special casing, and the ExactMatch/PrefixMatch/SuffixMatch fields are removed from the internal HeaderMatcher type RELEASE NOTES: * xds: fix a panic parsing a route whose header matcher has an empty exact_match, prefix_match, or suffix_match, and add support for contains_match in route header matchers
…gMatcher (grpc#9223) The RDS parser stored a header matcher's exact_match, prefix_match, or suffix_match without checking for the empty string, so a route carrying `exact_match: ""` (a valid proto oneof selection) was accepted. RouteToMatcher later tested those fields with `*h.X != ""` and treated an empty value as unset, so it fell through every arm of its header switch to `panic("illegal route: missing header_match_specifier")`. That runs when the client resolver builds a config selector and when the server processes an inline route config, neither behind a recover, so a management server could crash the client or an xds-enabled server with a single route. Per review feedback, instead of adding per-field empty checks the parser now converts the deprecated exact_match, prefix_match, suffix_match, and contains_match fields to the equivalent StringMatcher and runs them through matcher.StringMatcherFromProto, so validation for all of them is consolidated in one place. As a result: * an empty prefix_match, suffix_match, or contains_match is rejected and the resource is NACKed * an empty exact_match is accepted and matches an empty header value, per StringMatcher semantics, instead of panicking * contains_match is now supported for route header matching * RouteToMatcher drops its per-field `!= ""` special casing, and the ExactMatch/PrefixMatch/SuffixMatch fields are removed from the internal HeaderMatcher type RELEASE NOTES: * xds: fix a panic parsing a route whose header matcher has an empty exact_match, prefix_match, or suffix_match, and add support for contains_match in route header matchers
…(#83) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [google.golang.org/grpc](https://github.com/grpc/grpc-go) | `v1.82.0` → `v1.83.1` |  |  | --- ### Release Notes <details> <summary>grpc/grpc-go (google.golang.org/grpc)</summary> ### [`v1.83.1`](https://github.com/grpc/grpc-go/releases/tag/v1.83.1): Release 1.83.1 [Compare Source](grpc/grpc-go@v1.83.0...v1.83.1) ### Security - xds/rbac: Fix a bug where nested `Principal` or `Permission` rules with `:scheme` or `grpc-` prefixed header matchers were not rejected, which could cause DENY rules to fail open. ([#​9258](grpc/grpc-go#9258)) - Special Thanks: [@​nvxbug](https://github.com/nvxbug) - xds/rbac: Fix a bug where the `host` header matcher was not being replaced with `:authority` in nested `Principal` or `Permission` rules. ([#​9258](grpc/grpc-go#9258)) - Special Thanks: [@​nvxbug](https://github.com/nvxbug) - xds/rbac: Fix a bug where a header matcher whose name was not lowercase, such as `X-Role`, matched no header, which could cause DENY rules to fail open. ([#​9332](grpc/grpc-go#9332)) - Special Thanks: [@​alimony](https://github.com/alimony) - xds/rbac: Fix a bug where a `:scheme` or `grpc-` prefixed header matcher was accepted when its name was not lowercase. ([#​9332](grpc/grpc-go#9332)) - Special Thanks: [@​alimony](https://github.com/alimony) - xds/rbac: Fix a bug where a `Host` header matcher was not replaced with `:authority`. ([#​9332](grpc/grpc-go#9332)) - Special Thanks: [@​alimony](https://github.com/alimony) ### Performance - transport: Restrict memory overhead of buffering small data frames. ([#​9331](grpc/grpc-go#9331)) ### [`v1.83.0`](https://github.com/grpc/grpc-go/releases/tag/v1.83.0): Release 1.83.0 [Compare Source](grpc/grpc-go@v1.82.1...v1.83.0) ### Security - server: Stop reading from connections when flooded by HTTP/2 frames to mitigate resource exhaustion. The default value for this limit is 100 frames, excluding DATA and HEADERS, and may be changed by setting environment variable `GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT`. - xds/rbac: Support `Metadata` and `RequestedServerName` permissions matcher fields. If present in a DENY rule, previously these would be ignored and fail-open. - xds/rbac: Fix panic when parsing unsupported fields in `NotRule`/`NotId` permissions. - xds/rbac: Support the deprecated `source_ip` principal identifier by treating it as equivalent to `direct_remote_ip`. - xds: Fix panic when parsing route header matchers configured with empty `exact_match`, `prefix_match`, or `suffix_match` strings. ([#​9223](grpc/grpc-go#9223)) ### New Features - xds/googlec2p: Enable DirectPath over Interconnect support for on-premises clients via the `force-xds` target URI query parameter. ([#​9133](grpc/grpc-go#9133)) - xds: Enable xDS configuration to control which fields get propagated from ORCA backend metric reports to LRS load reports. ([#​9145](grpc/grpc-go#9145)) - authz: Add `OnPolicyUpdate` callback to `FileWatcherOptions` to notify when an authz policy is loaded or updated. ([#​9142](grpc/grpc-go#9142)) - Special Thanks: [@​hnefatl](https://github.com/hnefatl) - xds: Add support for the GCP Authentication HTTP Filter, which automatically fetches and attaches GCP Service Account Identity JWT tokens to outgoing RPCs. - This feature can be enabled by setting environment variable `GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER=true`. ([#​9119](grpc/grpc-go#9119)) - xds: Add support for xDS-based HTTP CONNECT proxies. - This feature can be enabled by setting environment variable `GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT=true`. ([#​9151](grpc/grpc-go#9151)) - xds: Add support for `contains_match` in route header matchers. ([#​9223](grpc/grpc-go#9223)) ### Bug Fixes - credentials/alts: Fix panic when processing malformed frames by validating that the message frame length exceeds the message type field size. ([#​9197](grpc/grpc-go#9197)) - grpc: Fix compilation on Plan 9 targets (`GOOS=plan9`), broken since v1.81.0. ([#​9255](grpc/grpc-go#9255)) - Special Thanks: [@​Yusufihsangorgel](https://github.com/Yusufihsangorgel) ### [`v1.82.1`](https://github.com/grpc/grpc-go/releases/tag/v1.82.1): Release 1.82.1 [Compare Source](grpc/grpc-go@v1.82.0...v1.82.1) ### Security - server: Stop reading from the connection when flooded by HTTP/2 frames. The default value for this limit is 100 frames, excluding DATA and HEADERS, and may be changed by setting environment variable `GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT`. - xds/rbac: Support `Metadata` and `RequestedServerName` permissions matcher fields. If present in a DENY rule, previously these would be ignored and fail-open. - xds/rbac: Fix panic when parsing unsupported fields in `NotRule`/`NotId` permissions. - xds/rbac: Support the deprecated `source_ip` principal identifier by treating it as equivalent to `direct_remote_ip`. </details> --- ### Configuration 📅 **Schedule**: (in timezone Europe/Paris) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjMxLjAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbInR5cGUvbWlub3IiXX0=--> Reviewed-on: https://git.erwanleboucher.dev/eleboucher/runner-k8s-plugin/pulls/83
…/forgejo) (#14216) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [google.golang.org/grpc](https://github.com/grpc/grpc-go) | `v1.82.1` → `v1.83.1` |  |  | --- >⚠️ **Warning** > > Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/2779) for more information. --- ### gRPC-Go: Heap Memory Exhaustion (OOM) via HTTP/2 DATA Frame Fragmentation [CVE-2026-84304](https://nvd.nist.gov/vuln/detail/CVE-2026-84304) / [GHSA-vp52-pcj8-j9qc](GHSA-vp52-pcj8-j9qc) <details> <summary>More information</summary> #### Details ##### Impact An unauthenticated remote attacker can initiate a gRPC stream and purposefully fragment their payload into millions of tiny (e.g., 1-byte) HTTP/2 DATA frames. Even if the total payload volume falls within the configured connection and stream flow-control windows, each independent fragment incurs memory overhead due to internal tracking structures and queue allocation. Repeated fragmentation massively inflates the heap space consumed by the stream. An attacker multiplexing multiple concurrent streams can exhaust the memory bounds of the runtime, forcing a runtime panic or OutOfMemory condition and leading to a remote Denial of Service (DoS). ##### Patches The change to fix this issue is merged in `master` and a patch release, 1.83.1, has been published that contains this fix. ##### Workarounds This vulnerability is mitigated by implementing receive buffer compaction. Consecutive small data buffers are automatically coalesced into larger buffers from a shared pool once the overhead is perceived to be excessive relative to actual payload data, drastically minimizing per-frame memory overheads. This behavior is enabled by default. A temporary escape hatch is provided via the environment variable `GRPC_GO_EXPERIMENTAL_ENABLE_RECEIVE_BUFFER_COMPACTION=false` to disable the feature if unforeseen issues arise, but it will be removed in a future release. #### Severity - CVSS Score: 8.7 / 10 (High) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N` #### References - [https://github.com/grpc/grpc-go/security/advisories/GHSA-vp52-pcj8-j9qc](https://github.com/grpc/grpc-go/security/advisories/GHSA-vp52-pcj8-j9qc) - [https://nvd.nist.gov/vuln/detail/CVE-2026-84304](https://nvd.nist.gov/vuln/detail/CVE-2026-84304) - [https://github.com/grpc/grpc-go/pull/9331](https://github.com/grpc/grpc-go/pull/9331) - [https://github.com/grpc/grpc-go/pull/9333](https://github.com/grpc/grpc-go/pull/9333) - [https://github.com/grpc/grpc-go/commit/7354d9c8debb4bcf2225bf429857078de310c176](https://github.com/grpc/grpc-go/commit/7354d9c8debb4bcf2225bf429857078de310c176) - [https://github.com/grpc/grpc-go/commit/8cfeca0e1ee5ea0980dcc320e20240fa1079ec77](https://github.com/grpc/grpc-go/commit/8cfeca0e1ee5ea0980dcc320e20240fa1079ec77) - [https://github.com/grpc/grpc-go](https://github.com/grpc/grpc-go) - [https://github.com/grpc/grpc-go/releases/tag/v1.83.1](https://github.com/grpc/grpc-go/releases/tag/v1.83.1) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-vp52-pcj8-j9qc) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>grpc/grpc-go (google.golang.org/grpc)</summary> ### [`v1.83.1`](https://github.com/grpc/grpc-go/releases/tag/v1.83.1): Release 1.83.1 [Compare Source](grpc/grpc-go@v1.83.0...v1.83.1) ### Security - xds/rbac: Fix a bug where nested `Principal` or `Permission` rules with `:scheme` or `grpc-` prefixed header matchers were not rejected, which could cause DENY rules to fail open. ([#​9258](grpc/grpc-go#9258)) - Special Thanks: [@​nvxbug](https://github.com/nvxbug) - xds/rbac: Fix a bug where the `host` header matcher was not being replaced with `:authority` in nested `Principal` or `Permission` rules. ([#​9258](grpc/grpc-go#9258)) - Special Thanks: [@​nvxbug](https://github.com/nvxbug) - xds/rbac: Fix a bug where a header matcher whose name was not lowercase, such as `X-Role`, matched no header, which could cause DENY rules to fail open. ([#​9332](grpc/grpc-go#9332)) - Special Thanks: [@​alimony](https://github.com/alimony) - xds/rbac: Fix a bug where a `:scheme` or `grpc-` prefixed header matcher was accepted when its name was not lowercase. ([#​9332](grpc/grpc-go#9332)) - Special Thanks: [@​alimony](https://github.com/alimony) - xds/rbac: Fix a bug where a `Host` header matcher was not replaced with `:authority`. ([#​9332](grpc/grpc-go#9332)) - Special Thanks: [@​alimony](https://github.com/alimony) ### Performance - transport: Restrict memory overhead of buffering small data frames. ([#​9331](grpc/grpc-go#9331)) ### [`v1.83.0`](https://github.com/grpc/grpc-go/releases/tag/v1.83.0): Release 1.83.0 [Compare Source](grpc/grpc-go@v1.82.2...v1.83.0) ### Security - server: Stop reading from connections when flooded by HTTP/2 frames to mitigate resource exhaustion. The default value for this limit is 100 frames, excluding DATA and HEADERS, and may be changed by setting environment variable `GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT`. - xds/rbac: Support `Metadata` and `RequestedServerName` permissions matcher fields. If present in a DENY rule, previously these would be ignored and fail-open. - xds/rbac: Fix panic when parsing unsupported fields in `NotRule`/`NotId` permissions. - xds/rbac: Support the deprecated `source_ip` principal identifier by treating it as equivalent to `direct_remote_ip`. - xds: Fix panic when parsing route header matchers configured with empty `exact_match`, `prefix_match`, or `suffix_match` strings. ([#​9223](grpc/grpc-go#9223)) ### New Features - xds/googlec2p: Enable DirectPath over Interconnect support for on-premises clients via the `force-xds` target URI query parameter. ([#​9133](grpc/grpc-go#9133)) - xds: Enable xDS configuration to control which fields get propagated from ORCA backend metric reports to LRS load reports. ([#​9145](grpc/grpc-go#9145)) - authz: Add `OnPolicyUpdate` callback to `FileWatcherOptions` to notify when an authz policy is loaded or updated. ([#​9142](grpc/grpc-go#9142)) - Special Thanks: [@​hnefatl](https://github.com/hnefatl) - xds: Add support for the GCP Authentication HTTP Filter, which automatically fetches and attaches GCP Service Account Identity JWT tokens to outgoing RPCs. - This feature can be enabled by setting environment variable `GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER=true`. ([#​9119](grpc/grpc-go#9119)) - xds: Add support for xDS-based HTTP CONNECT proxies. - This feature can be enabled by setting environment variable `GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT=true`. ([#​9151](grpc/grpc-go#9151)) - xds: Add support for `contains_match` in route header matchers. ([#​9223](grpc/grpc-go#9223)) ### Bug Fixes - credentials/alts: Fix panic when processing malformed frames by validating that the message frame length exceeds the message type field size. ([#​9197](grpc/grpc-go#9197)) - grpc: Fix compilation on Plan 9 targets (`GOOS=plan9`), broken since v1.81.0. ([#​9255](grpc/grpc-go#9255)) - Special Thanks: [@​Yusufihsangorgel](https://github.com/Yusufihsangorgel) ### [`v1.82.2`](https://github.com/grpc/grpc-go/releases/tag/v1.82.2): Release 1.82.2 [Compare Source](grpc/grpc-go@v1.82.1...v1.82.2) ### Security - server: Reject requests missing both `:authority` and `Host` headers with HTTP 400 and status `Internal`. ([#​9365](grpc/grpc-go#9365)) - Special Thanks: [@​winklemad](https://github.com/winklemad) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - Between 12:00 AM and 03:59 AM (`* 0-3 * * *`) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC41Mi4wIiwidXBkYXRlZEluVmVyIjoiNDQuNTIuMCIsInRhcmdldEJyYW5jaCI6InYxNi4wL2Zvcmdlam8iLCJsYWJlbHMiOlsiZGVwZW5kZW5jeS11cGdyYWRlIiwidGVzdC9ub3QtbmVlZGVkIl19--> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/14216 Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
The RDS parser stored a header matcher's exact_match, prefix_match, or suffix_match without checking for the empty string, so a route carrying
exact_match: ""(a valid proto oneof selection) was accepted. RouteToMatcher later tested those fields with*h.X != ""and treated an empty value as unset, so it fell through every arm of its header switch topanic("illegal route: missing header_match_specifier"). That runs when the client resolver builds a config selector and when the server processes an inline route config, neither behind a recover, so a management server could crash the client or an xds-enabled server with a single route.Per review feedback, instead of adding per-field empty checks the parser now converts the deprecated exact_match, prefix_match, suffix_match, and contains_match fields to the equivalent StringMatcher and runs them through matcher.StringMatcherFromProto, so validation for all of them is consolidated in one place. As a result:
!= ""special casing, and the ExactMatch/PrefixMatch/SuffixMatch fields are removed from the internal HeaderMatcher typeRELEASE NOTES: