Skip to content

Commit a637506

Browse files
rshriramlizan
authored andcommitted
Extensions: Network filter to set upstream cluster from SNI (#4489)
*Description*: Use the SNI value as the upstream cluster name. This is similar to the cluster_header feature in HCM. Leverages the perConnectionState to dynamically control the cluster used by tcp_proxy filter. We plan to use this in Istio, where Pilot would manage two kubernetes setups, such that the envoys will have the same set of clusters, but the non-local clusters will have the IP of a Gateway envoy (edge/front envoy). mTLS traffic arriving at the gateway envoy will be routed to the internal (envoy)clusters based on the SNI value Depends on envoyproxy/envoy#4454 *Risk Level*: Low *Testing*: Unit tests *Docs Changes*: yes *Release Notes*: yes Signed-off-by: Shriram Rajagopalan <[email protected]>
1 parent 440076f commit a637506

File tree

18 files changed

+346
-0
lines changed

18 files changed

+346
-0
lines changed

CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@
1010
/*/extensions/filters/http/header_to_metadata @rgs1 @zuercher
1111
# alts transport socket extension
1212
/*/extensions/transport_sockets/alts @lizan @yangminzhu
13+
# sni_cluster extension
14+
/*/extensions/filters/network/sni_cluster @rshriram @lizan

docs/root/configuration/network_filters/network_filters.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ filters.
1919
redis_proxy_filter
2020
tcp_proxy_filter
2121
thrift_proxy_filter
22+
sni_cluster_filter
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.. _config_network_filters_sni_cluster:
2+
3+
Upstream Cluster from SNI
4+
=========================
5+
6+
The `sni_cluster` is a network filter that uses the SNI value in a TLS
7+
connection as the upstream cluster name. The filter will not modify the
8+
upstream cluster for non-TLS connections.
9+
10+
This filter has no configuration. It must be installed before the
11+
:ref:`tcp_proxy <config_network_filters_tcp_proxy>` filter.
12+
13+
* :ref:`v2 API reference <envoy_api_field_listener.Filter.name>`

docs/root/configuration/network_filters/tcp_proxy_filter.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ TCP proxy
77
* :ref:`v1 API reference <config_network_filters_tcp_proxy_v1>`
88
* :ref:`v2 API reference <envoy_api_msg_config.filter.network.tcp_proxy.v2.TcpProxy>`
99

10+
.. _config_network_filters_tcp_proxy_dynamic_cluster:
11+
12+
Dynamic cluster selection
13+
-------------------------
14+
15+
The upstream cluster used by the TCP proxy filter can be dynamically set by
16+
other network filters on a per-connection basis by setting a per-connection
17+
state object under the key `envoy.tcp_proxy.cluster`. See the
18+
implementation for the details.
19+
1020
.. _config_network_filters_tcp_proxy_stats:
1121

1222
Statistics

docs/root/intro/version_history.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ Version history
9494
* config: Fixed stat inconsistency between xDS and ADS implementation. :ref:`update_failure <config_cluster_manager_cds>`
9595
stat is incremented in case of network failure and :ref:`update_rejected <config_cluster_manager_cds>` stat is incremented
9696
in case of schema/validation error.
97+
* :ref:`sni_cluster <config_network_filters_sni_cluster>`: introduced a new network filter that forwards connections to the
98+
upstream cluster specified by the SNI value presented by the client during a TLS handshake.
9799
* config: Added a stat :ref:`connected_state <management_server_stats>` that indicates current connected state of Envoy with
98100
management server.
99101

source/common/tcp_proxy/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ envoy_cc_library(
1919
"//include/envoy/event:dispatcher_interface",
2020
"//include/envoy/network:connection_interface",
2121
"//include/envoy/network:filter_interface",
22+
"//include/envoy/request_info:filter_state_interface",
2223
"//include/envoy/router:router_interface",
2324
"//include/envoy/server:filter_config_interface",
2425
"//include/envoy/stats:stats_interface",

source/common/tcp_proxy/tcp_proxy.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
namespace Envoy {
2323
namespace TcpProxy {
2424

25+
const std::string PerConnectionCluster::Key = "envoy.tcp_proxy.cluster";
26+
2527
Config::Route::Route(
2628
const envoy::config::filter::network::tcp_proxy::v2::TcpProxy::DeprecatedV1::TCPRoute& config) {
2729
cluster_name_ = config.cluster();
@@ -108,6 +110,13 @@ Config::Config(const envoy::config::filter::network::tcp_proxy::v2::TcpProxy& co
108110
}
109111

110112
const std::string& Config::getRegularRouteFromEntries(Network::Connection& connection) {
113+
// First check if the per-connection state to see if we need to route to a pre-selected cluster
114+
if (connection.perConnectionState().hasData<PerConnectionCluster>(PerConnectionCluster::Key)) {
115+
const PerConnectionCluster& per_connection_cluster =
116+
connection.perConnectionState().getData<PerConnectionCluster>(PerConnectionCluster::Key);
117+
return per_connection_cluster.value();
118+
}
119+
111120
for (const Config::Route& route : routes_) {
112121
if (!route.source_port_ranges_.empty() &&
113122
!Network::Utility::portInRangeList(*connection.remoteAddress(),

source/common/tcp_proxy/tcp_proxy.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "envoy/event/timer.h"
1212
#include "envoy/network/connection.h"
1313
#include "envoy/network/filter.h"
14+
#include "envoy/request_info/filter_state.h"
1415
#include "envoy/runtime/runtime.h"
1516
#include "envoy/server/filter_config.h"
1617
#include "envoy/stats/scope.h"
@@ -154,6 +155,19 @@ class Config {
154155

155156
typedef std::shared_ptr<Config> ConfigSharedPtr;
156157

158+
/**
159+
* Per-connection TCP Proxy Cluster configuration.
160+
*/
161+
class PerConnectionCluster : public RequestInfo::FilterState::Object {
162+
public:
163+
PerConnectionCluster(absl::string_view cluster) : cluster_(cluster) {}
164+
const std::string& value() const { return cluster_; }
165+
static const std::string Key;
166+
167+
private:
168+
const std::string cluster_;
169+
};
170+
157171
/**
158172
* An implementation of a TCP (L3/L4) proxy. This filter will instantiate a new outgoing TCP
159173
* connection using the defined load balancing proxy for the configured cluster. All data will

source/extensions/extensions_build_config.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ EXTENSIONS = {
7070
"envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config",
7171
"envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config",
7272
"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config",
73+
"envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config",
7374

7475
#
7576
# Resource monitors
@@ -181,6 +182,7 @@ WINDOWS_EXTENSIONS = {
181182
#"envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config",
182183
"envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config",
183184
#"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config",
185+
#"envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config",
184186

185187
#
186188
# Stat sinks
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
licenses(["notice"]) # Apache 2
2+
3+
load(
4+
"//bazel:envoy_build_system.bzl",
5+
"envoy_cc_library",
6+
"envoy_package",
7+
)
8+
9+
envoy_package()
10+
11+
envoy_cc_library(
12+
name = "sni_cluster",
13+
srcs = ["sni_cluster.cc"],
14+
hdrs = ["sni_cluster.h"],
15+
deps = [
16+
"//include/envoy/network:connection_interface",
17+
"//include/envoy/network:filter_interface",
18+
"//source/common/common:assert_lib",
19+
"//source/common/common:minimal_logger_lib",
20+
"//source/common/tcp_proxy",
21+
],
22+
)
23+
24+
envoy_cc_library(
25+
name = "config",
26+
srcs = ["config.cc"],
27+
hdrs = ["config.h"],
28+
deps = [
29+
":sni_cluster",
30+
"//include/envoy/registry",
31+
"//include/envoy/server:filter_config_interface",
32+
"//source/extensions/filters/network:well_known_names",
33+
],
34+
)

0 commit comments

Comments
 (0)