Skip to content
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

listeners: add unified matcher for filter chains #20110

Merged
merged 58 commits into from
Apr 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
6a37223
[wip] Add filter chain match predicate order
kyessenov Nov 2, 2021
8aa165f
spelling
kyessenov Nov 2, 2021
cfdb93a
review
kyessenov Nov 5, 2021
a13ed92
review
kyessenov Nov 5, 2021
621fcbf
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Nov 8, 2021
2228fef
review
kyessenov Nov 8, 2021
b610420
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Nov 12, 2021
f45bc06
review
kyessenov Nov 15, 2021
bada313
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Nov 15, 2021
3f8f8e4
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Nov 17, 2021
937f7a8
review
kyessenov Nov 17, 2021
9fd0f34
review
kyessenov Nov 17, 2021
595eb18
add move note
kyessenov Nov 22, 2021
7d7909a
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Nov 22, 2021
9a56f14
merge fix
kyessenov Nov 29, 2021
262d084
merge
kyessenov Nov 29, 2021
76e5040
more review
kyessenov Dec 1, 2021
3b92ec5
typo
kyessenov Dec 1, 2021
03fbfc6
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Dec 15, 2021
ddeeaf9
update
kyessenov Dec 15, 2021
be4636d
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Jan 7, 2022
72b978e
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Feb 1, 2022
e981df6
review
kyessenov Feb 1, 2022
a9b056a
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Feb 2, 2022
987a2fd
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Feb 17, 2022
0185225
try validation
kyessenov Feb 18, 2022
d1c8f75
verify example
kyessenov Feb 23, 2022
a4dcf21
initial implementation
kyessenov Feb 23, 2022
2cd4931
changes
kyessenov Feb 24, 2022
e896186
fix doc
kyessenov Feb 24, 2022
d5feb5f
increase coverage
kyessenov Feb 25, 2022
d0f78a4
fix quic test
kyessenov Feb 25, 2022
0831118
review
kyessenov Feb 28, 2022
559341f
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Feb 28, 2022
bf77d77
review
kyessenov Feb 28, 2022
2451c0a
fix order
kyessenov Mar 1, 2022
7b2bbe6
docs
kyessenov Mar 1, 2022
15ae545
docs
kyessenov Mar 1, 2022
65143c5
typo
kyessenov Mar 3, 2022
98619c8
merge fix
kyessenov Mar 7, 2022
d661a00
fix test
kyessenov Mar 7, 2022
2e640a2
enable more tests
kyessenov Mar 7, 2022
9bac339
enable more tests
kyessenov Mar 7, 2022
98e15d8
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Mar 8, 2022
2d24544
docs
kyessenov Mar 8, 2022
5a57b4d
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Mar 14, 2022
eb3da3c
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Mar 24, 2022
2015ccf
code fixes
kyessenov Mar 24, 2022
5ab50c6
docs
kyessenov Mar 25, 2022
efbe36a
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Mar 25, 2022
b32362e
update docs
kyessenov Mar 25, 2022
45a8482
download button
kyessenov Mar 25, 2022
7b97140
fix windows example
kyessenov Mar 25, 2022
468d702
merge fix
kyessenov Apr 11, 2022
e436cdf
merge fix
kyessenov Apr 11, 2022
74c2f58
Merge remote-tracking branch 'upstream/main' into extensible_filter_c…
kyessenov Apr 11, 2022
c611e2d
review
kyessenov Apr 11, 2022
8ac6a1d
fix logger
kyessenov Apr 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/envoy/config/listener/v3/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ api_proto_package(
"//envoy/config/core/v3:pkg",
"//envoy/type/v3:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
"@com_github_cncf_udpa//xds/annotations/v3:pkg",
"@com_github_cncf_udpa//xds/core/v3:pkg",
"@com_github_cncf_udpa//xds/type/matcher/v3:pkg",
],
)
23 changes: 22 additions & 1 deletion api/envoy/config/listener/v3/listener.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import "envoy/config/listener/v3/udp_listener_config.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";

import "xds/annotations/v3/status.proto";
import "xds/core/v3/collection_entry.proto";
import "xds/type/matcher/v3/matcher.proto";

import "envoy/annotations/deprecation.proto";
import "udpa/annotations/security.proto";
Expand All @@ -36,7 +38,7 @@ message ListenerCollection {
repeated xds.core.v3.CollectionEntry entries = 1;
}

// [#next-free-field: 32]
// [#next-free-field: 33]
message Listener {
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.Listener";

Expand Down Expand Up @@ -120,6 +122,25 @@ message Listener {
// :ref:`FAQ entry <faq_how_to_setup_sni>`.
repeated FilterChain filter_chains = 3;

// :ref:`Matcher API <arch_overview_matching_listener>` resolving the filter chain name from the
// network properties. This matcher is used as a replacement for the filter chain match condition
// :ref:`filter_chain_match
// <envoy_v3_api_field_config.listener.v3.FilterChain.filter_chain_match>`. If specified, all
// :ref:`filter_chains <envoy_v3_api_field_config.listener.v3.Listener.filter_chains>` must have a
Comment on lines +126 to +129
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if both are defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filter chain match is ignored when listener matcher is defined. I added a debug log to warn on listener construction.

// non-empty and unique :ref:`name <envoy_v3_api_field_config.listener.v3.FilterChain.name>` field
// and not specify :ref:`filter_chain_match
// <envoy_v3_api_field_config.listener.v3.FilterChain.filter_chain_match>` field.
//
// .. note::
//
// Once matched, each connection is permanently bound to its filter chain.
// If the matcher changes but the filter chain remains the same, the
// connections bound to the filter chain are not drained. If, however, the
// filter chain is removed or structurally modified, then the drain for its
// connections is initiated.
xds.type.matcher.v3.Matcher filter_chain_matcher = 32
[(xds.annotations.v3.field_status).work_in_progress = true];

// If a connection is redirected using *iptables*, the port on which the proxy
// receives it might be different from the original destination address. When this flag is set to
// true, the listener hands off redirected connections to the listener associated with the
Expand Down
11 changes: 7 additions & 4 deletions api/envoy/config/listener/v3/listener_components.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import "google/protobuf/any.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/wrappers.proto";

import "xds/annotations/v3/status.proto";

import "envoy/annotations/deprecation.proto";
import "udpa/annotations/status.proto";
import "udpa/annotations/versioning.proto";
Expand Down Expand Up @@ -258,10 +260,11 @@ message FilterChain {
// establishment, the connection is summarily closed.
google.protobuf.Duration transport_socket_connect_timeout = 9;

// [#not-implemented-hide:] The unique name (or empty) by which this filter chain is known. If no
// name is provided, Envoy will allocate an internal UUID for the filter chain. If the filter
// chain is to be dynamically updated or removed via FCDS a unique name must be provided.
string name = 7;
// The unique name (or empty) by which this filter chain is known.
// Note: :ref:`filter_chain_matcher
// <envoy_v3_api_field_config.listener.v3.Listener.filter_chain_matcher>`
// requires that filter chains are uniquely named within a listener.
string name = 7 [(xds.annotations.v3.field_status).work_in_progress = true];

// [#not-implemented-hide:] The configuration to specify whether the filter chain will be built on-demand.
// If this field is not empty, the filter chain will be built on-demand.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
static_resources:
listeners:
- name: outbound
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 15000
listener_filters:
- name: original_dst
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.listener.original_dst.v3.OriginalDst
traffic_direction: OUTBOUND
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
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
- name: internal
filters:
- name: tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: internal
cluster: some_service
- name: tls
transport_socket:
name: tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain: {filename: "certs/servercert.pem"}
private_key: {filename: "certs/serverkey.pem"}
filters:
- name: tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: tls
cluster: some_service
# Snippet: 58-102
filter_chain_matcher:
matcher_tree:
input:
name: port
typed_config:
"@type": type.googleapis.com/envoy.extensions.matching.common_inputs.network.v3.DestinationPortInput
exact_match_map:
map:
"80":
action:
name: http
typed_config:
"@type": type.googleapis.com/google.protobuf.StringValue
value: http
"443":
matcher:
matcher_tree:
input:
name: ip
typed_config:
"@type": type.googleapis.com/envoy.extensions.matching.common_inputs.network.v3.SourceIPInput
htuch marked this conversation as resolved.
Show resolved Hide resolved
custom_match:
name: ip-matcher
typed_config:
"@type": type.googleapis.com/xds.type.matcher.v3.IPMatcher
range_matchers:
- ranges:
- address_prefix: 192.0.0.0
prefix_len: 2
- address_prefix: 10.0.0.0
prefix_len: 24
on_match:
action:
name: internal
typed_config:
"@type": type.googleapis.com/google.protobuf.StringValue
value: internal
- ranges:
- address_prefix: 0.0.0.0
on_match:
action:
name: tls
typed_config:
"@type": type.googleapis.com/google.protobuf.StringValue
value: tls

clusters:
- name: some_service
load_assignment:
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.1.2.10
port_value: 10002
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
static_resources:
listeners:
- name: outbound
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 8443
listener_filters:
- name: tls_inspector
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
filter_chains:
- name: tls
transport_socket:
name: tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain: {filename: "certs/servercert.pem"}
private_key: {filename: "certs/serverkey.pem"}
filters:
- name: tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: tls
cluster: some_service
- name: plaintext
filters:
- name: tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: plaintext
cluster: some_service
# Snippet: 37-56
filter_chain_matcher:
matcher_tree:
input:
name: transport
typed_config:
"@type": type.googleapis.com/envoy.extensions.matching.common_inputs.network.v3.TransportProtocolInput
exact_match_map:
map:
"tls":
action:
name: tls
typed_config:
"@type": type.googleapis.com/google.protobuf.StringValue
value: tls
Comment on lines +46 to +50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I am missing some context, but could this be simplified to just have the name? it seems redundant to have the same info in two places and I worry if this may impact XDS sizes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a proposal cncf/xds#28.

on_no_match:
action:
name: plaintext
typed_config:
"@type": type.googleapis.com/google.protobuf.StringValue
value: plaintext

clusters:
- name: some_service
load_assignment:
cluster_name: some_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.1.2.10
port_value: 10002
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
static_resources:
listeners:
- name: outbound
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 15000
listener_filters:
- name: proxy_protocol
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.listener.proxy_protocol.v3.ProxyProtocol
filter_chains:
- name: vip
filters:
- name: tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: vip
cluster: original_dst
- name: default
filters:
- name: tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: default
cluster: original_dst
# Snippet: 29-48
filter_chain_matcher:
matcher_tree:
input:
name: destination_ip
typed_config:
"@type": type.googleapis.com/envoy.extensions.matching.common_inputs.network.v3.DestinationIPInput
prefix_match_map:
map:
"10.0.0.":
action:
name: vip
typed_config:
"@type": type.googleapis.com/google.protobuf.StringValue
value: vip
on_no_match:
action:
name: default
typed_config:
"@type": type.googleapis.com/google.protobuf.StringValue
value: default

clusters:
- name: original_dst
type: ORIGINAL_DST
lb_policy: CLUSTER_PROVIDED
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Generic Matching
:maxdepth: 2

matching_api
matching_listener
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.. _arch_overview_matching_listener:

Matching Filter Chains in Listeners
===================================

Envoy listeners implement the :ref:`matching API <envoy_v3_api_msg_.xds.type.matcher.v3.Matcher>` for selecting a filter
chain based on a collection of :ref:`network inputs <extension_category_envoy.matching.network.input>`. Matching is done
once per connection. Connections are drained when the associated named filter chain configuration changes, but not when
the filter chain matcher is the only updated field in a listener.

The action in the matcher API must be a string value corresponding to the name of the filter chain. If there is no
filter chain with the given name, the match fails, and the :ref:`default filter chain
<envoy_v3_api_field_config.listener.v3.Listener.default_filter_chain>` is used if specified, or the connection is
rejected. Filter chain matcher requires that all filter chains in a listener are uniquely named.

The matcher API replaces the existing filter :ref:`filter_chain_match
<envoy_v3_api_field_config.listener.v3.FilterChain.filter_chain_match>` field. When using the matcher API, the filter
chain match field is ignored and should not be set.
Comment on lines +16 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be exposed to the user in some way? ie fail the config or at the very least log something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a debug log. Failing might complicate migration IMHO, so just ignoring the field seems reasonable as it is an opt-in feature.


Examples
########

Detect TLS traffic
******************

The following examples uses :ref:`tls_inspector <config_listener_filters_tls_inspector>` listener filter to detect
whether the transport appears to be TLS, in which case the matcher in the listener selects the filter chain ``tls``.
Otherwise, the filter chain ``plaintext`` is used.

.. literalinclude:: _include/listener_tls.yaml
:language: yaml
:lines: 37-56
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we are going to include linenos and the sample doesnt start from the first line, then i think we also need to set lineno-start to the first line

it would also be good to include a caption so users can get the entire file:

:caption: :download:`listener_tls.yaml <_include/listener_tls.yaml>`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I removed the line numbers, don't seem essential.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think they are a helpful guide to the user - both visually that there is a full file that the snippet is taken from and for quickly finding the snippet in the config

i wont block on it tho as usage is inconsistent elsewhere

:caption: :download:`listener_tls.yaml <_include/listener_tls.yaml>`

Match Against the Destination IP
********************************

The following example assumes :ref:`PROXY protocol <config_listener_filters_proxy_protocol>` is used for incoming
traffic. If the recovered destination IP is in CIDR ``10.0.0.0/24``, then the filter chain ``vip`` is used. Otherwise,
the filter chain ``default`` is used.

.. literalinclude:: _include/listener_vip.yaml
:language: yaml
:lines: 29-48
:caption: :download:`listener_vip.yaml <_include/listener_vip.yaml>`

Match Against the Destination Port and the Source IP
****************************************************

The following example uses :ref:`original_dst <config_listener_filters_original_dst>` listener filter to recover the
original destination port. The matcher in the listener selects one of the three filter chains ``http``, ``internal``,
and ``tls`` as follows:

* If the destination port is ``80``, then the filter chain ``http`` accepts the connection.
* If the destination port is ``443`` and the source IP is in the range ``192.0.0.0/2`` or ``10.0.0.0/24``, then the
filter chain ``internal`` accepts the connection. If the source IP is not in the ranges then the filter chain ``tls``
accepts the connection.
* Otherwise, the connection is rejected, because there is no default filter chain.

.. literalinclude:: _include/listener_complicated.yaml
:language: yaml
:lines: 58-102
:caption: :download:`listener_complicated.yaml <_include/listener_complicated.yaml>`
Loading