Skip to content

Commit

Permalink
router: unified header formatters (envoyproxy#21932)
Browse files Browse the repository at this point in the history
Signed-off-by: Christoph Pakulski <[email protected]>
  • Loading branch information
cpakulski authored Sep 22, 2022
1 parent fa309d1 commit 28e8c5c
Show file tree
Hide file tree
Showing 26 changed files with 609 additions and 337 deletions.
5 changes: 5 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ removed_config_or_runtime:
removed ``envoy.reloadable_features.update_grpc_response_error_tag`` and legacy code paths.
new_features:
- area: header_formatters
change: |
all access log formatters can be used as custom request/response headers. Custom header's syntax is parsed using access logger's parser and
header values are obtained using access log's substitution formatters. This feature can be reversed by setting runtime guard
``envoy.reloadable_features.unified_header_formatter`` to false.
- area: http
change: |
made the :ref:`admission control <envoy_v3_api_msg_extensions.filters.http.admission_control.v3.AdmissionControl>` work as an upstream filter.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 15000
filter_chains:
- name: http
filters:
- name: http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
request_headers_to_add:
- header:
key: "response-code"
value: "%RESPONSE_CODE%"
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: some_service
http_filters:
- name: router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: some_service
load_assignment:
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 10002
273 changes: 27 additions & 246 deletions docs/root/configuration/http/http_conn_man/headers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -574,191 +574,34 @@ used to delimit variable names.
doubling it. For example, to emit a header with the value ``100%``, the custom header value in
the Envoy configuration must be ``100%%``.

Supported variable names are:
All HTTP :ref:`Command Operators <config_access_log_command_operators>` used for access logging may be specified
in custom request/response headers. However, depending where a particular command operator is used, the context needed for
the operator may not be available and the produced output is empty string. For example, the following configuration
uses ``%RESPONSE_CODE%`` operator to modify request headers using code from the response.
The output is an empty string, because request headers are modified
before the request is sent upstream and the response is not received yet.

.. literalinclude:: _include/header_formatters.yaml
:language: yaml
:linenos:
:lines: 15-20
:emphasize-lines: 3-6
:caption: :download:`header_formatters.yaml <_include/header_formatters.yaml>`

%DOWNSTREAM_REMOTE_ADDRESS%
Remote address of the downstream connection. If the address is an IP address it includes both
address and port.

.. note::

This may not be the physical remote address of the peer if the address has been inferred from
:ref:`Proxy Protocol filter <config_listener_filters_proxy_protocol>` or :ref:`x-forwarded-for
<config_http_conn_man_headers_x-forwarded-for>`.
.. attention::

%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%
Remote address of the downstream connection, without any port component.
IP addresses are the only address type with a port component.
The following legacy header formatters are still supported, but will be deprecated in the future.
The equivalent information can be accessed using indicated substitutes.

.. note::
``%DYNAMIC_METADATA(["namespace", "key", ...])%``
Populates the header with dynamic metadata available in a request
(e.g.: added by filters like the header-to-metadata filter).

This may not be the physical remote address of the peer if the address has been inferred from
:ref:`Proxy Protocol filter <config_listener_filters_proxy_protocol>` or :ref:`x-forwarded-for
<config_http_conn_man_headers_x-forwarded-for>`.
This works both on request and response headers.

%DOWNSTREAM_REMOTE_PORT%
Remote port of the downstream connection.
IP addresses are the only address type with a port component.
Use :ref:`%DYNAMIC_METADATA(namespace:key:…):Z%<config_access_log_format_dynamic_metadata>` instead.

.. note::

This may not be the physical remote address of the peer if the address has been inferred from
:ref:`Proxy Protocol filter <config_listener_filters_proxy_protocol>` or :ref:`x-forwarded-for
<config_http_conn_man_headers_x-forwarded-for>`.

%DOWNSTREAM_DIRECT_REMOTE_ADDRESS%
Direct remote address of the downstream connection. If the address is an IP address it includes both
address and port.

.. note::

This is always the physical remote address of the peer even if the downstream remote address has
been inferred from :ref:`Proxy Protocol filter <config_listener_filters_proxy_protocol>`
or :ref:`x-forwarded-for <config_http_conn_man_headers_x-forwarded-for>`.

%DOWNSTREAM_DIRECT_REMOTE_ADDRESS_WITHOUT_PORT%
Direct remote address of the downstream connection, without any port component.
IP addresses are the only address type with a port component.

.. note::

This is always the physical remote address of the peer even if the downstream remote address has
been inferred from :ref:`Proxy Protocol filter <config_listener_filters_proxy_protocol>`
or :ref:`x-forwarded-for <config_http_conn_man_headers_x-forwarded-for>`.

%DOWNSTREAM_DIRECT_REMOTE_PORT%
Direct remote port of the downstream connection.
IP addresses are the only address type with a port component.

.. note::

This is always the physical remote address of the peer even if the downstream remote address has
been inferred from :ref:`Proxy Protocol filter <config_listener_filters_proxy_protocol>`
or :ref:`x-forwarded-for <config_http_conn_man_headers_x-forwarded-for>`.


%DOWNSTREAM_LOCAL_ADDRESS%
Local address of the downstream connection. If the address is an IP address it includes both
address and port.

If the original connection was redirected by iptables REDIRECT, this represents
the original destination address restored by the
:ref:`Original Destination Filter <config_listener_filters_original_dst>` using SO_ORIGINAL_DST socket option.
If the original connection was redirected by iptables TPROXY, and the listener's transparent
option was set to true, this represents the original destination address and port.

%DOWNSTREAM_LOCAL_ADDRESS_WITHOUT_PORT%
Local address of the downstream connection, without any port component.
IP addresses are the only address type with a port component.

%DOWNSTREAM_LOCAL_PORT%
Local port of the downstream connection.
IP addresses are the only address type with a port component.

%DOWNSTREAM_LOCAL_URI_SAN%
HTTP
The URIs present in the SAN of the local certificate used to establish the downstream TLS connection.
TCP
The URIs present in the SAN of the local certificate used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_URI_SAN%
HTTP
The URIs present in the SAN of the peer certificate used to establish the downstream TLS connection.
TCP
The URIs present in the SAN of the peer certificate used to establish the downstream TLS connection.

%DOWNSTREAM_LOCAL_SUBJECT%
HTTP
The subject present in the local certificate used to establish the downstream TLS connection.
TCP
The subject present in the local certificate used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_SUBJECT%
HTTP
The subject present in the peer certificate used to establish the downstream TLS connection.
TCP
The subject present in the peer certificate used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_ISSUER%
HTTP
The issuer present in the peer certificate used to establish the downstream TLS connection.
TCP
The issuer present in the peer certificate used to establish the downstream TLS connection.

%DOWNSTREAM_TLS_SESSION_ID%
HTTP
The session ID for the established downstream TLS connection.
TCP
The session ID for the established downstream TLS connection.

%DOWNSTREAM_TLS_CIPHER%
HTTP
The OpenSSL name for the set of ciphers used to establish the downstream TLS connection.
TCP
The OpenSSL name for the set of ciphers used to establish the downstream TLS connection.

%DOWNSTREAM_TLS_VERSION%
HTTP
The TLS version (e.g., ``TLSv1.2``, ``TLSv1.3``) used to establish the downstream TLS connection.
TCP
The TLS version (e.g., ``TLSv1.2``, ``TLSv1.3``) used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_FINGERPRINT_256%
HTTP
The hex-encoded SHA256 fingerprint of the client certificate used to establish the downstream TLS connection.
TCP
The hex-encoded SHA256 fingerprint of the client certificate used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_FINGERPRINT_1%
HTTP
The hex-encoded SHA1 fingerprint of the client certificate used to establish the downstream TLS connection.
TCP
The hex-encoded SHA1 fingerprint of the client certificate used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_SERIAL%
HTTP
The serial number of the client certificate used to establish the downstream TLS connection.
TCP
The serial number of the client certificate used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_CERT%
HTTP
The client certificate in the URL-encoded PEM format used to establish the downstream TLS connection.
TCP
The client certificate in the URL-encoded PEM format used to establish the downstream TLS connection.

%DOWNSTREAM_PEER_CERT_V_START%
HTTP
The validity start date of the client certificate used to establish the downstream TLS connection.
TCP
The validity start date of the client certificate used to establish the downstream TLS connection.

DOWNSTREAM_PEER_CERT_V_START can be customized with specifiers as specified in
:ref:`access log format rules<config_access_log_format_downstream_peer_cert_v_start>`.

%DOWNSTREAM_PEER_CERT_V_END%
HTTP
The validity end date of the client certificate used to establish the downstream TLS connection.
TCP
The validity end date of the client certificate used to establish the downstream TLS connection.

DOWNSTREAM_PEER_CERT_V_END can be customized with specifiers as specified in
:ref:`access log format rules<config_access_log_format_downstream_peer_cert_v_end>`.

%HOSTNAME%
The system hostname.

%PROTOCOL%
The original protocol which is already added by Envoy as a
:ref:`x-forwarded-proto <config_http_conn_man_headers_x-forwarded-proto>` request header.

%REQUESTED_SERVER_NAME%
HTTP
String value set on ssl connection socket for Server Name Indication (SNI)
TCP
String value set on ssl connection socket for Server Name Indication (SNI)

%UPSTREAM_METADATA(["namespace", "key", ...])%
``%UPSTREAM_METADATA(["namespace", "key", ...])%``
Populates the header with :ref:`EDS endpoint metadata <envoy_v3_api_field_config.endpoint.v3.LbEndpoint.metadata>` from the
upstream host selected by the router. Metadata may be selected from any namespace. In general,
metadata values may be strings, numbers, booleans, lists, nested structures, or null. Upstream
Expand All @@ -771,74 +614,12 @@ Supported variable names are:
Upstream metadata cannot be added to request headers as the upstream host has not been selected
when custom request headers are generated.

%DYNAMIC_METADATA(["namespace", "key", ...])%
Similar to UPSTREAM_METADATA, populates the header with dynamic metadata available in a request
(e.g.: added by filters like the header-to-metadata filter).

This works both on request and response headers.

%UPSTREAM_LOCAL_ADDRESS%
Local address of the upstream connection. If the address is an IP address it includes both
address and port.

The upstream local address cannot be added to request headers as the upstream host
hremote as not been selected when custom request headers are generated.

%UPSTREAM_LOCAL_ADDRESS_WITHOUT_PORT%
Local address of the upstream connection, without any port component.
IP addresses are the only address type with a port component.

%UPSTREAM_LOCAL_PORT%
Local port of the upstream connection.
IP addresses are the only address type with a port component.
Use :ref:`%UPSTREAM_METADATA(namespace:key:…):Z%<config_access_log_format_upstream_host_metadata>` instead.

%UPSTREAM_REMOTE_ADDRESS%
Remote address of the upstream connection. If the address is an IP address it includes both
address and port.

The upstream remote address cannot be added to request headers as the upstream host
has not been selected when custom request headers are generated.

%UPSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%
Remote address of the upstream connection, without any port component.
IP addresses are the only address type with a port component.

%UPSTREAM_REMOTE_PORT%
Remote port of the upstream connection.
IP addresses are the only address type with a port component.

%PER_REQUEST_STATE(reverse.dns.data.name)%
Populates the header with values set on the stream info filterState() object. To be
``%PER_REQUEST_STATE(reverse.dns.data.name)%``
Populates the header with values set on the stream info ``filterState()`` object. To be
usable in custom request/response headers, these values must be of type
Envoy::Router::StringAccessor. These values should be named in standard reverse DNS style,
``Envoy::Router::StringAccessor``. These values should be named in standard reverse DNS style,
identifying the organization that created the value and ending in a unique name for the data.

%REQ(header-name)%
Populates the header with a value of the request header.

%START_TIME%
Request start time. START_TIME can be customized with specifiers as specified in
:ref:`access log format rules<config_access_log_format_start_time>`.

An example of setting a custom header with current time in seconds with the milliseconds resolution:

.. code-block:: none
route:
cluster: www
request_headers_to_add:
- header:
key: "x-request-start"
value: "%START_TIME(%s.%3f)%"
append_action: APPEND_IF_EXISTS_OR_ADD
%RESPONSE_FLAGS%
Additional details about the response or connection, if any. Possible values and their meanings
are listed in the access log formatter :ref:`documentation<config_access_log_format_response_flags>`.

%RESPONSE_CODE_DETAILS%
Response code details provides additional information about the HTTP response code, such as
who set it (the upstream or envoy) and why.

%VIRTUAL_CLUSTER_NAME%
Name of the Virtual Cluster which gets matched (if any).
Use :ref:`%FILTER_STATE(reverse.dns.data.name:PLAIN):Z%<config_access_log_format_filter_state>` instead.
6 changes: 5 additions & 1 deletion envoy/http/header_evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ class HeaderEvaluator {
/**
* Apply the header operations that are saved in the HeaderEvaluator. An example of the operation
* is to add a new header name `foo` to the target header map and the header value is extracted
* from the `bar` field in the stream_info.
* from the `bar` field in the stream_info, request headers or response headers.
*
* @param headers the target header map to be mutated.
* @param request_headers request headers to be used in the header manipulation.
* @param response_headers response headers to be used in the header manipulation.
* @param stream_info the source of values that can be used in the evaluation.
*/
virtual void evaluateHeaders(Http::HeaderMap& headers,
const Http::RequestHeaderMap& request_headers,
const Http::ResponseHeaderMap& response_headers,
const StreamInfo::StreamInfo& stream_info) const PURE;
};
} // namespace Http
Expand Down
5 changes: 5 additions & 0 deletions source/common/grpc/async_client_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ void AsyncStreamImpl::initialize(bool buffer_body_for_retry) {
parent_.host_name_.empty() ? parent_.remote_cluster_name_ : parent_.host_name_,
service_full_name_, method_name_, options_.timeout);
// Fill service-wide initial metadata.
// TODO(cpakulski): Find a better way to access requestHeaders after runtime guard
// envoy_reloadable_features_unified_header_formatter runtime guard is deprecated and
// request headers are not stored in stream_info.
// Maybe put it to parent_context?
// Since request headers may be empty, consider using Envoy::OptRef.
parent_.metadata_parser_->evaluateHeaders(headers_message_->headers(),
options_.parent_context.stream_info);

Expand Down
4 changes: 4 additions & 0 deletions source/common/grpc/google_async_client_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ void GoogleAsyncStreamImpl::initialize(bool /*buffer_body_for_retry*/) {
ctxt_.set_deadline(abs_deadline);
// Fill service-wide initial metadata.
auto initial_metadata = Http::RequestHeaderMapImpl::create();
// TODO(cpakulski): Find a better way to access requestHeaders after runtime guard
// envoy_reloadable_features_unified_header_formatter runtime guard is deprecated
// and request headers are not stored in stream_info.
// Maybe put it to parent_context?
parent_.metadata_parser_->evaluateHeaders(*initial_metadata, options_.parent_context.stream_info);
callbacks_.onCreateInitialMetadata(*initial_metadata);
initial_metadata->iterate([this](const Http::HeaderEntry& header) {
Expand Down
Loading

0 comments on commit 28e8c5c

Please sign in to comment.