Skip to content

Commit 2cdf0a1

Browse files
authored
ext_authz: document the limitations of filter_enabled_metadata (envoyproxy#42462)
## Description This PR clearly documents the limitations of using `filter_enabled_metadata` in the ExtAuthZ filter and that it doesn't work if the filter itself is disabled in the main filter chain or using a per-route override. We have also provided examples using `ExtensionWithMatcher` which should be the recommended way to invoke filters conditionally. The [previous attempt](envoyproxy#41937) which broke some expectations is reverted. Fix envoyproxy#41501 --- **Commit Message:** ext_authz: document the limitations of filter_enabled_metadata **Additional Description:** Add documentation for describing the limitations around using `filter_enabled_metadata` and provide workaround examples using `ExtensionWithMatcher`. **Risk Level:** N/A **Testing:** CI **Docs Changes:** Added **Release Notes:** N/A --------- Signed-off-by: Rohit Agrawal <rohit.agrawal@databricks.com>
1 parent ab62d9e commit 2cdf0a1

5 files changed

Lines changed: 357 additions & 0 deletions

File tree

api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,24 @@ message ExtAuthz {
165165

166166
// Specifies if the filter is enabled with metadata matcher.
167167
// If this field is not specified, the filter will be enabled for all requests.
168+
//
169+
// .. note::
170+
//
171+
// This field is only evaluated if the filter is instantiated. If the filter is marked with
172+
// ``disabled: true`` in the :ref:`HttpFilter
173+
// <envoy_v3_api_msg_extensions.filters.network.http_connection_manager.v3.HttpFilter>`
174+
// configuration or in per-route configuration via :ref:`ExtAuthzPerRoute
175+
// <envoy_v3_api_msg_extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute>`,
176+
// the filter will not be instantiated and this field will have no effect.
177+
//
178+
// .. tip::
179+
//
180+
// For dynamic filter activation based on metadata (such as metadata set by a preceding
181+
// filter), consider using :ref:`ExtensionWithMatcher
182+
// <envoy_v3_api_msg_extensions.common.matching.v3.ExtensionWithMatcher>` instead. This
183+
// provides a more flexible matching framework that can evaluate conditions before filter
184+
// instantiation. See the :ref:`ext_authz filter documentation
185+
// <config_http_filters_ext_authz>` for examples.
168186
type.matcher.v3.MetadataMatcher filter_enabled_metadata = 14;
169187

170188
// Specifies whether to deny the requests when the filter is disabled.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
static_resources:
2+
listeners:
3+
- name: listener_0
4+
address:
5+
socket_address:
6+
protocol: TCP
7+
address: 0.0.0.0
8+
port_value: 10000
9+
filter_chains:
10+
- filters:
11+
- name: envoy.filters.network.http_connection_manager
12+
typed_config:
13+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
14+
stat_prefix: ingress_http
15+
route_config:
16+
name: local_route
17+
virtual_hosts:
18+
- name: local_service
19+
domains: ["*"]
20+
routes:
21+
- match:
22+
prefix: "/"
23+
route:
24+
host_rewrite_literal: upstream.com
25+
cluster: upstream_com
26+
http_filters:
27+
# Lua filter sets dynamic metadata that controls whether ext_authz runs.
28+
- name: envoy.filters.http.lua
29+
typed_config:
30+
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
31+
default_source_code:
32+
inline_string: |
33+
function envoy_on_request(request_handle)
34+
-- Set metadata to conditionally enable ext_authz.
35+
-- For example, enable auth for requests to /secure paths.
36+
local path = request_handle:headers():get(":path")
37+
if string.match(path, "^/secure") then
38+
request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.ext_authz", "require_auth", "true")
39+
else
40+
request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.ext_authz", "require_auth", "false")
41+
end
42+
end
43+
# ExtensionWithMatcher wraps ext_authz and conditionally invokes it based on dynamic metadata.
44+
- name: ext-authz-with-matcher
45+
typed_config:
46+
"@type": type.googleapis.com/envoy.extensions.common.matching.v3.ExtensionWithMatcher
47+
extension_config:
48+
name: envoy.filters.http.ext_authz
49+
typed_config:
50+
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
51+
grpc_service:
52+
envoy_grpc:
53+
cluster_name: ext-authz
54+
timeout: 0.5s
55+
include_peer_certificate: true
56+
# The xds matcher evaluates dynamic metadata to decide whether to invoke ext_authz.
57+
# We use matcher_list with custom_match because DynamicMetadataInput returns a custom
58+
# MetadataMatchData type that requires a custom matcher and not exact_match_map.
59+
xds_matcher:
60+
matcher_list:
61+
matchers:
62+
- predicate:
63+
single_predicate:
64+
input:
65+
name: envoy.matching.inputs.dynamic_metadata
66+
typed_config:
67+
"@type": type.googleapis.com/envoy.extensions.matching.common_inputs.network.v3.DynamicMetadataInput
68+
filter: envoy.filters.http.ext_authz
69+
path:
70+
- key: require_auth
71+
custom_match:
72+
name: envoy.matching.matchers.metadata_matcher
73+
typed_config:
74+
"@type": type.googleapis.com/envoy.extensions.matching.input_matchers.metadata.v3.Metadata
75+
value:
76+
string_match:
77+
exact: "false"
78+
# When require_auth is "false", skip ext_authz.
79+
on_match:
80+
action:
81+
name: skip
82+
typed_config:
83+
"@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter
84+
- name: envoy.filters.http.router
85+
typed_config:
86+
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
87+
88+
clusters:
89+
- name: ext-authz
90+
type: STATIC
91+
typed_extension_protocol_options:
92+
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
93+
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
94+
explicit_http_config:
95+
http2_protocol_options: {}
96+
load_assignment:
97+
cluster_name: ext-authz
98+
endpoints:
99+
- lb_endpoints:
100+
- endpoint:
101+
address:
102+
socket_address:
103+
address: 127.0.0.1
104+
port_value: 10003
105+
- name: upstream_com
106+
type: LOGICAL_DNS
107+
# Comment out the following line to test on v6 networks.
108+
dns_lookup_family: V4_ONLY
109+
lb_policy: ROUND_ROBIN
110+
load_assignment:
111+
cluster_name: service_upstream_com
112+
endpoints:
113+
- lb_endpoints:
114+
- endpoint:
115+
address:
116+
socket_address:
117+
address: upstream.com
118+
port_value: 443
119+
transport_socket:
120+
name: envoy.transport_sockets.tls
121+
typed_config:
122+
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
123+
sni: upstream.com

docs/root/configuration/http/http_filters/ext_authz_filter.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,48 @@ Per-Route Configuration
134134
A sample virtual host and route filter configuration.
135135
In this example, we add additional context on the virtual host and disable the filter for ``/static``-prefixed routes.
136136

137+
Conditional Filter Activation with Dynamic Metadata
138+
----------------------------------------------------
139+
140+
When you need to conditionally invoke the ext_authz filter based on dynamic metadata set by a preceding
141+
filter (such as a Lua filter), it is recommended to use :ref:`ExtensionWithMatcher
142+
<envoy_v3_api_msg_extensions.common.matching.v3.ExtensionWithMatcher>` rather than the
143+
:ref:`filter_enabled_metadata <envoy_v3_api_field_extensions.filters.http.ext_authz.v3.ExtAuthz.filter_enabled_metadata>`
144+
field.
145+
146+
The key differences are:
147+
148+
* **ExtensionWithMatcher**: Evaluates matching conditions before filter instantiation. The filter is only
149+
created and invoked when the matcher determines it should run. This is the recommended approach for
150+
metadata-based conditional invocation.
151+
152+
* **filter_enabled_metadata**: Only evaluated after the filter is instantiated. If the filter is marked with
153+
``disabled: true`` in the HttpFilter configuration, it will not be instantiated and ``filter_enabled_metadata``
154+
will have no effect.
155+
156+
The following example demonstrates using ExtensionWithMatcher to conditionally invoke ext_authz based on
157+
dynamic metadata set by a Lua filter:
158+
159+
.. literalinclude:: _include/ext-authz-extension-with-matcher.yaml
160+
:language: yaml
161+
:lines: 26-83
162+
:lineno-start: 26
163+
:linenos:
164+
:caption: :download:`ext-authz-extension-with-matcher.yaml <_include/ext-authz-extension-with-matcher.yaml>`
165+
166+
In this configuration:
167+
168+
* The Lua filter examines the request path and sets dynamic metadata (``envoy.filters.http.ext_authz.require_auth``)
169+
to indicate whether authorization is required.
170+
* The ExtensionWithMatcher uses :ref:`DynamicMetadataInput
171+
<envoy_v3_api_msg_extensions.matching.common_inputs.network.v3.DynamicMetadataInput>` to read this metadata.
172+
* When ``require_auth`` is ``true``, the ext_authz filter is invoked.
173+
* When ``require_auth`` is ``false``, the :ref:`SkipFilter
174+
<envoy_v3_api_msg_extensions.filters.common.matcher.action.v3.SkipFilter>` action causes the filter to be skipped.
175+
176+
This pattern provides clean separation between the decision logic (in the Lua filter) and the authorization
177+
enforcement (in ext_authz), while ensuring the ext_authz filter is only instantiated and invoked when needed.
178+
137179
Statistics
138180
----------
139181
.. _config_http_filters_ext_authz_stats:

test/extensions/filters/http/ext_authz/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ envoy_extension_cc_test(
8181
":logging_test_filter_lib",
8282
"//source/extensions/clusters/dns:dns_cluster_lib",
8383
"//source/extensions/filters/http/ext_authz:config",
84+
"//source/extensions/filters/http/lua:config",
85+
"//source/extensions/filters/http/match_delegate:config",
8486
"//source/extensions/listener_managers/validation_listener_manager:validation_listener_manager_lib",
87+
"//source/extensions/matching/http/metadata_input:metadata_input_lib",
88+
"//source/extensions/matching/input_matchers/metadata:config",
8589
"//source/extensions/network/dns_resolver/getaddrinfo:config",
8690
"//source/server/config_validation:server_lib",
8791
"//test/integration:http_integration_lib",
@@ -91,7 +95,12 @@ envoy_extension_cc_test(
9195
"@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto",
9296
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
9397
"@envoy_api//envoy/config/listener/v3:pkg_cc_proto",
98+
"@envoy_api//envoy/extensions/common/matching/v3:pkg_cc_proto",
99+
"@envoy_api//envoy/extensions/filters/common/matcher/action/v3:pkg_cc_proto",
94100
"@envoy_api//envoy/extensions/filters/http/ext_authz/v3:pkg_cc_proto",
101+
"@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto",
102+
"@envoy_api//envoy/extensions/matching/common_inputs/network/v3:pkg_cc_proto",
103+
"@envoy_api//envoy/extensions/matching/input_matchers/metadata/v3:pkg_cc_proto",
95104
"@envoy_api//envoy/service/auth/v3:pkg_cc_proto",
96105
],
97106
)

0 commit comments

Comments
 (0)