diff --git a/api/envoy/config/core/v3/resolver.proto b/api/envoy/config/core/v3/resolver.proto new file mode 100644 index 0000000000000..b5e31814f56aa --- /dev/null +++ b/api/envoy/config/core/v3/resolver.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package envoy.config.core.v3; + +import "envoy/config/core/v3/address.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.core.v3"; +option java_outer_classname = "ResolverProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Resolver] + +// DNS resolver configuration which includes the underlying dns resolver addresses and options. +message DnsResolver { + // A list of dns resolver addresses + // Setting this value causes failure if the + // ``envoy.restart_features.use_apple_api_for_dns_lookups`` runtime value is true during + // server startup. Apple's API only allows overriding DNS resolvers via system settings. + repeated Address resolvers = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/api/envoy/config/core/v4alpha/resolver.proto b/api/envoy/config/core/v4alpha/resolver.proto new file mode 100644 index 0000000000000..ad597c85549de --- /dev/null +++ b/api/envoy/config/core/v4alpha/resolver.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package envoy.config.core.v4alpha; + +import "envoy/config/core/v4alpha/address.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.core.v4alpha"; +option java_outer_classname = "ResolverProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Resolver] + +// DNS resolver configuration which includes the underlying dns resolver addresses and options. +message DnsResolver { + option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.DnsResolver"; + + // A list of dns resolver addresses + // Setting this value causes failure if the + // ``envoy.restart_features.use_apple_api_for_dns_lookups`` runtime value is true during + // server startup. Apple's API only allows overriding DNS resolvers via system settings. + repeated Address resolvers = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/api/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD b/api/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD index b7a0695c214bc..6e74b985c580d 100644 --- a/api/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD +++ b/api/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/cluster/v3:pkg", "//envoy/config/common/dynamic_forward_proxy/v2alpha:pkg", + "//envoy/config/core/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto b/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto index 4b182a29711bd..1c14739b94c86 100644 --- a/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto +++ b/api/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.common.dynamic_forward_proxy.v3; import "envoy/config/cluster/v3/cluster.proto"; +import "envoy/config/core/v3/resolver.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; @@ -27,7 +28,7 @@ message DnsCacheCircuitBreakers { // Configuration for the dynamic forward proxy DNS cache. See the :ref:`architecture overview // ` for more information. -// [#next-free-field: 9] +// [#next-free-field: 10] message DnsCacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.common.dynamic_forward_proxy.v2alpha.DnsCacheConfig"; @@ -101,4 +102,9 @@ message DnsCacheConfig { // ``envoy.restart_features.use_apple_api_for_dns_lookups`` runtime value is true during // server startup. Apple' API only uses UDP for DNS resolution. bool use_tcp_for_dns_lookups = 8; + + // DNS resolver configuration + // If specified, DNS cache will perform resolution via the underlying DNS resolvers. + // Otherwise, the default system resolvers (e.g., /etc/resolv.conf) will be used. + config.core.v3.DnsResolver dns_resolver = 9; } diff --git a/docs/root/api-v3/common_messages/common_messages.rst b/docs/root/api-v3/common_messages/common_messages.rst index 843154a3bb9e1..2826b6c67ecdb 100644 --- a/docs/root/api-v3/common_messages/common_messages.rst +++ b/docs/root/api-v3/common_messages/common_messages.rst @@ -16,6 +16,7 @@ Common messages ../config/core/v3/grpc_service.proto ../config/core/v3/grpc_method_list.proto ../config/core/v3/http_uri.proto + ../config/core/v3/resolver.proto ../config/core/v3/socket_option.proto ../config/core/v3/udp_socket_config.proto ../config/core/v3/substitution_format_string.proto diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 0a13ed9987f94..2597bef503552 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -64,8 +64,10 @@ Removed Config or Runtime New Features ------------ + * bandwidth_limit: added new :ref:`HTTP bandwidth limit filter `. * crash support: restore crash context when continuing to processing requests or responses as a result of an asynchronous callback that invokes a filter directly. This is unlike the call stacks that go through the various network layers, to eventually reach the filter. For a concrete example see: ``Envoy::Extensions::HttpFilters::Cache::CacheFilter::getHeaders`` which posts a callback on the dispatcher that will invoke the filter directly. +* dynamic_forward_proxy: added :ref:`dns_resolver` option to the DNS cache config in order use custom DNS resolvers instead of the system default resolvers. * http: a new field `is_optional` is added to `extensions.filters.network.http_connection_manager.v3.HttpFilter`. When value is `true`, the unsupported http filter will be ignored by envoy. This is also same with unsupported http filter in the typed per filter config. For more information, please reference diff --git a/generated_api_shadow/envoy/config/core/v3/resolver.proto b/generated_api_shadow/envoy/config/core/v3/resolver.proto new file mode 100644 index 0000000000000..b5e31814f56aa --- /dev/null +++ b/generated_api_shadow/envoy/config/core/v3/resolver.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package envoy.config.core.v3; + +import "envoy/config/core/v3/address.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.core.v3"; +option java_outer_classname = "ResolverProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Resolver] + +// DNS resolver configuration which includes the underlying dns resolver addresses and options. +message DnsResolver { + // A list of dns resolver addresses + // Setting this value causes failure if the + // ``envoy.restart_features.use_apple_api_for_dns_lookups`` runtime value is true during + // server startup. Apple's API only allows overriding DNS resolvers via system settings. + repeated Address resolvers = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/generated_api_shadow/envoy/config/core/v4alpha/resolver.proto b/generated_api_shadow/envoy/config/core/v4alpha/resolver.proto new file mode 100644 index 0000000000000..ad597c85549de --- /dev/null +++ b/generated_api_shadow/envoy/config/core/v4alpha/resolver.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package envoy.config.core.v4alpha; + +import "envoy/config/core/v4alpha/address.proto"; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.config.core.v4alpha"; +option java_outer_classname = "ResolverProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Resolver] + +// DNS resolver configuration which includes the underlying dns resolver addresses and options. +message DnsResolver { + option (udpa.annotations.versioning).previous_message_type = "envoy.config.core.v3.DnsResolver"; + + // A list of dns resolver addresses + // Setting this value causes failure if the + // ``envoy.restart_features.use_apple_api_for_dns_lookups`` runtime value is true during + // server startup. Apple's API only allows overriding DNS resolvers via system settings. + repeated Address resolvers = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD index b7a0695c214bc..6e74b985c580d 100644 --- a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD +++ b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/BUILD @@ -8,6 +8,7 @@ api_proto_package( deps = [ "//envoy/config/cluster/v3:pkg", "//envoy/config/common/dynamic_forward_proxy/v2alpha:pkg", + "//envoy/config/core/v3:pkg", "@com_github_cncf_udpa//udpa/annotations:pkg", ], ) diff --git a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto index 4b182a29711bd..1c14739b94c86 100644 --- a/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto +++ b/generated_api_shadow/envoy/extensions/common/dynamic_forward_proxy/v3/dns_cache.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.common.dynamic_forward_proxy.v3; import "envoy/config/cluster/v3/cluster.proto"; +import "envoy/config/core/v3/resolver.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/wrappers.proto"; @@ -27,7 +28,7 @@ message DnsCacheCircuitBreakers { // Configuration for the dynamic forward proxy DNS cache. See the :ref:`architecture overview // ` for more information. -// [#next-free-field: 9] +// [#next-free-field: 10] message DnsCacheConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.common.dynamic_forward_proxy.v2alpha.DnsCacheConfig"; @@ -101,4 +102,9 @@ message DnsCacheConfig { // ``envoy.restart_features.use_apple_api_for_dns_lookups`` runtime value is true during // server startup. Apple' API only uses UDP for DNS resolution. bool use_tcp_for_dns_lookups = 8; + + // DNS resolver configuration + // If specified, DNS cache will perform resolution via the underlying DNS resolvers. + // Otherwise, the default system resolvers (e.g., /etc/resolv.conf) will be used. + config.core.v3.DnsResolver dns_resolver = 9; } diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc index c2f6cd82a8581..2aa86bbb80ba4 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.cc @@ -4,6 +4,7 @@ #include "common/config/utility.h" #include "common/http/utility.h" +#include "common/network/resolver_impl.h" #include "common/network/utility.h" // TODO(mattklein123): Move DNS family helpers to a smaller include. @@ -20,8 +21,8 @@ DnsCacheImpl::DnsCacheImpl( const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config) : main_thread_dispatcher_(main_thread_dispatcher), dns_lookup_family_(Upstream::getDnsLookupFamilyFromEnum(config.dns_lookup_family())), - resolver_(main_thread_dispatcher.createDnsResolver({}, config.use_tcp_for_dns_lookups())), - tls_slot_(tls), scope_(root_scope.createScope(fmt::format("dns_cache.{}.", config.name()))), + resolver_(selectDnsResolver(config, main_thread_dispatcher)), tls_slot_(tls), + scope_(root_scope.createScope(fmt::format("dns_cache.{}.", config.name()))), stats_(generateDnsCacheStats(*scope_)), resource_manager_(*scope_, loader, config.name(), config.dns_cache_circuit_breaker()), refresh_interval_(PROTOBUF_GET_MS_OR_DEFAULT(config, dns_refresh_rate, 60000)), @@ -46,6 +47,22 @@ DnsCacheImpl::~DnsCacheImpl() { } } +Network::DnsResolverSharedPtr DnsCacheImpl::selectDnsResolver( + const envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig& config, + Event::Dispatcher& main_thread_dispatcher) { + if (config.has_dns_resolver()) { + const auto& resolver_addrs = config.dns_resolver().resolvers(); + std::vector resolvers; + resolvers.reserve(resolver_addrs.size()); + for (const auto& resolver_addr : resolver_addrs) { + resolvers.push_back(Network::Address::resolveProtoAddress(resolver_addr)); + } + return main_thread_dispatcher.createDnsResolver(resolvers, config.use_tcp_for_dns_lookups()); + } + + return main_thread_dispatcher.createDnsResolver({}, config.use_tcp_for_dns_lookups()); +} + DnsCacheStats DnsCacheImpl::generateDnsCacheStats(Stats::Scope& scope) { return {ALL_DNS_CACHE_STATS(POOL_COUNTER(scope), POOL_GAUGE(scope))}; } diff --git a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h index c076c79404113..1f5237dc035e5 100644 --- a/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h +++ b/source/extensions/common/dynamic_forward_proxy/dns_cache_impl.h @@ -46,6 +46,9 @@ class DnsCacheImpl : public DnsCache, Logger::Loggableaddress() == nullptr; } +MATCHER_P(CustomDnsResolversSizeEquals, expected_resolvers, "") { + return expected_resolvers.size() == arg.size(); +} + // Basic successful resolution and then re-resolution. TEST_F(DnsCacheImplTest, ResolveSuccess) { initialize(); @@ -780,6 +786,42 @@ TEST(DnsCacheManagerImplTest, LoadViaConfig) { "config specified DNS cache 'foo' with different settings"); } +TEST(DnsCacheConfigOptionsTest, EmtpyDnsResolverConfig) { + NiceMock dispatcher; + std::shared_ptr resolver{std::make_shared()}; + NiceMock tls; + NiceMock random; + NiceMock loader; + Stats::IsolatedStoreImpl store; + + envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig config; + std::vector expectedEmptyDnsResolvers; + EXPECT_CALL(dispatcher, createDnsResolver(expectedEmptyDnsResolvers, _)) + .WillOnce(Return(resolver)); + DnsCacheImpl dns_cache_(dispatcher, tls, random, loader, store, config); +} + +TEST(DnsCacheConfigOptionsTest, NonEmptyDnsResolverConfig) { + NiceMock dispatcher; + std::shared_ptr resolver{std::make_shared()}; + NiceMock tls; + NiceMock random; + NiceMock loader; + Stats::IsolatedStoreImpl store; + envoy::extensions::common::dynamic_forward_proxy::v3::DnsCacheConfig config; + + envoy::config::core::v3::Address* dns_resolvers = config.mutable_dns_resolver()->add_resolvers(); + dns_resolvers->mutable_socket_address()->set_address("1.2.3.4"); + dns_resolvers->mutable_socket_address()->set_port_value(8080); + + std::vector expected_dns_resolver_config; + expected_dns_resolver_config.push_back(Network::Address::resolveProtoAddress(*dns_resolvers)); + EXPECT_CALL(dispatcher, + createDnsResolver(CustomDnsResolversSizeEquals(expected_dns_resolver_config), _)) + .WillOnce(Return(resolver)); + DnsCacheImpl dns_cache_(dispatcher, tls, random, loader, store, config); +} + // Note: this test is done here, rather than a TYPED_TEST_SUITE in // //test/common/config:utility_test, because we did not want to include an extension type in // non-extension test suites.