diff --git a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto index 2cc6e689b6e20..322e9bdde91c3 100644 --- a/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto +++ b/api/envoy/extensions/filters/http/ext_authz/v3/ext_authz.proto @@ -484,7 +484,7 @@ message BufferSettings { // metadata as well as body may be added to the client's response. See :ref:`allowed_client_headers // ` // for details. -// [#next-free-field: 11] +// [#next-free-field: 12] message HttpService { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.http.ext_authz.v2.HttpService"; @@ -502,6 +502,12 @@ message HttpService { // Only one of ``path_prefix`` or ``path_override`` may be set. string path_override = 10; + // Overrides the HTTP method of the authorization request sent to the authorization service. + // If not set, the original request method is forwarded. This is useful when the authorization + // service exposes a single fixed endpoint (e.g. ``POST /auth``) regardless of the original + // request method. + string method_override = 11; + // Settings used for controlling authorization request metadata. AuthorizationRequest authorization_request = 7; diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 84bf69e3abd2c..7735da189b3a4 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -18,5 +18,692 @@ removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` new_features: +- area: ext_authz + change: | + Added :ref:`shadow_mode + ` to the HTTP + ext_authz filter. When enabled, the filter calls the authorization service as normal but never + terminates the request. The authorization decision is written to FilterState as a + :ref:`ShadowDecision ` + under the filter's configured name with a ``.shadow`` suffix + (``envoy.filters.http.ext_authz.shadow`` by default) so that a subsequent filter can read + and optionally enforce it. +- area: lua + change: | + Added stats API support to the :ref:`Lua filter `, allowing Lua + scripts to create and increment counters, gauges, and histograms via + ``handle:streamInfo():stats()``. +- area: dynamic_modules + change: | + Added ``envoy_dynamic_module_callback_is_validation_mode`` ABI callback that allows dynamic + modules to check if the server is running in config validation mode. +- area: ratelimit + change: | + Added ``is_negative_hits`` boolean to the ``hits_addend`` + configuration in the rate limit API. This allows rate limit + requests to add to the rate limit budget (refill previously + consumed tokens) via + :ref:`HitsAddend.is_negative_hits + `, + or per descriptor in + :ref:`RateLimitDescriptor.is_negative_hits + `. +- area: redis + change: | + Added zone-aware routing support for Redis Cluster proxy. New read policies + ``LOCAL_ZONE_AFFINITY`` and ``LOCAL_ZONE_AFFINITY_REPLICAS_AND_PRIMARY`` route read requests + to replicas in the same availability zone. Zone discovery is enabled via + :ref:`enable_zone_discovery + `. + Note: This feature currently works with Valkey only. +- area: overload_manager + change: | + Added :ref:`ShrinkHeapConfig ` typed + configuration for the ``envoy.overload_actions.shrink_heap`` overload action. This allows + operators to configure the timer interval (``timer_interval``, minimum 1s, default 10s) and + the memory release threshold (``max_unfreed_memory_bytes``, default 100MB) passed to + ``tcmalloc::MallocExtension::ReleaseMemoryToSystem()``. +- area: dns_resolver + change: | + Added :ref:`HickoryDnsResolverConfig + `, a new DNS + resolver using the `Hickory DNS `_ library. +- area: dynamic_modules + change: | + Added :ref:`tracer ` + support for dynamic modules, enabling custom distributed tracing backends to be implemented + in dynamic modules. +- area: dynamic_modules + change: | + Added upstream HTTP TCP bridge extension for dynamic modules. This enables modules to transform + HTTP requests into raw TCP data for upstream connections and convert TCP responses back into HTTP + responses via explicit send callbacks. See :ref:`envoy.upstreams.http.dynamic_modules + `. +- area: filters + change: | + Added filters to update the filter state in :ref:`a listener filter `. +- area: tls + change: | + Added a per-connection filter state object to select a workload trust domain in the SPIFFE validator in + the multi-tenant deployments. +- area: tls + change: | + Extended TLS certificate compression (RFC 8879): added brotli to QUIC (which already supported zlib), + and added brotli and zlib to TCP TLS. Controlled by runtime flag + ``envoy.reloadable_features.tls_certificate_compression_brotli`` (defaults to ``true``). + When disabled, QUIC retains zlib-only compression, while TCP TLS has no compression. +- area: tls + change: | + Exposed the SHA-256 fingerprint and serial number of the verified issuer (CA) certificate from + the validated downstream mTLS peer certificate chain. Accessible via the + ``%DOWNSTREAM_PEER_ISSUER_FINGERPRINT_256%`` and ``%DOWNSTREAM_PEER_ISSUER_SERIAL%`` + :ref:`access log formatters `, and via the + ``sha256PeerCertificateIssuerDigest()`` and ``serialNumberPeerCertificateIssuer()`` methods on + :ref:`downstreamSslConnection() ` in the Lua filter. +- area: dynamic_modules + change: | + Added custom metrics (counters, gauges, histograms) support to load balancer dynamic modules. + Modules can now define metrics during configuration and record them during host selection. +- area: dynamic_modules + change: | + Rust SDK now provides an opt-in ``CatchUnwind`` wrapper for filter callbacks. When a + wrapped callback panics, Envoy logs the panic and returns a fail-closed error (e.g. + HTTP 500, stream reset, connection close) instead of aborting the process. +- area: http_11_proxy + change: | + Added ability to configure a default proxy address that is used when the proxy address is not + configured via metadata. +- area: compressor + change: | + Added :ref:`weaken_etag_on_compress + ` + to the :ref:`compressor filter `. When enabled in + ``response_direction_config``, strong ``ETag`` response headers are weakened (``W/`` prefix) + instead of removed when compression is applied, allowing caches and conditional requests to + work while indicating the body was modified by compression. When both ``weaken_etag_on_compress`` + and ``disable_on_etag_header`` are true, the new field takes precedence. +- area: golang + change: | + Added ``DownstreamSslConnection()`` method to the Golang HTTP filter's ``StreamInfo`` interface, + providing access to SSL/TLS connection information for the downstream connection. This includes + peer certificate details (subject, issuer, serial number, SANs, validity), TLS version, cipher + suite, and PEM-encoded certificates. This achieves feature parity with the Lua filter's + ``downstreamSslConnection()`` functionality. +- area: outlier_detection + change: | + Added :ref:`detect_degraded_hosts ` + to enable passive degraded host detection. When enabled, outlier detection marks hosts as degraded when they return + the ``x-envoy-degraded`` header. Degraded hosts are deprioritized in load balancing but remain in rotation (not + ejected). The degraded state is cleared using the same backoff algorithm as ejection. Defaults to ``false``. +- area: http2 + change: | + Added :ref:`max_header_field_size_kb + ` to configure the + maximum wire-encoded size in KiB of an individual HPACK-encoded header field that the HTTP/2 + codec will accept. This allows increasing the default nghttp2 per-header limit of 64 KiB on the + wire when larger single headers need to be supported. +- area: dynamic_modules + change: | + Added dynamic module input matcher extension that allows implementing custom matching logic + in external languages (Rust, Go, C) via dynamic modules. +- area: dynamic_modules + change: | + Added listener lifecycle event callbacks to the bootstrap dynamic module extension. Modules can + opt in via ``enable_listener_lifecycle`` to receive ``on_listener_add_or_update`` and + ``on_listener_removal`` notifications when listeners change in the ``ListenerManager``. +- area: memory + change: | + Added ``soft_memory_limit_bytes``, ``max_per_cpu_cache_size_bytes``, and ``max_unfreed_memory_bytes`` + fields to :ref:`MemoryAllocatorManager ` + for fine-grained control of tcmalloc memory management. +- area: dynamic_modules + change: | + Added :ref:`TLS certificate validator + ` + support for dynamic modules, enabling custom TLS certificate validation to be implemented in dynamic modules. +- area: dynamic_modules + change: | + Added filter state read/write support for dynamic module cert validators, allowing modules to set and + get string values in the connection's filter state during certificate chain verification. +- area: dynamic_modules + change: | + Added ``write_to_socket`` and ``close_socket`` ABI callbacks for the dynamic module listener + filter, enabling protocol negotiation use cases such as Postgres SSL and MySQL handshake at the listener + filter level. +- area: dynamic_modules + change: | + Added HTTP callout support for dynamic module listener filters, enabling listener filters to initiate + asynchronous HTTP requests to upstream clusters and receive responses via the ``send_http_callout`` ABI + callback and ``on_listener_filter_http_callout_done`` event hook. +- area: ext_authz + change: | + Added :ref:`path_override ` + to the HTTP ext_authz filter. When set, the request path sent to the authorization service is replaced + entirely by this value. Only one of ``path_prefix`` or ``path_override`` may be set; validation fails + at config load if both are specified. +- area: ext_authz + change: | + Added :ref:`method_override + ` + to the HTTP ext_authz filter. When set, the HTTP method of the request sent to the authorization + service is replaced with this value, regardless of the original request method. This allows + the authorization service to expose a single fixed endpoint (e.g. ``POST /auth``) rather than + handling every method the upstream receives. +- area: stats + change: | + Added support to limit the number of metrics stored in each scope within the stats library. +- area: stats + change: | + The admin prometheus stats endpoint now supports the protobuf exposition format, and will automatically + use it if the request contains the correct Accept header, or if query parameter ``prom_protobuf=1`` is + set. In a prometheus scrape configuration, add ``PrometheusProto`` to ``scrape_protocols`` to use + the protobuf format. Additionally, when using the protobuf exposition format, the admin prometheus stats + endpoint now supports `native histograms `_ + when using the prometheus protobuf exposition format, using query + ``/stats/prometheus?histogram_buckets=prometheusnative``. +- area: stats + change: | + Added support for cluster-level stats matcher, allowing more granular control over which stats + are enabled and reported at the cluster level. This the stats matcher could be configured via + the xDS API dynamically on a per-cluster basis. + See :ref:`envoy.stats_matcher ` for more details. +- area: stats + change: | + Added support for listener-level stats matcher via ``typed_filter_metadata`` key + ``envoy.stats_matcher`` in the listener's + :ref:`metadata `. When present, + the provided :ref:`StatsMatcher ` replaces + the global stats matcher for that listener's stats scope, allowing fine-grained control over + which listener stats are created. + If the value of ``envoy.stats_matcher`` is set but does not contain a valid ``StatsMatcher`` + message, this will be treated as an invalid configuration and the listener will fail to start. + This strict validation can be disabled temporarily by setting the runtime guard + ``envoy.reloadable_features.strict_stats_matcher_unpacked`` to ``false``. +- area: tls + change: | + Added support for fetching certificates on-demand via SDS in the upstream TLS transport socket + using the extension :ref:`on-demand certificate selector + `. +- area: access_log + change: | + Added stats customization support for the :ref:`access logger `. +- area: dynamic modules + change: | + Introduced the extended ABI forward compatibility mechanism for dynamic modules + where modules built with an SDK version can be loaded by Envoy + binaries of the next Envoy version. For example, a module built with the v1.38 SDK + can now be loaded by an Envoy binary of v1.39. +- area: dynamic modules + change: | + Added drain and shutdown lifecycle hooks for bootstrap dynamic modules. +- area: dynamic modules + change: | + Added support for dynamic modules authors to register any combination of HTTP, network, listener, + UDP listener, and bootstrap filters in the Rust SDK. +- area: dynamic modules + change: | + Added connection state and flow control ABI callbacks for the dynamic module network filter, + including ``read_disable``, ``read_enabled``, ``get_connection_state``, ``enable_half_close``, + ``is_half_close_enabled``, ``get_buffer_limit``, ``set_buffer_limits``, and + ``above_high_watermark``. +- area: dynamic modules + change: | + Added socket property getter and SSL/TLS information ABI callbacks for the dynamic module listener + filter, including ``get_requested_server_name``, ``get_detected_transport_protocol``, + ``get_requested_application_protocols``, ``get_ja3_hash``, ``get_ja4_hash``, ``is_ssl``, + ``get_ssl_uri_sans``, ``get_ssl_dns_sans``, and ``get_ssl_subject``. +- area: mcp_router + change: | + Added support for MCP resource methods ``resources/list``, ``resources/read``, + ``resources/subscribe``, and ``resources/unsubscribe``. +- area: mcp_router + change: | + Added support for MCP prompt methods ``prompts/list`` and ``prompts/get``. +- area: matching + change: | + Added an optional ``field`` parameter to + :ref:`FilterStateInput `. + When set, ``FilterStateInput`` calls ``getField()`` on the filter state object instead of + ``serializeAsString()``, enabling direct matching on individual fields within composite filter + state objects such as proxy protocol TLVs stored via ``tlv_location: FILTER_STATE``. +- area: ratelimit + change: | + Added per-descriptor ``x-ratelimit-*`` headers support. See the + :ref:`x_ratelimit_option ` + field documentation for more details. +- area: ratelimit + change: | + Added ``RemoteAddressMatch`` action to the rate limit filter. This action will generate a descriptor based on the remote address of the + downstream connection by matching it against specified CIDR ranges with support for inversion and formatter substitution. +- area: mcp_router + change: | + Added support for MCP client-to-server notification methods ``notifications/cancelled`` + and ``notifications/roots/list_changed``. +- area: dynamic_modules + change: | + Added typed filter state support for dynamic module HTTP and network filters. This allows modules + to set and get filter state objects using registered ``StreamInfo::FilterState::ObjectFactory`` + instances, enabling interoperability with built-in Envoy filters that expect specific typed objects. +- area: mcp_router + change: | + Added support for MCP completion method ``completion/complete`` with routing based on + ``ref/prompt`` or ``ref/resource``. +- area: mcp_router + change: | + Added support for MCP logging method ``logging/setLevel``. +- area: upstream + change: | + Coalesced load balancer rebuilds during EDS batch host updates. When multiple priorities change in a + single batch, each LB level (LoadBalancerBase, ZoneAwareLoadBalancerBase, EdfLoadBalancerBase, + ThreadAwareLoadBalancerBase) now defers expensive per-priority recalculations to a single pass after + the batch completes, reducing CPU spikes on large clusters. This behavior can be reverted by setting + the runtime guard ``envoy.reloadable_features.coalesce_lb_rebuilds_on_batch_update`` to ``false``. +- area: cel + change: | + Added functionality to reevaluate CEL expressions that attempt to read response path data on the + request path once the data is available. Allows CEL matching based on both request and response + headers. This may cause a behavior change for matchers that previously would silently fail to + match due to attempting to match response headers in the request path. This behavior can be + reverted by setting the runtime guard + ``envoy.reloadable_features.enable_cel_response_path_matching`` to ``false``. +- area: access_log + change: | + Added support for gauges in the :ref:`stats access logger `. +- area: network + change: | + Added access logging support for network filters, similar to HTTP filters, by allowing network filters to + register as access logger instances. +- area: formatter + change: | + Extended ``*_WITHOUT_PORT`` address formatters to accept an optional ``MASK_PREFIX_LEN`` parameter + that masks IP addresses and returns them in CIDR notation (e.g., ``%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT(16)%`` + returns ``10.1.0.0/16`` for client IP ``10.1.10.23``). +- area: dynamic_modules + change: | + Added :ref:`cluster + ` + support for dynamic modules, enabling custom service discovery and host management to be implemented + in dynamic modules. +- area: dynamic_modules + change: | + Added server lifecycle callbacks (``on_server_initialized``, ``on_drain_started``, ``on_shutdown``) + for dynamic module custom clusters, enabling modules to react to server readiness, drain, and + shutdown events. +- area: dynamic_modules + change: | + Changed the ``details`` parameter in ``cluster_lb_async_host_selection_complete`` ABI callback + from raw ``const char*`` and ``size_t`` to ``envoy_dynamic_module_type_module_buffer`` for + consistency with the ABI style guide. +- area: stat_sinks + change: | + Added support for exporting OpenTelemetry metrics via HTTP. The new ``http_service`` field + in :ref:`SinkConfig ` + enables direct OTLP metrics export to backends that only accept HTTP (Dynatrace, Datadog, Elastic), + without requiring an intermediate collector sidecar. +- area: ratelimit + change: | + Added support for shadow mode in the local rate limit filter. +- area: config + change: | + Added support for :ref:`set_node_on_first_message_only + ` to Delta-xDS. + Guarded by runtime flag ``envoy.reloadable_features.xds_legacy_delta_skip_subsequent_node``. +- area: formatter + change: | + Added the new access log formatter ``DOWNSTREAM_LOCAL_CLOSE_REASON``. +- area: formatter + change: | + Added ``%UPSTREAM_DETECTED_CLOSE_TYPE%`` and ``%DOWNSTREAM_DETECTED_CLOSE_TYPE%`` to expose the detected + close type of downstream and upstream connections. The possible values are ``Normal``, ``LocalReset``, + and ``RemoteReset``. +- area: formatter + change: | + Added extensions for :ref:`%FILE_CONTENT(/path/to/file)% ` + and :ref:`%SECRET(name)% `. +- area: http_service + change: | + Added the ability for :ref:`request_headers_to_add ` + to use a formatter extension that can retrieve secrets, for including authentication tokens. This support is added for + all uses of this message, including the open telemetry, ``ext_proc``, and ``zipkin`` tracers. +- area: reverse_tunnel + change: | + Added optional tenant isolation support to the reverse tunnel network filter. When + ``enable_tenant_isolation`` is set, Envoy scopes cached reverse tunnel sockets with composite + ``@`` and ``@`` identifiers and rejects handshake headers that already + contain the ``@`` delimiter to prevent ambiguous lookups. +- area: tcp_proxy + change: | + Added an option to emit a log entry when the connection is accepted. +- area: mcp_router + change: | + Added SSE (Server-Sent Events) streaming support for MCP backend responses. The router now handles + SSE responses from backends for ``tools/call`` with direct pass-through streaming, and supports SSE + aggregation for fanout operations (``tools/list``, ``initialize``) with incremental event parsing. +- area: http + change: | + Added an optional :ref:`JSON format ` + for the :ref:`x-forwarded-client-cert ` + (XFCC) header. The format can be configured via :ref:`format + `. +- area: dynamic_modules + change: | + Added :ref:`load balancing policies + ` + support for dynamic modules, enabling custom load balancing algorithms to be implemented in dynamic modules. +- area: dynamic_modules + change: | + Added ``get_host_health_by_address`` ABI callback for dynamic module load balancers, providing O(1) + host health lookup by address string using the cross-priority host map. +- area: sse_parser + change: | + Extended the SSE parser utility to support all standard SSE fields: ``id``, ``event`` (as ``event_type``), + and ``retry``, in addition to the existing ``data`` field. The ``retry`` field is parsed as a ``uint32_t`` + and only accepts values consisting of ASCII digits per the SSE specification. +- area: http + change: | + Added :ref:`envoy.filters.http.sse_to_metadata ` filter for extracting + values from Server-Sent Events (SSE) streams and writing them to dynamic metadata. Useful for capturing + token usage metrics from LLM API responses. Supports pluggable content parsers for different SSE data formats. +- area: content_parsers + change: | + Added :ref:`envoy.content_parsers.json ` content parser for extracting + values from JSON content using JSON path selectors. Can be used by filters that need to parse structured + JSON data and extract specific fields into metadata. +- area: resource_monitors + change: | + Added cgroup v2 support to the CPU utilization resource monitor. The monitor now automatically + detects and selects between cgroup v1 and v2 at runtime by checking available cgroup files on + the system. This enables the resource monitor to work correctly in both cgroup v1 and v2 + environments without configuration changes. +- area: connection + change: | + Add support for closing connections when they stay above the buffer high watermark for a configured time. + This can be enabled by setting the :ref:`per_connection_buffer_high_watermark_timeout + ` + field on the listener and :ref:`per_connection_buffer_high_watermark_timeout + ` + field on the cluster. By default, the timeout is disabled. +- area: dynamic_modules + change: | + Added configurable :ref:`metrics_namespace + ` field + to ``DynamicModuleConfig``. This allows users to customize the prefix used for all metrics + created by dynamic modules. Metrics now appear with the standard ``envoy_`` prefix followed by + the namespace in prometheus output (e.g. ``envoy_myapp_requests_total``). The legacy behavior + (stripping the namespace prefix from prometheus output) can be restored by setting the runtime + guard ``envoy.reloadable_features.dynamic_modules_strip_custom_stat_prefix`` to ``true``. +- area: oauth2 + change: | + Added ``TLS_CLIENT_AUTH`` for the OAuth2 HTTP filter to support RFC 8705 mutual TLS client + authentication. In this mode ``token_secret`` is optional and ignored, and the token endpoint + cluster must be configured with mTLS. +- area: dynamic_modules + change: | + Added a process-wide function registry to the dynamic modules ABI. Modules can register + functions by name via ``envoy_dynamic_module_callback_register_function`` and other modules + can resolve them via ``envoy_dynamic_module_callback_get_function``, enabling zero-copy + cross-module interactions analogous to ``dlsym``. +- area: dynamic_modules + change: | + Added a process-wide shared data registry to the dynamic modules ABI. +- area: dynamic_modules + change: | + Added support for loading dynamic module binaries from local file paths via the new + :ref:`module ` + field in ``DynamicModuleConfig``. This allows specifying an absolute path to a ``.so`` file + via ``module.local.filename`` as an alternative to the name-based search path. +- area: dynamic_modules + change: | + Added support for fetching dynamic module binaries from remote HTTP sources via + ``module.remote`` in ``DynamicModuleConfig``. The module is downloaded asynchronously during + listener initialization with SHA256 verification, written to a temporary file, and loaded + via ``dlopen``. If the remote fetch fails, the filter is not installed and requests pass + through (fail-open). +- area: dynamic_modules + change: | + Added caching for remotely fetched dynamic modules. Since ``newDynamicModuleFromBytes`` + writes modules to a deterministic path based on SHA256, subsequent config updates + referencing the same SHA256 load from the existing file, avoiding redundant HTTP fetches. +- area: dynamic_modules + change: | + Added ``nack_on_cache_miss`` option for remote dynamic module sources. When enabled, + uncached remote modules cause configuration rejection (NACK) with a background fetch, + instead of blocking listener warming. This enables remote modules in ECDS and per-route + configurations where an init manager is not available. +- area: dynamic_modules + change: | + Network filter read and write buffers now persist after ``on_read``/``on_write`` callbacks, allowing + modules to access buffered data from ``on_scheduled`` and other callbacks. Added + ``envoy_dynamic_module_callback_network_filter_get_cluster_host_count`` to query cluster host counts + by name, enabling scale-to-zero and custom load balancing decisions in network filters. +- area: dynamic_modules + change: | + Added metrics definition and update support for bootstrap dynamic modules. +- area: dynamic_modules + change: | + Added timer API to the bootstrap extension dynamic modules ABI. +- area: dynamic_modules + change: | + Added admin handler API to the bootstrap extension dynamic modules ABI, enabling modules to + register custom admin HTTP endpoints. +- area: formatter + change: | + Added ``SPAN_ID`` :ref:`access log formatter ` to log the span ID of + the active (downstream) span for a request, complementing the existing ``TRACE_ID`` formatter. +- area: formatter + change: | + Added ``QUERY_PARAMS`` support for substitution formatter to log all query params. + They can either be logged in their original form or decoded. +- area: formatter + change: | + Added new access log formatters for tracking upstream hosts and connection IDs attempted during + request processing: ``%UPSTREAM_HOSTS_ATTEMPTED%``, ``%UPSTREAM_HOSTS_ATTEMPTED_WITHOUT_PORT%``, + ``%UPSTREAM_HOST_NAMES_ATTEMPTED%``, ``%UPSTREAM_HOST_NAMES_ATTEMPTED_WITHOUT_PORT%``, and + ``%UPSTREAM_CONNECTION_IDS_ATTEMPTED%``. These are useful for debugging retry behavior and + understanding which hosts were tried before a successful connection or final failure. +- area: dynamic_modules + change: | + Added init manager integration to the dynamic modules bootstrap extension ABI. An init target + is automatically registered for every bootstrap extension, blocking traffic until the module + signals readiness via ``signal_init_complete``. +- area: dynamic_modules + change: | + Added ``on_host_membership_update`` event hook and ``get_member_update_host_address`` callback + for dynamic module load balancers, enabling modules to receive notifications when hosts are + added or removed from the cluster and inspect the affected host addresses. +- area: geoip + change: | + Added ``asn_org`` field to :ref:`geo_field_keys + ` + to populate a header with the autonomous system organization name from the MaxMind ASN database. +- area: tracers + change: | + Added log events to spans created by the OpenTelemetry tracer. +- area: mcp_router + change: | + Added SSE response support for MCP ``resources/list`` fanout aggregation. +- area: mcp_router + change: | + Added support for MCP ``resources/templates/list`` method with fanout aggregation. +- area: tcp_proxy + change: | + Added :ref:`proxy_protocol_tlv_merge_policy + ` + to control how TLVs in ``proxy_protocol_tlvs`` are merged with existing PROXY protocol state. + Supports ``ADD_IF_ABSENT`` (default), ``OVERWRITE_BY_TYPE_IF_EXISTS_OR_ADD``, and + ``APPEND_IF_EXISTS_OR_ADD``. +- area: admin + change: | + Added ``filter`` query parameter support to the ``/clusters`` endpoint. The parameter accepts a RE2 + regular expression to filter clusters by name. Compatible with the ``format`` parameter for both + text and JSON output (e.g., ``/clusters?filter=service&format=json``). +- area: mcp_router + change: | + Added :ref:`statistics ` to the MCP router filter for + observability into request routing, fanout operations, and error conditions. +- area: mcp + change: | + Added HTTP DELETE session termination support to the MCP filter. DELETE requests with an + ``MCP-Session-Id`` header are now recognized as valid MCP traffic in ``REJECT_NO_MCP`` mode. +- area: a2a + change: | + Added parsing support for the A2A (Agent2Agent) protocol, enabling parsing of A2A JSON-RPC messages. +- area: mcp + change: | + Added options ``propagate_trace_context`` and ``propagate_baggage`` for extracting ``traceparent``, + ``tracestate``, and baggage from MCP parameters, respectively. +- area: mcp_json_rest_bridge + change: | + Added the MCP JSON REST Bridge HTTP filter configuration to transcode MCP JSON-RPC requests into + standard JSON-REST HTTP requests. +- area: http + change: | + Added :ref:`file_server http filter ` to allow responding with + file contents from the filesystem. +- area: mcp_router + change: | + Added SSE response support for MCP ``prompts/list`` fanout aggregation. +- area: mcp_json_rest_bridge + change: | + Added support for MCP session negotiation, including ``initialize`` and + ``notifications/initialized`` methods. This filter is currently a work-in-progress and not + recommended for production use. +- area: listener_manager + change: | + Added ``ListenerUpdateCallbacks`` interface to ``ListenerManager``, similar to the existing + ``ClusterUpdateCallbacks`` on ``ClusterManager``. +- area: matching + change: | + Added :ref:`local reply matcher input ` + to distinguish Envoy generated local replies from upstream responses. This matcher input returns ``true`` + for local replies and ``false`` for upstream responses, enabling ``custom_response`` filter policies to + selectively apply only to locally generated error responses. +- area: mcp_json_rest_bridge + change: | + Added support for MCP ``tools/call`` request transcoding. Support for ``tools/list`` and + ``tools/call`` response transcoding is planned. This filter is currently a work-in-progress and + not recommended for production use. +- area: ext_proc + change: | + Added :ref:`allow_content_length_header + ` to allow + the ext_proc filter to preserve the original ``Content-Length`` header or let ext_proc server modify it as needed. +- area: cluster + change: | + Added :ref:`MCP multi cluster ` extension that combines multiple MCP services + into a single one. This cluster type is primarily intended to support aggregation of multiple MCP services into one. +- area: tls + change: | + Enhanced TLS certificate validation failure messages for CRL-related errors in access logs. The + ``%DOWNSTREAM_TRANSPORT_FAILURE_REASON%`` and ``%UPSTREAM_TRANSPORT_FAILURE_REASON%`` access log + formatters now include the certificate's CRL Distribution Point (CRLDP) information when CRL + validation fails. For errors such as ``CRL for certificate was not provided``, ``CRL has expired``, ``CRL + is not yet valid``, or ``certificate revoked``, the error message now includes the certificate's + CRL distribution points (e.g., ``X509_verify_cert: certificate verification error at depth 0: certificate revocation + check against provided CRLs failed: unable to get certificate CRL, certificate CRL distribution points: + [http://crl.example.com/ca.crl, http://backup-crl.example.com/ca.crl]``). This provides better visibility into CRL + validation failures and helps operators identify connectivity or CRL server issues without requiring debug-level logging. +- area: lua + change: | + Added :ref:`set() ` to the Lua filter + state API, allowing Lua scripts to create and store filter state objects dynamically using + registered object factories. +- area: http + change: | + Fixed an issue where filter chain execution could continue on HTTP streams that had been reset but not yet + destroyed. This could cause use-after-free conditions when filter callbacks were invoked on filters that + had already received ``onDestroy()``. The fix ensures that ``decodeHeaders()``, ``decodeData()``, + ``decodeTrailers()``, and ``decodeMetadata()`` are blocked after a downstream reset. +- area: formatter + change: | + Added support for ``%TRACE_ID%`` in the custom response policy filter's local response policy to allow + trace ID to be substituted into the response body. +- area: redis + change: | + Added support for ``BITFIELD_RO`` in ``redis_proxy``. +- area: network_filter + change: | + Added support for + ``on_downstream_data`` (see + :ref:`envoy_v3_api_field_extensions.filters.network.set_filter_state.v3.Config.on_downstream_data`) + to the :ref:`set_filter_state network filter `, allowing + connection filter state to be populated after first receiving data from the downstream connection. +- area: oauth2 + change: | + Added :ref:`allow_failed_matcher ` + to allow requests to proceed to upstream as unauthenticated when OAuth validation fails (missing, invalid, or expired + credentials) and the request matches the configured matchers. This enables graceful degradation patterns where services + can handle both authenticated and unauthenticated requests. When triggered, all OAuth cookies are stripped and context + headers ``x-envoy-oauth-status: failed`` and ``x-envoy-oauth-failure-reason`` are added. The matcher evaluation follows + priority order: ``pass_through_matcher`` > ``allow_failed_matcher`` > ``deny_redirect_matcher`` > default OAuth behavior. +- area: mcp_json_rest_bridge + change: | + Added support for MCP ``tools/call`` response transcoding. Support for ``tools/list`` is planned. + This filter is currently a work-in-progress and not recommended for production use. +- area: http_filter + change: | + Added support for clear route cache in the :ref:`set_filter_state http filter `. When + ``clear_route_cache`` is set, the filter will clear the route cache for the current request after applying filter state updates. + This is necessary if the route configuration may depend on the filter state values set. +- area: stateful_session + change: | + Added :ref:`status_on_strict_destination_not_found + ` + to the :ref:`stateful session filter `. When + :ref:`strict ` + mode is enabled and the requested destination is not found in the set of available endpoints, Envoy + will return the configured HTTP status code instead of the default ``503``. This does not apply when + the destination exists but is unhealthy. +- area: tcp_proxy + change: | + Propagate upstream TCP RST to downstream when detected close type is RemoteReset. + This behavioral change can be temporarily reverted by setting runtime guard + ``envoy.reloadable_features.propagate_upstream_rst_through_tunneled_tcp_proxy`` to false. +- area: tcp_proxy + change: | + HttpUpstream now maps remote-originated HTTP stream reset reasons (RemoteReset, + RemoteRefusedStreamReset, RemoteConnectionFailure) to RemoteClose with + DetectedCloseType::RemoteReset, enabling RST propagation through tunneled tcp_proxy. + This behavioral change can be temporarily reverted by setting runtime guard + ``envoy.reloadable_features.map_http_stream_reset_to_tcp_rst`` to false. +- area: ratelimit + change: | + Support populating rate limit descriptors from cluster metadata. +- area: ratelimit + change: | + Support populating rate limit descriptors from cluster locality metadata. +- area: http + change: | + Added a new overload action :ref:`envoy.overload_actions.close_idle_http_connections ` + that closes idle downstream HTTP connections when the overload action is active. Currently only HTTP/3 idle connections + trimming is supported, and HTTP/1 and HTTP/2 support are to be implemented. + +- area: contrib + change: | + Added a new :ref:`Kafka stats sink ` contrib extension + (``envoy.stat_sinks.kafka``) that produces metrics directly to an Apache Kafka topic + using librdkafka. Supports both JSON and Protobuf serialization formats; the Protobuf + format uses the same ``StreamMetricsMessage`` wire format as the gRPC + :ref:`metrics_service ` sink, + allowing consumers to reuse existing deserializers. Configurable batching, delta counters, + tag-as-label emission, and full librdkafka producer tuning (including TLS and SASL + authentication) are supported via + :ref:`KafkaStatsSinkConfig `. +- area: tls + change: | + Added support for building Envoy with OpenSSL as an alternative to the default BoringSSL. + This is achieved through a BoringSSL compatibility layer (``bssl-compat``) that translates BoringSSL + API calls to OpenSSL, allowing Envoy's TLS code to remain unchanged. To build with OpenSSL, use + ``--config=openssl``. HTTP/3 (QUIC) is disabled for OpenSSL builds. Note that OpenSSL builds are + not currently covered by the `Envoy security policy `_. + See :repo:`bazel/SSL.md ` for details. +- area: dynamic_modules + change: | + Promoted the dynamic modules HTTP filter (``envoy.filters.http.dynamic_modules``) from alpha + to stable. +- area: attributes + change: | + Added ``connection.peer_certificate`` and ``upstream.peer_certificate`` attributes that provide the + PEM-encoded peer certificate for downstream and upstream TLS connections respectively. deprecated: diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc index e8e9d07795a9c..332408d75e0db 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.cc @@ -124,6 +124,14 @@ absl::StatusOr validatePathOverride(absl::string_view path_override return std::string(path_override); } +absl::StatusOr validateMethodOverride(absl::string_view method_override) { + if (!method_override.empty() && + method_override.find_first_of(" \t\r\n") != absl::string_view::npos) { + return absl::InvalidArgumentError("method_override must not contain whitespace."); + } + return std::string(method_override); +} + absl::Status validateOnlyOneOfPathPrefixOrOverride(absl::string_view path_prefix, absl::string_view path_override) { if (!path_prefix.empty() && !path_override.empty()) { @@ -174,6 +182,8 @@ ClientConfig::ClientConfig(const envoy::extensions::filters::http::ext_authz::v3 path_prefix_(THROW_OR_RETURN_VALUE(validatePathPrefix(path_prefix), std::string)), path_override_(THROW_OR_RETURN_VALUE( validatePathOverride(config.http_service().path_override()), std::string)), + method_override_(THROW_OR_RETURN_VALUE( + validateMethodOverride(config.http_service().method_override()), std::string)), tracing_name_(fmt::format("async {} egress", config.http_service().server_uri().cluster())), request_headers_parser_(THROW_OR_RETURN_VALUE( Router::HeaderParser::configure( @@ -208,6 +218,8 @@ ClientConfig::ClientConfig( THROW_OR_RETURN_VALUE(validatePathPrefix(http_service.path_prefix()), std::string)), path_override_( THROW_OR_RETURN_VALUE(validatePathOverride(http_service.path_override()), std::string)), + method_override_(THROW_OR_RETURN_VALUE(validateMethodOverride(http_service.method_override()), + std::string)), tracing_name_(fmt::format("async {} egress", http_service.server_uri().cluster())), request_headers_parser_(THROW_OR_RETURN_VALUE( Router::HeaderParser::configure( @@ -328,6 +340,8 @@ void RawHttpClientImpl::check(RequestCallbacks& callbacks, } else { headers->addCopy(key, header.raw_value()); } + } else if (key == Http::Headers::get().Method && !config_->methodOverride().empty()) { + headers->addCopy(key, config_->methodOverride()); } else { headers->addCopy(key, header.raw_value()); } @@ -349,6 +363,8 @@ void RawHttpClientImpl::check(RequestCallbacks& callbacks, } else { headers->addCopy(key, header.second); } + } else if (key == Http::Headers::get().Method && !config_->methodOverride().empty()) { + headers->addCopy(key, config_->methodOverride()); } else { headers->addCopy(key, header.second); } diff --git a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h index 3fd7d4a3b760e..75421eb67117b 100644 --- a/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h +++ b/source/extensions/filters/common/ext_authz/ext_authz_http_impl.h @@ -49,6 +49,12 @@ class ClientConfig { */ const std::string& pathOverride() { return path_override_; } + /** + * Returns the authorization request method override. When non-empty, replaces the HTTP method + * on the outgoing authorization request. + */ + const std::string& methodOverride() const { return method_override_; } + /** * Returns authorization request timeout. */ @@ -131,6 +137,7 @@ class ClientConfig { const std::chrono::milliseconds timeout_; const std::string path_prefix_; const std::string path_override_; + const std::string method_override_; const std::string tracing_name_; Router::HeaderParserPtr request_headers_parser_; const bool encode_raw_headers_; diff --git a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc index 767c68cde2002..6feb7a1d9b03a 100644 --- a/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc +++ b/test/extensions/filters/common/ext_authz/ext_authz_http_impl_test.cc @@ -376,6 +376,101 @@ TEST_F(ExtAuthzHttpClientTest, PathOverrideMustStartWithSlash) { "path_override should start with \"/\"."); } +// Verify method_override replaces the original request method on the authorization request. +TEST_F(ExtAuthzHttpClientTest, AuthorizationOkWithMethodOverride) { + const std::string yaml = R"EOF( + http_service: + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 0.25s + method_override: "POST" + )EOF"; + initialize(yaml); + Http::RequestMessagePtr message_ptr = + sendRequest({{":path", "/hello"}, {":method", "GET"}, {"foo", "bar"}}); + EXPECT_EQ(message_ptr->headers().getMethodValue(), "POST"); +} + +// Verify that without method_override, the original request method is forwarded unchanged. +TEST_F(ExtAuthzHttpClientTest, AuthorizationOkWithoutMethodOverride) { + const std::string yaml = R"EOF( + http_service: + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 0.25s + )EOF"; + initialize(yaml); + Http::RequestMessagePtr message_ptr = + sendRequest({{":path", "/hello"}, {":method", "DELETE"}, {"foo", "bar"}}); + EXPECT_EQ(message_ptr->headers().getMethodValue(), "DELETE"); +} + +// Verify method_override with encode_raw_headers also overrides the method. +TEST_F(ExtAuthzHttpClientTest, AuthorizationOkWithMethodOverrideRawHeaders) { + const std::string yaml = R"EOF( + http_service: + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 0.25s + method_override: "POST" + encode_raw_headers: true + )EOF"; + initialize(yaml); + Http::RequestMessagePtr message_ptr = + sendRequest({{":path", "/hello"}, {":method", "GET"}}, {.encode_raw_headers = true}); + EXPECT_EQ(message_ptr->headers().getMethodValue(), "POST"); +} + +// Verify ClientConfig from HttpService directly also captures method_override. +TEST_F(ExtAuthzHttpClientTest, ClientConfigFromHttpServiceWithMethodOverride) { + envoy::extensions::filters::http::ext_authz::v3::HttpService http_service; + TestUtility::loadFromYaml(R"EOF( + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 0.25s + method_override: "POST" + )EOF", + http_service); + auto cfg = std::make_shared(http_service, false, 250, factory_context_); + EXPECT_EQ(cfg->methodOverride(), "POST"); +} + +// Verify method_override containing whitespace is rejected at config load. +TEST_F(ExtAuthzHttpClientTest, MethodOverrideWithWhitespaceRejected) { + const std::string yaml = R"EOF( + http_service: + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 0.25s + method_override: "POST EVIL" + )EOF"; + EXPECT_THROW_WITH_MESSAGE(createConfig(yaml), EnvoyException, + "method_override must not contain whitespace."); +} + +// Verify method_override and path_override can be combined to target a fixed endpoint. +TEST_F(ExtAuthzHttpClientTest, AuthorizationOkWithMethodAndPathOverride) { + const std::string yaml = R"EOF( + http_service: + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 0.25s + method_override: "POST" + path_override: "/auth" + )EOF"; + initialize(yaml); + Http::RequestMessagePtr message_ptr = + sendRequest({{":path", "/original/path"}, {":method", "GET"}, {"foo", "bar"}}); + EXPECT_EQ(message_ptr->headers().getMethodValue(), "POST"); + EXPECT_EQ(message_ptr->headers().getPathValue(), "/auth"); +} + // Verify request body is set correctly when the normal body is empty and raw body is set. TEST_F(ExtAuthzHttpClientTest, AuthorizationOkWithRawBody) { Http::RequestMessagePtr message_ptr = sendRequest( diff --git a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc index 3982766672437..bf91f239a22f6 100644 --- a/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc +++ b/test/extensions/filters/http/ext_authz/ext_authz_integration_test.cc @@ -2658,6 +2658,72 @@ TEST_P(ExtAuthzHttpIntegrationTest, HttpRetryPolicyOldBehaviorWithFlagDisabled) cleanup(); } +// Verifies that method_override replaces the HTTP method on the request sent to the +// authorization server, regardless of the original client request method. +TEST_P(ExtAuthzHttpIntegrationTest, MethodOverride) { + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + auto* ext_authz_cluster = bootstrap.mutable_static_resources()->add_clusters(); + ext_authz_cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); + ext_authz_cluster->set_name("ext_authz"); + + const std::string ext_authz_config = R"EOF( + http_service: + server_uri: + uri: "ext_authz:9000" + cluster: "ext_authz" + timeout: 300s + method_override: "POST" + failure_mode_allow: false + )EOF"; + TestUtility::loadFromYaml(ext_authz_config, proto_config_); + proto_config_.set_encode_raw_headers(encodeRawHeaders()); + + envoy::extensions::filters::network::http_connection_manager::v3::HttpFilter ext_authz_filter; + ext_authz_filter.set_name("envoy.filters.http.ext_authz"); + ext_authz_filter.mutable_typed_config()->PackFrom(proto_config_); + + config_helper_.prependFilter(MessageUtil::getJsonStringFromMessageOrError(ext_authz_filter)); + }); + + HttpIntegrationTest::initialize(); + + // Send a GET request from the client; method_override should cause POST to reach ext_authz. + auto conn = makeClientConnection(lookupPort("http")); + codec_client_ = makeHttpConnection(std::move(conn)); + const auto headers = Http::TestRequestHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":scheme", "http"}, {":authority", "host"}}; + response_ = codec_client_->makeHeaderOnlyRequest(headers); + + AssertionResult result = + fake_upstreams_.back()->waitForHttpConnection(*dispatcher_, fake_ext_authz_connection_); + RELEASE_ASSERT(result, result.message()); + result = fake_ext_authz_connection_->waitForNewStream(*dispatcher_, ext_authz_request_); + RELEASE_ASSERT(result, result.message()); + result = ext_authz_request_->waitForEndStream(*dispatcher_); + RELEASE_ASSERT(result, result.message()); + + EXPECT_EQ(ext_authz_request_->headers().getMethodValue(), "POST"); + + Http::TestResponseHeaderMapImpl authz_response_headers{{":status", "200"}}; + ext_authz_request_->encodeHeaders(authz_response_headers, true); + + AssertionResult upstream_result = + fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_); + RELEASE_ASSERT(upstream_result, upstream_result.message()); + upstream_result = fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_); + RELEASE_ASSERT(upstream_result, upstream_result.message()); + upstream_result = upstream_request_->waitForEndStream(*dispatcher_); + RELEASE_ASSERT(upstream_result, upstream_result.message()); + + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, true); + + ASSERT_TRUE(response_->waitForEndStream()); + EXPECT_TRUE(response_->complete()); + EXPECT_EQ("200", response_->headers().getStatusValue()); + + cleanup(); +} + class ExtAuthzLocalReplyIntegrationTest : public HttpIntegrationTest, public TestWithParam { public: