-
Notifications
You must be signed in to change notification settings - Fork 255
A103: xDS Composite Filter #511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
markdroth
wants to merge
13
commits into
grpc:master
Choose a base branch
from
markdroth:xds_composite_filter
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
59cc515
A103: xDS Composite Filter
markdroth 4412a75
more CEL fields
markdroth 32c8b04
more details
markdroth b8987fb
matcher tree encodes skip actions
markdroth c932213
recursion limit
markdroth 94c4bfb
support only filter_metadata, not typed_filter_metadata
markdroth 4d4f41a
add filter behavior section
markdroth 13868f6
fix typo
markdroth c6bbc1e
add mailing list link
markdroth 27fd0ad
fix attribute name
markdroth 5515054
reference A106 instead of A77 for unified matcher and CEL
markdroth b87b76e
disallow keep_matching
markdroth 39a1fe2
add nested filter validation, and clarify filter behavior
markdroth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| A103: xDS Composite Filter | ||
| ---- | ||
| * Author(s): markdroth | ||
| * Approver: ejona86, dfawley | ||
| * Status: {Draft, In Review, Ready for Implementation, Implemented} | ||
| * Implemented in: <language, ...> | ||
| * Last updated: 2026-02-12 | ||
| * Discussion at: https://groups.google.com/g/grpc-io/c/es5taH0OZS8 | ||
|
|
||
| ## Abstract | ||
|
|
||
| gRPC will support the [xDS Composite filter][composite], which is a | ||
| "wrapper" filter that dynamically determines which filter to use based | ||
| on request attributes. | ||
|
|
||
| [composite]: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/composite_filter | ||
|
|
||
| ## Background | ||
|
|
||
| xDS support in the gRPC client and server are described in [A27] and | ||
| [A36], respectively. xDS HTTP filter support is described in [A39]. | ||
|
|
||
| The composite filter will make use of the Unified Matching API and CEL | ||
| support described in [A106]. | ||
|
|
||
| ### Related Proposals: | ||
| * [A27: xDS-Based Global Load Balancing][A27] | ||
| * [A39: xDS HTTP Filter Support][A39] | ||
| * [A36: xDS-Enabled Servers][A36] | ||
| * [A83: xDS GCP Authentication Filter][A83] | ||
| * [A106: xDS Unified Matcher and CEL Integration][A106] (pending) | ||
|
|
||
| [A27]: A27-xds-global-load-balancing.md | ||
| [A36]: A36-xds-for-servers.md | ||
| [A39]: A39-xds-http-filters.md | ||
| [A106]: https://github.com/grpc/proposal/pull/520 | ||
| [A83]: A83-xds-gcp-authn-filter.md | ||
|
|
||
| ## Proposal | ||
|
|
||
| We will support the composite filter in both the gRPC client and gRPC | ||
| server. | ||
|
|
||
| ### xDS Resource Validation | ||
|
|
||
| Today, the composite filter supports configuring only one filter as a | ||
| result of the matching tree. However, we have use-cases where we need | ||
| to select a chain of more than one filter based on the matching tree. | ||
| As a result, we are proposing a change to the composite filter's config | ||
| to allow selecting a chain of filters | ||
| (https://github.com/envoyproxy/envoy/pull/40885). | ||
|
|
||
| The composite filter is configured via the | ||
| [`envoy.extensions.common.matching.v3.ExtensionWithMatcher` | ||
| proto](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/common/matching/v3/extension_matcher.proto#L25) | ||
| message. Within it, gRPC will look at the following fields: | ||
| - [extension_config](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/common/matching/v3/extension_matcher.proto#L34C39-L34C55): | ||
| This must contain a | ||
| [`envoy.extensions.filters.http.composite.v3.Composite` | ||
| proto](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/filters/http/composite/v3/composite.proto#L33) | ||
| message, which has no fields. | ||
| - [xds_matcher](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/common/matching/v3/extension_matcher.proto#L31): | ||
| Specifies a unified matcher tree indicating the config of the filter | ||
| to use, as described in [A106]. Validation will fail if `keep_matching` | ||
| is enabled anywhere in the matcher tree. The actions in this tree must | ||
| be one of two types: | ||
| - [`envoy.extensions.filters.common.matcher.action.v3.SkipFilter`](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/filters/common/matcher/action/v3/skip_action.proto#L24): | ||
| This indicates that no filter will be executed. | ||
| - [`envoy.extensions.filters.http.composite.v3.ExecuteFilterAction`](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/filters/http/composite/v3/composite.proto#L49): | ||
| This indicates which filter(s) should be executed. Within it: | ||
| - [typed_config](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/filters/http/composite/v3/composite.proto#L54C39-L54C51): | ||
| The filter to configure. See [Nested Filter | ||
| Validation](#nested-filter-validation) below for validation rules. | ||
| This field is ignored if `filter_chain` is set. It | ||
| is an error if neither `typed_config` nor `filter_chain` are set. | ||
| - [dynamic_config](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/filters/http/composite/v3/composite.proto#L59): | ||
| This field will be ignored for now, since gRPC does not currently | ||
| support ECDS. Support for ECDS will be added in a subsequent gRFC. | ||
| - filter_chain (new field added in | ||
| https://github.com/envoyproxy/envoy/pull/40885): This specifies a | ||
| chain of filters to call, in order. See [Nested Filter | ||
| Validation](#nested-filter-validation) below for validation rules. | ||
| If set, the `typed_config` field is ignored. It is an error if | ||
| neither `typed_config` nor `filter_chain` are set. | ||
| - [sample_percent](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/filters/http/composite/v3/composite.proto#L69C43-L69C57): | ||
| - Optional; if unset, the specified filter(s) are always executed. | ||
| If set, for each RPC, a random number will be generated between | ||
| 0 and 100, and if that number is less than the specified | ||
| threshold, the specified filter(s) will be executed. | ||
| Within this field: | ||
| - [default_value](https://github.com/envoyproxy/envoy/blob/cdd19052348f7f6d85910605d957ba4fe0538aec/api/envoy/config/core/v3/base.proto#L648): | ||
| This field must be present. The configured value will be capped at | ||
| 100%. | ||
| - runtime_key: This field will be ignored, since gRPC does not | ||
| have a runtime system. | ||
| - matcher: gRPC will not support this deprecated field. | ||
|
|
||
| We will also support per-route overrides via the | ||
| [`envoy.extensions.common.matching.v3.ExtensionWithMatcherPerRoute` | ||
| proto](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/common/matching/v3/extension_matcher.proto#L39C9-L39C37) | ||
| proto. In this proto, the | ||
| [xds_matcher](https://github.com/envoyproxy/envoy/blob/0685d7bf568485eb112df2a9c73248cb8bfc1c37/api/envoy/extensions/common/matching/v3/extension_matcher.proto#L41) | ||
| field must be validated the same way as the corresponding field in the | ||
| top-level config. The value of this field will replace the value of the | ||
| field in the top-level config. | ||
|
|
||
| The parsed representation of the composite filter's config will be | ||
| a matcher tree, using the unified matcher API described in [A106]. | ||
| The actions in the matcher tree will be one of two possible values: a | ||
| list of parsed filter configs, or an indication that the filter should | ||
| be skipped (in the case of a `SkipFilter` proto). | ||
|
|
||
| Note that in order to avoid potential stack overflows, we will impose | ||
| a maximum recursion depth of 8 when parsing HTTP filter configs. | ||
|
|
||
| #### Nested Filter Validation | ||
|
|
||
| Filter configs within the matcher tree will be validated using the xDS | ||
| HTTP filter registry, just as the top-level filters in the HTTP connection | ||
| manager config are. | ||
|
|
||
| It will be considered a configuration error if a nested filter is a | ||
| terminal filter (as described in [A39]). While it is conceivably | ||
| possible that there could be a use-case where the composite filter is | ||
| *intended* to provide a terminal filter, it will be difficult to | ||
| determine at config validation time whether this will actually happen | ||
| in all cases, so for now we will simply disallow this. | ||
|
|
||
| Note that, as per [A39], any given xDS HTTP filter may be supported on | ||
| only the gRPC client or server side. When processing the filter list | ||
| in LDS, we know whether the resource is an API listener (client side) | ||
| or socket listener (server side), so we reject filters that are not | ||
| supported on the side they are being configured on. However, because | ||
| the composite filter's per-route override config may be delivered via | ||
| RDS instead of LDS, it will in this case not be possible for gRPC to | ||
| detect that a filter is being configured on an unsupported side when | ||
| validating the RDS resource. Instead, the composite filter will need | ||
| to handle this on a per-RPC basis: if a nested filter chain includes a | ||
| filter that is not supported on the side that it is running on, it will | ||
| fail the RPC with status UNAVAILABLE. Note that if the problematic | ||
| filter is not the first filter in the nested filter chain, implementations | ||
| may fail the RPC without ever starting to process the RPC on that filter | ||
| chain. | ||
|
|
||
| ### CEL Attributes | ||
|
|
||
| In addition to the CEL request attributes described in [A106], we will | ||
| also add support for some additional [CEL | ||
| attributes](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes.html) | ||
| that we expect to be useful for the composite filter. | ||
|
|
||
| On both the gRPC client and server sides, we will add support for the | ||
| `xds.route_metadata.filter_metadata` attribute. To support this, we will | ||
| add support for parsing the [`Route.metadata` | ||
| field](https://github.com/envoyproxy/envoy/blob/f384ab2b3e3aa0564ef25f57dc2ed8ad61eaf0cb/api/envoy/config/route/v3/route_components.proto#L319) | ||
| in the xDS RouteConfiguration. This field will be validated the same | ||
| way as cluster metadata, as described in [A83]. The parsed metadata map | ||
| will be added to the route in the parsed RouteConfiguration resource, | ||
| and that map will be accessed by this CEL attribute. Note that we will | ||
| support only `filter_metadata`, not `typed_filter_metadata`, so that we | ||
| do not have to handle protobuf descriptor functionality; to that end, we | ||
| will use only those entries in the parsed metadata map that correspond | ||
| to `google.protobuf.Struct` type. | ||
|
|
||
| We will also add support for the following attributes on the gRPC server | ||
| side only (these attributes are not relevant on the client side): | ||
| - `source.address` | ||
| - `source.port` | ||
| - `connection.requested_server_name` | ||
| - `connection.tls_version` | ||
| - `connection.sha256_peer_certificate_digest` | ||
|
|
||
| ### Filter Behavior | ||
|
|
||
| When the filter sees the client's initial metadata, it will evaluate the | ||
| matcher tree for the RPC. The result of that evaluation will be one of | ||
| the following: | ||
|
|
||
| - If the matcher tree does not find a match, the RPC will be failed with | ||
| UNAVAILABLE status. | ||
| - If the matcher tree finds a `SkipFilter` match, the filter will simply | ||
| pass the RPC through to the next filter (the one after the composite | ||
| filter), without delegating to any nested filters. | ||
| - If the matcher tree finds an `ExecuteFilterAction` match, then the | ||
| filter will generate a random number and check the sample_percent | ||
| field to determine if the RPC should be sampled. If the RPC is not | ||
| sampled, then the filter will pass the RPC through to the next filter | ||
| (the one after the composite filter), without delegating to any nested | ||
| filters. Otherwise (if the RPC *is* sampled), the RPC will be passed | ||
| to the nested filter chain before being sent to the next filter (the | ||
| one after the composite filter). | ||
|
|
||
| Note: Because all of the CEL attributes that we are currently supporting | ||
| are available when we see the client's initial metadata, that is the | ||
| point at which the filter will evaluate the matcher tree and decide which | ||
| filter chain to use. After that point, all other filter hooks will be | ||
| delegated to the chosen filter chain. (If we ever in the future need | ||
| to support other attributes that are not yet available at this point, | ||
| such as response attributes, we might need a more complex structure here.) | ||
|
|
||
| ### Temporary environment variable protection | ||
|
|
||
| Support for the composite filter will be guarded by the | ||
| `GRPC_EXPERIMENTAL_XDS_COMPOSITE_FILTER` environment variable. This | ||
| guard will be removed once the feature passes interop tests. | ||
|
|
||
| ## Rationale | ||
|
|
||
| The composite filter API seems a little unusual. One would naively | ||
| have expected it to be structured by having the matcher tree live | ||
| in the config for the composite filter directly, rather than using | ||
| `ExtensionWithMatcher`. Unfortunately, that's not the way the API evolved | ||
| in Envoy, so we'll stick with what already exists for compatibility | ||
| reasons. (Envoy is currently attempting to add a more sane API in | ||
| https://github.com/envoyproxy/envoy/pull/43227. In the future, | ||
| we can support that new API as well.) | ||
|
|
||
| ## Implementation | ||
|
|
||
| Will be implemented in C-core, Java, Go, and Node. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this means we can have filters defined wholly in the RouteConfiguration? The Listener would no longer enumerate all the filters being used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's correct.
In the future, we'll also add ECDS support, which would be another way for the Listener to not enumerate all of the filters being used, even if there is no per-route override.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A39 says that
There must be at least one filter in the list, or the Listener resource will be NACKed.Is this no longer true ?Note that gRPC will not fail validation if the map key specifies a filter instance name that does not exist in the HttpConnectionManager filter list. This is because during an update, the xDS client code cannot know which HttpConnectionManager config is currently being used.Do we ignore such filters currently ? and will we continue doing so ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gRFC does not modify A39 in any way. Both of those statements from A39 are still true.