diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index b62d36dd071b..517f6263565e 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -38,15 +38,22 @@ namespace Envoy { namespace Server { namespace { +bool anyFilterChain( + const envoy::config::listener::v3::Listener& config, + std::function predicate) { + return (config.has_default_filter_chain() && predicate(config.default_filter_chain())) || + std::any_of(config.filter_chains().begin(), config.filter_chains().end(), predicate); +} + bool needTlsInspector(const envoy::config::listener::v3::Listener& config) { - return std::any_of(config.filter_chains().begin(), config.filter_chains().end(), - [](const auto& filter_chain) { - const auto& matcher = filter_chain.filter_chain_match(); - return matcher.transport_protocol() == "tls" || - (matcher.transport_protocol().empty() && - (!matcher.server_names().empty() || - !matcher.application_protocols().empty())); - }) && + return anyFilterChain(config, + [](const auto& filter_chain) { + const auto& matcher = filter_chain.filter_chain_match(); + return matcher.transport_protocol() == "tls" || + (matcher.transport_protocol().empty() && + (!matcher.server_names().empty() || + !matcher.application_protocols().empty())); + }) && !std::any_of( config.listener_filters().begin(), config.listener_filters().end(), [](const auto& filter) { @@ -55,6 +62,14 @@ bool needTlsInspector(const envoy::config::listener::v3::Listener& config) { filter.name() == "envoy.listener.tls_inspector"; }); } + +bool usesProxyProto(const envoy::config::listener::v3::Listener& config) { + // TODO(#14085): `use_proxy_proto` should be deprecated. + // Checking only the first or default filter chain is done for backwards compatibility. + return PROTOBUF_GET_WRAPPED_OR_DEFAULT( + config.filter_chains().empty() ? config.default_filter_chain() : config.filter_chains()[0], + use_proxy_proto, false); +} } // namespace ListenSocketFactoryImpl::ListenSocketFactoryImpl(ListenerComponentFactory& factory, @@ -458,21 +473,22 @@ void ListenerImpl::createListenerFilterFactories(Network::Socket::Type socket_ty } void ListenerImpl::validateFilterChains(Network::Socket::Type socket_type) { - if (config_.filter_chains().empty() && (socket_type == Network::Socket::Type::Stream || - !udp_listener_factory_->isTransportConnectionless())) { + if (config_.filter_chains().empty() && !config_.has_default_filter_chain() && + (socket_type == Network::Socket::Type::Stream || + !udp_listener_factory_->isTransportConnectionless())) { // If we got here, this is a tcp listener or connection-oriented udp listener, so ensure there // is a filter chain specified throw EnvoyException(fmt::format("error adding listener '{}': no filter chains specified", address_->asString())); } else if (udp_listener_factory_ != nullptr && !udp_listener_factory_->isTransportConnectionless()) { - for (auto& filter_chain : config_.filter_chains()) { - // Early fail if any filter chain doesn't have transport socket configured. - if (!filter_chain.has_transport_socket()) { - throw EnvoyException(fmt::format("error adding listener '{}': no transport socket " - "specified for connection oriented UDP listener", - address_->asString())); - } + // Early fail if any filter chain doesn't have transport socket configured. + if (anyFilterChain(config_, [](const auto& filter_chain) { + return !filter_chain.has_transport_socket(); + })) { + throw EnvoyException(fmt::format("error adding listener '{}': no transport socket " + "specified for connection oriented UDP listener", + address_->asString())); } } } @@ -528,7 +544,7 @@ void ListenerImpl::buildProxyProtocolListenerFilter() { // TODO(jrajahalme): This is the last listener filter on purpose. When filter chain matching // is implemented, this needs to be run after the filter chain has been // selected. - if (PROTOBUF_GET_WRAPPED_OR_DEFAULT(config_.filter_chains()[0], use_proxy_proto, false)) { + if (usesProxyProto(config_)) { auto& factory = Config::Utility::getAndCheckFactoryByName( Extensions::ListenerFilters::ListenerFilterNames::get().ProxyProtocol); @@ -712,10 +728,8 @@ bool ListenerImpl::supportUpdateFilterChain(const envoy::config::listener::v3::L return false; } - // See buildProxyProtocolListenerFilter(). Full listener update guarantees at least 1 filter chain - // at tcp listener. - if (PROTOBUF_GET_WRAPPED_OR_DEFAULT(config_.filter_chains()[0], use_proxy_proto, false) ^ - PROTOBUF_GET_WRAPPED_OR_DEFAULT(config.filter_chains()[0], use_proxy_proto, false)) { + // See buildProxyProtocolListenerFilter(). + if (usesProxyProto(config_) ^ usesProxyProto(config)) { return false; } diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index bf60d318a573..de70751f4d02 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -371,6 +371,20 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, UdpAddress) { EXPECT_EQ(1u, manager_->listeners().size()); } +TEST_F(ListenerManagerImplWithRealFiltersTest, AllowOnlyDefaultFilterChain) { + const std::string yaml = R"EOF( +address: + socket_address: + address: 127.0.0.1 + port_value: 1234 +default_filter_chain: + filters: [] + )EOF"; + + manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true); + EXPECT_EQ(1, manager_->listeners().size()); +} + TEST_F(ListenerManagerImplWithRealFiltersTest, BadListenerConfig) { const std::string yaml = R"EOF( address: