Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 2 additions & 9 deletions api/envoy/api/v2/lds.proto
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,8 @@ message Listener {
// :ref:`FilterChainMatch <envoy_api_msg_listener.FilterChainMatch>` criteria is used on a
// connection.
//
// .. attention::
//
// In the current version, multiple filter chains are supported **only** so that SNI can be
// configured. See the :ref:`FAQ entry <faq_how_to_setup_sni>` on how to configure SNI for more
// information. When multiple filter chains are configured, each filter chain must have an
// **identical** set of :ref:`filters <envoy_api_field_listener.FilterChain.filters>`. If the
// filters differ, the configuration will fail to load. In the future, this limitation will be
// relaxed such that different filters can be used depending on which filter chain matches
// (based on SNI or some other parameter).
// Example using SNI for filter chain selection can be found in the
// :ref:`FAQ entry <faq_how_to_setup_sni>`.
repeated listener.FilterChain filter_chains = 3
[(validate.rules).repeated .min_items = 1, (gogoproto.nullable) = false];

Expand Down
39 changes: 35 additions & 4 deletions api/envoy/api/v2/listener/listener.proto
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,32 @@ message Filter {

// Specifies the match criteria for selecting a specific filter chain for a
// listener.
//
// In order for a filter chain to be selected, *ALL* of its criteria must be
// fulfilled by the incoming connection, properties of which are set by the
// networking stack and/or listener filters.
//
// The following order applies:
//
// [#comment:TODO(PiotrSikora): destination IP / ranges are going to be 1.]
// 1. Server name (e.g. SNI for TLS protocol),
// 2. Transport protocol.
// [#comment:TODO(PiotrSikora): application protocols are going to be 4.]
//
// For criterias that allow ranges or wildcards, the most specific value in any
// of the configured filter chains that matches the incoming connection is going
// to be used (e.g. for SNI ``www.example.com`` the most specific match would be
// ``www.example.com``, then ``*.example.com``, then any filter chain without
// ``sni_domains`` requirements).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What about when we have CIDR and SNI, what order do we resolve with? In general, it would be helpful to clarify where we want to go in the end state of the API for all the match criteria and precedence.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I've been thinking about this a fair bit. I think the precedence should be configurable. I can think of a few ways that might be useful; I'm not sure yet what the right answer is.

One option is to let the user provide a list for the field precedence order. For instance, [SNI, source-ip, transport protocol]. Another possible matching type is the entire list of matches as an ordered list, with first-match-wins. I'm sure there's other options as well. I would be surprised if we can come up with a one-size-fits-all matching algorithm with this many potential criteria.

I think this can all be solved in a later PR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sure, I can see this being a useful thing to add later. So, for now, as long as we have precedence for the fields that are actually implemented, and a TODO for the rest I'd be happy.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As mentioned earlier (I think?), the destination IP would take precedence over SNI, over transport protocol. I didn't consider source IPs.

I could see having filter_chain_match_precedence: ["server_names", "source_ranges", "transport_protocol"] as a way of controlling precedence, but it doesn't seem that this is something required by most of the use cases, so I'm going to let someone that has an use case for it work on this.

As for the "end goal" docs - I'm under the impression that the API and docs document current state, and not future vision, so I don't think the "end goal" should be added here, especially since things change during implementation, and I wouldn't want to promise stuff that won't be eventually implemented.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't quite correct. The user facing API docs should reflect the current state. We do explicit hidden TODOs in various places to signal to developers and those interested in understanding future directions where we want to head, e.g.

// [#comment:TODO(mattklein123): Block type in non-tcp proxy cases?]
.

Please do add appropriate TODOs in that style and preferably open an issue to track future work here. That's all that's being asked at this point.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added TODOs about few upcoming rules and the precedence we discussed here (I'll open separate issue to track it).

//
// [#comment:TODO(PiotrSikora): Add support for configurable precedence of the rules]
message FilterChainMatch {
// If non-empty, the SNI domain names to consider. May contain a wildcard prefix for
// the bottom-level domain of a domain name, e.g. ``*.example.com``. Note that
// ``foo.example.com`` will be matched by ``foo.example.com`` and ``*.example.com``
// SNI domain names, but **not** by ``*foo.example.com``, ``*oo.example.com``,
// ``*example.com``, ``*.com`` or ``*``.
// the bottom-level domain of a domain name, e.g. ``*.example.com``.
//
// Note that ``foo.example.com`` will be matched by ``foo.example.com``
// and ``*.example.com`` SNI domain names, but **not** by ``*foo.example.com``,
// ``*oo.example.com``, ``*example.com``, ``*.com`` or ``*``.
//
// .. attention::
//
Expand Down Expand Up @@ -90,6 +110,17 @@ message FilterChainMatch {
// listener in determining a filter chain match.
// [#not-implemented-hide:]
google.protobuf.UInt32Value destination_port = 8;

// If non-empty, a transport protocol to consider when determining a filter chain match.
// This value will be compared against the transport protocol of a new connection, when
// it's detected by one of the listener filters (this needs to be configured separately).
//
// Valid values include:
//
// * ``raw_buffer`` - default, used when no transport protocol is detected,
// * ``tls`` - set by :ref:`envoy.listener.tls_inspector <config_listener_filters_tls_inspector>`
// when TLS protocol is detected.
string transport_protocol = 9;
}

// A filter chain wraps a set of match criteria, an option TLS context, a set of filters, and
Expand Down
2 changes: 1 addition & 1 deletion docs/root/configuration/listeners/stats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ Every listener has a statistics tree rooted at *listener.<address>.* with the fo
downstream_cx_destroy, Counter, Total destroyed connections
downstream_cx_active, Gauge, Total active connections
downstream_cx_length_ms, Histogram, Connection length milliseconds
no_filter_chain_match, Counter, Total connections that didn't match any filter chain
ssl.connection_error, Counter, Total TLS connection errors not including failed certificate verifications
ssl.handshake, Counter, Total successful TLS connection handshakes
ssl.session_reused, Counter, Total successful TLS session resumptions
ssl.no_certificate, Counter, Total successul TLS connections with no client certificate
ssl.fail_no_sni_match, Counter, Total TLS connections that were rejected because of missing SNI match
ssl.fail_verify_no_cert, Counter, Total TLS connections that failed because of missing client certificate
ssl.fail_verify_error, Counter, Total TLS connections that failed CA verification
ssl.fail_verify_san, Counter, Total TLS connections that failed SAN verification
Expand Down
7 changes: 0 additions & 7 deletions docs/root/faq/sni.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ How do I setup SNI?
`SNI <https://en.wikipedia.org/wiki/Server_Name_Indication>`_ is only supported in the :ref:`v2
configuration/API <config_overview_v2>`.

The current implementation has the requirement that the :ref:`filters
<envoy_api_field_listener.FilterChain.filters>` in every :ref:`FilterChain <envoy_api_msg_listener.FilterChain>` must
be identical. In a future release, this requirement will be relaxed so that SNI can be used to
choose between completely different filter chains. :ref:`Domain name matching
<envoy_api_field_route.VirtualHost.domains>` can still be used within the HTTP connection manager to
choose different routes. This is by far the most common use case for SNI.

The following is a YAML example of the above requirement.

.. code-block:: yaml
Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Version history
Notably the HTTP response code is always "200" in this case, and the gRPC error code is carried in "grpc-status"
header, optionally accompanied with a text message in "grpc-message" header.
* listeners: added :ref:`tcp_fast_open_queue_length <envoy_api_field_Listener.tcp_fast_open_queue_length>` option.
* listeners: removed restriction on all filter chains having identical filters.
* load balancing: added :ref:`weighted round robin
<arch_overview_load_balancing_types_round_robin>` support. The round robin
scheduler now respects endpoint weights and also has improved fidelity across
Expand Down
3 changes: 2 additions & 1 deletion include/envoy/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ envoy_cc_library(
name = "filter_interface",
hdrs = ["filter.h"],
deps = [
":listen_socket_interface",
":transport_socket_interface",
"//include/envoy/buffer:buffer_interface",
"//include/envoy/upstream:host_description_interface",
],
Expand All @@ -67,7 +69,6 @@ envoy_cc_library(
name = "transport_socket_interface",
hdrs = ["transport_socket.h"],
deps = [
":connection_interface",
"//include/envoy/ssl:connection_interface",
],
)
Expand Down
43 changes: 42 additions & 1 deletion include/envoy/network/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <memory>

#include "envoy/buffer/buffer.h"
#include "envoy/network/listen_socket.h"
#include "envoy/network/transport_socket.h"
#include "envoy/upstream/host_description.h"

namespace Envoy {
Expand Down Expand Up @@ -232,6 +234,43 @@ class ListenerFilterManager {
*/
typedef std::function<void(ListenerFilterManager& filter_manager)> ListenerFilterFactoryCb;

/**
* Interface representing a single filter chain.
*/
class FilterChain {
public:
virtual ~FilterChain() {}

/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You're copying two method from TransportSocketFactory, why don't just have an accessor returns a const TransportSocketFactory&, like Cluster does?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To be honest, I don't see much value in exposing implementation details / member classes, since it makes it harder to make changes in the future.

What would be benefit of returning TransportSocketFactory directly here? Calls to those 2 methods are done in separate places, so it wouldn't save any calls.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It is not exposing implementation details / member classes, the TransportSocketFactory is a pure interface that could be returned here. Just to be more consistent with cluster, also avoid copy and paste same interface around.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fair enough, changed.

* @return const TransportSocketFactory& a transport socket factory to be used by the new
* connection.
*/
virtual const TransportSocketFactory& transportSocketFactory() const PURE;

/**
* const std::vector<FilterFactoryCb>& a list of filters to be used by the new connection.
*/
virtual const std::vector<FilterFactoryCb>& networkFilterFactories() const PURE;
};

typedef std::shared_ptr<FilterChain> FilterChainSharedPtr;

/**
* Interface for searching through configured filter chains.
*/
class FilterChainManager {
public:
virtual ~FilterChainManager() {}

/**
* Find filter chain that's matching metadata from the new connection.
* @param socket supplies connection metadata that's going to be used for the filter chain lookup.
* @return const FilterChain* filter chain to be used by the new connection,
* nullptr if no matching filter chain was found.
*/
virtual const FilterChain* findFilterChain(const ConnectionSocket& socket) const PURE;
};

/**
* Creates a chain of network filters for a new connection.
*/
Expand All @@ -242,10 +281,12 @@ class FilterChainFactory {
/**
* Called to create the network filter chain.
* @param connection supplies the connection to create the chain on.
* @param filter_factories supplies a list of filter factories to create the chain from.
* @return true if filter chain was created successfully. Otherwise
* false, e.g. filter chain is empty.
*/
virtual bool createNetworkFilterChain(Connection& connection) PURE;
virtual bool createNetworkFilterChain(Connection& connection,
const std::vector<FilterFactoryCb>& filter_factories) PURE;

/**
* Called to create the listener filter chain.
Expand Down
11 changes: 6 additions & 5 deletions include/envoy/network/listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ class ListenerConfig {
public:
virtual ~ListenerConfig() {}

/**
* @return FilterChainManager& the factory for adding and searching through configured
* filter chains.
*/
virtual FilterChainManager& filterChainManager() PURE;

/**
* @return FilterChainFactory& the factory for setting up the filter chain on a new
* connection.
Expand All @@ -32,11 +38,6 @@ class ListenerConfig {
*/
virtual Socket& socket() PURE;

/**
* @return TransportSocketFactory& the transport socket factory.
*/
virtual TransportSocketFactory& transportSocketFactory() PURE;

/**
* @return bool specifies whether the listener should actually listen on the port.
* A listener that doesn't listen on a port can only receive connections
Expand Down
4 changes: 3 additions & 1 deletion include/envoy/network/transport_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

#include "envoy/buffer/buffer.h"
#include "envoy/common/pure.h"
#include "envoy/network/connection.h"
#include "envoy/ssl/connection.h"

namespace Envoy {
namespace Network {

class Connection;
enum class ConnectionEvent;

/**
* Action that should occur on a connection after I/O.
*/
Expand Down
13 changes: 3 additions & 10 deletions include/envoy/server/transport_socket_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,8 @@ class DownstreamTransportSocketConfigFactory : public virtual TransportSocketCon
public:
/**
* Create a particular downstream transport socket factory implementation.
* TODO(lizan): Revisit the parameters for SNI below when TLS sniffing and filter chain match are
* implemented.
* @param listener_name const std::string& the name of the listener.
* @param server_names const std::vector<std::string>& the names of the server. This parameter is
* currently used by SNI implementation to know the expected server names.
* @param skip_ssl_context_update bool indicates whether the ssl context update should be skipped.
* This parameter is currently used by SNI implementation to know whether it should perform
* certificate selection.
* @param config const Protobuf::Message& supplies the config message for the transport socket
* implementation.
* @param context TransportSocketFactoryContext& supplies the transport socket's context.
Expand All @@ -95,10 +89,9 @@ class DownstreamTransportSocketConfigFactory : public virtual TransportSocketCon
* parameters.
*/
virtual Network::TransportSocketFactoryPtr
createTransportSocketFactory(const std::string& listener_name,
const std::vector<std::string>& server_names,
bool skip_ssl_context_update, const Protobuf::Message& config,
TransportSocketFactoryContext& context) PURE;
createTransportSocketFactory(const Protobuf::Message& config,
TransportSocketFactoryContext& context,
const std::vector<std::string>& server_names) PURE;
};

} // namespace Configuration
Expand Down
17 changes: 3 additions & 14 deletions include/envoy/ssl/context_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,10 @@ class ContextManager {

/**
* Builds a ServerContext from a ServerContextConfig.
* The skip_context_update parameter is used for fast-path (avoiding lock & context lookup)
* on listeners with a single filter chain and no SNI restrictions.
*/
virtual ServerContextPtr createSslServerContext(const std::string& listener_name,
const std::vector<std::string>& server_names,
Stats::Scope& scope,
const ServerContextConfig& config,
bool skip_context_update) PURE;

/**
* Find ServerContext for a given listener and server_name.
* @return ServerContext or nullptr in case there is no match.
*/
virtual ServerContext* findSslServerContext(const std::string& listener_name,
const std::string& server_name) const PURE;
virtual ServerContextPtr
createSslServerContext(Stats::Scope& scope, const ServerContextConfig& config,
const std::vector<std::string>& server_names) PURE;

/**
* @return the number of days until the next certificate being managed will expire.
Expand Down
1 change: 1 addition & 0 deletions source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ envoy_cc_library(
hdrs = ["raw_buffer_socket.h"],
deps = [
":utility_lib",
"//include/envoy/network:connection_interface",
"//include/envoy/network:transport_socket_interface",
"//source/common/buffer:buffer_lib",
"//source/common/common:empty_string",
Expand Down
1 change: 1 addition & 0 deletions source/common/network/raw_buffer_socket.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "envoy/buffer/buffer.h"
#include "envoy/network/connection.h"
#include "envoy/network/transport_socket.h"

#include "common/common/logger.h"
Expand Down
8 changes: 8 additions & 0 deletions source/common/protobuf/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ class ProtoValidationException : public EnvoyException {

class MessageUtil {
public:
// std::hash
std::size_t operator()(const Protobuf::Message& message) const { return hash(message); }

// std::equals_to
bool operator()(const Protobuf::Message& lhs, const Protobuf::Message& rhs) const {
return Protobuf::util::MessageDifferencer::Equivalent(lhs, rhs);
}

static std::size_t hash(const Protobuf::Message& message) {
// Use Protobuf::io::CodedOutputStream to force deterministic serialization, so that the same
// message doesn't hash to different values.
Expand Down
2 changes: 1 addition & 1 deletion source/common/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ envoy_cc_library(
deps = [
":context_config_lib",
":context_lib",
"//include/envoy/network:connection_interface",
"//include/envoy/network:transport_socket_interface",
"//source/common/common:assert_lib",
"//source/common/common:empty_string",
Expand Down Expand Up @@ -61,7 +62,6 @@ envoy_cc_library(
"//include/envoy/stats:stats_interface",
"//include/envoy/stats:stats_macros",
"//source/common/common:assert_lib",
"//source/common/common:empty_string",
"//source/common/common:hex_lib",
],
)
Loading