diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c7a6bbe1a8..036295a5674 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -496,6 +496,12 @@ protobuf_generate_grpc_cpp( protobuf_generate_grpc_cpp( src/proto/grpc/testing/xds/v3/route.proto ) +protobuf_generate_grpc_cpp( + src/proto/grpc/testing/xds/v3/string.proto +) +protobuf_generate_grpc_cpp( + src/proto/grpc/testing/xds/v3/tls.proto +) protobuf_generate_grpc_cpp( test/core/tsi/alts/fake_handshaker/handshaker.proto ) @@ -15518,6 +15524,14 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.grpc.pb.cc ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.pb.h ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/route.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.grpc.pb.cc + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.pb.h + ${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/tls.grpc.pb.h test/cpp/end2end/test_service_impl.cc test/cpp/end2end/xds_end2end_test.cc third_party/googletest/googletest/src/gtest-all.cc diff --git a/Makefile b/Makefile index 743d6c01b7c..27308c8fc84 100644 --- a/Makefile +++ b/Makefile @@ -1358,12 +1358,12 @@ $(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.pb.cc: protoc_dep_error $(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.grpc.pb.cc: protoc_dep_error else -$(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.pb.cc: src/proto/grpc/testing/xds/v3/cluster.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/xds/v3/config_source.pb.cc +$(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.pb.cc: src/proto/grpc/testing/xds/v3/cluster.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/xds/v3/base.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/v3/config_source.pb.cc $(E) "[PROTOC] Generating protobuf CC file from $<" $(Q) mkdir -p `dirname $@` $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --cpp_out=$(GENDIR) $< -$(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.grpc.pb.cc: src/proto/grpc/testing/xds/v3/cluster.proto $(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.pb.cc $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/xds/v3/config_source.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/v3/config_source.grpc.pb.cc +$(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.grpc.pb.cc: src/proto/grpc/testing/xds/v3/cluster.proto $(GENDIR)/src/proto/grpc/testing/xds/v3/cluster.pb.cc $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/xds/v3/base.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/v3/base.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/v3/config_source.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/v3/config_source.grpc.pb.cc $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $< @@ -1561,6 +1561,38 @@ $(GENDIR)/src/proto/grpc/testing/xds/v3/route.grpc.pb.cc: src/proto/grpc/testing $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $< endif +ifeq ($(NO_PROTOC),true) +$(GENDIR)/src/proto/grpc/testing/xds/v3/string.pb.cc: protoc_dep_error +$(GENDIR)/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc: protoc_dep_error +else + +$(GENDIR)/src/proto/grpc/testing/xds/v3/string.pb.cc: src/proto/grpc/testing/xds/v3/string.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --cpp_out=$(GENDIR) $< + +$(GENDIR)/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc: src/proto/grpc/testing/xds/v3/string.proto $(GENDIR)/src/proto/grpc/testing/xds/v3/string.pb.cc $(PROTOBUF_DEP) $(PROTOC_PLUGINS) + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $< +endif + +ifeq ($(NO_PROTOC),true) +$(GENDIR)/src/proto/grpc/testing/xds/v3/tls.pb.cc: protoc_dep_error +$(GENDIR)/src/proto/grpc/testing/xds/v3/tls.grpc.pb.cc: protoc_dep_error +else + +$(GENDIR)/src/proto/grpc/testing/xds/v3/tls.pb.cc: src/proto/grpc/testing/xds/v3/tls.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/xds/v3/string.pb.cc + $(E) "[PROTOC] Generating protobuf CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --cpp_out=$(GENDIR) $< + +$(GENDIR)/src/proto/grpc/testing/xds/v3/tls.grpc.pb.cc: src/proto/grpc/testing/xds/v3/tls.proto $(GENDIR)/src/proto/grpc/testing/xds/v3/tls.pb.cc $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/xds/v3/string.pb.cc $(GENDIR)/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc + $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" + $(Q) mkdir -p `dirname $@` + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin$(EXECUTABLE_SUFFIX) $< +endif + ifeq ($(NO_PROTOC),true) $(GENDIR)/test/core/tsi/alts/fake_handshaker/handshaker.pb.cc: protoc_dep_error $(GENDIR)/test/core/tsi/alts/fake_handshaker/handshaker.grpc.pb.cc: protoc_dep_error diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 03175d21bdf..ab08482b054 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -7960,6 +7960,8 @@ targets: - src/proto/grpc/testing/xds/v3/range.proto - src/proto/grpc/testing/xds/v3/regex.proto - src/proto/grpc/testing/xds/v3/route.proto + - src/proto/grpc/testing/xds/v3/string.proto + - src/proto/grpc/testing/xds/v3/tls.proto - test/cpp/end2end/test_service_impl.cc - test/cpp/end2end/xds_end2end_test.cc deps: diff --git a/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc b/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc index bb336d7df21..a7532e83e51 100644 --- a/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc +++ b/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc @@ -24,6 +24,7 @@ #include "src/core/ext/filters/client_channel/lb_policy_factory.h" #include "src/core/ext/filters/client_channel/lb_policy_registry.h" #include "src/core/ext/filters/client_channel/service_config.h" +#include "src/core/ext/xds/xds_certificate_provider.h" #include "src/core/ext/xds/xds_client.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gprpp/memory.h" @@ -31,6 +32,7 @@ #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/security/credentials/xds/xds_credentials.h" #include "src/core/lib/transport/error_utils.h" namespace grpc_core { @@ -121,6 +123,9 @@ class CdsLb : public LoadBalancingPolicy { void OnError(grpc_error* error); void OnResourceDoesNotExist(); + grpc_error* UpdateXdsCertificateProvider( + const XdsApi::CdsUpdate& cluster_data); + void MaybeDestroyChildPolicyLocked(); RefCountedPtr config_; @@ -134,6 +139,10 @@ class CdsLb : public LoadBalancingPolicy { // Note that this is not owned, so this pointer must never be derefernced. ClusterWatcher* cluster_watcher_ = nullptr; + RefCountedPtr root_certificate_provider_; + RefCountedPtr identity_certificate_provider_; + RefCountedPtr xds_certificate_provider_; + // Child LB policy. OrphanablePtr child_policy_; @@ -322,6 +331,11 @@ void CdsLb::OnClusterChanged(XdsApi::CdsUpdate cluster_data) { : "(unset)", cluster_data.max_concurrent_requests); } + grpc_error* error = GRPC_ERROR_NONE; + error = UpdateXdsCertificateProvider(cluster_data); + if (error != GRPC_ERROR_NONE) { + return OnError(error); + } // Construct config for child policy. Json::Object child_config = { {"clusterName", config_->cluster()}, @@ -359,7 +373,6 @@ void CdsLb::OnClusterChanged(XdsApi::CdsUpdate cluster_data) { gpr_log(GPR_INFO, "[cdslb %p] generated config for child policy: %s", this, json_str.c_str()); } - grpc_error* error = GRPC_ERROR_NONE; RefCountedPtr config = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(json, &error); if (error != GRPC_ERROR_NONE) { @@ -389,7 +402,12 @@ void CdsLb::OnClusterChanged(XdsApi::CdsUpdate cluster_data) { // Update child policy. UpdateArgs args; args.config = std::move(config); - args.args = grpc_channel_args_copy(args_); + if (xds_certificate_provider_ != nullptr) { + grpc_arg arg_to_add = xds_certificate_provider_->MakeChannelArg(); + args.args = grpc_channel_args_copy_and_add(args_, &arg_to_add, 1); + } else { + args.args = grpc_channel_args_copy(args_); + } child_policy_->UpdateLocked(std::move(args)); } @@ -425,6 +443,122 @@ void CdsLb::OnResourceDoesNotExist() { MaybeDestroyChildPolicyLocked(); } +grpc_error* CdsLb::UpdateXdsCertificateProvider( + const XdsApi::CdsUpdate& cluster_data) { + // Early out if channel is not configured to use xds security. + grpc_channel_credentials* channel_credentials = + grpc_channel_credentials_find_in_args(args_); + if (channel_credentials == nullptr || + channel_credentials->type() != XdsCredentials::kCredentialsTypeXds) { + xds_certificate_provider_ = nullptr; + return GRPC_ERROR_NONE; + } + absl::string_view root_provider_instance_name = + cluster_data.common_tls_context.combined_validation_context + .validation_context_certificate_provider_instance.instance_name; + absl::string_view root_provider_cert_name = + cluster_data.common_tls_context.combined_validation_context + .validation_context_certificate_provider_instance.certificate_name; + absl::string_view identity_provider_instance_name = + cluster_data.common_tls_context + .tls_certificate_certificate_provider_instance.instance_name; + absl::string_view identity_provider_cert_name = + cluster_data.common_tls_context + .tls_certificate_certificate_provider_instance.certificate_name; + RefCountedPtr new_root_provider; + if (!root_provider_instance_name.empty()) { + new_root_provider = + xds_client_->certificate_provider_store() + .CreateOrGetCertificateProvider(root_provider_instance_name); + if (new_root_provider == nullptr) { + return GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("Certificate provider instance name: \"", + root_provider_instance_name, "\" not recognized.") + .c_str()); + } + } + if (root_certificate_provider_ != new_root_provider) { + if (root_certificate_provider_ != nullptr && + root_certificate_provider_->interested_parties() != nullptr) { + grpc_pollset_set_del_pollset_set( + interested_parties(), + root_certificate_provider_->interested_parties()); + } + if (new_root_provider != nullptr && + new_root_provider->interested_parties() != nullptr) { + grpc_pollset_set_add_pollset_set(interested_parties(), + new_root_provider->interested_parties()); + } + root_certificate_provider_ = std::move(new_root_provider); + } + RefCountedPtr new_identity_provider; + if (!identity_provider_instance_name.empty()) { + new_identity_provider = + xds_client_->certificate_provider_store() + .CreateOrGetCertificateProvider(identity_provider_instance_name); + if (new_identity_provider == nullptr) { + return GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("Certificate provider instance name: \"", + identity_provider_instance_name, "\" not recognized.") + .c_str()); + } + } + if (identity_certificate_provider_ != new_identity_provider) { + if (identity_certificate_provider_ != nullptr && + identity_certificate_provider_->interested_parties() != nullptr) { + grpc_pollset_set_del_pollset_set( + interested_parties(), + identity_certificate_provider_->interested_parties()); + } + if (new_identity_provider != nullptr && + new_identity_provider->interested_parties() != nullptr) { + grpc_pollset_set_add_pollset_set( + interested_parties(), new_identity_provider->interested_parties()); + } + identity_certificate_provider_ = std::move(new_identity_provider); + } + if (!root_provider_instance_name.empty() && + !identity_provider_instance_name.empty()) { + // Using mTLS configuration + if (xds_certificate_provider_ != nullptr && + xds_certificate_provider_->ProvidesRootCerts() && + xds_certificate_provider_->ProvidesIdentityCerts()) { + xds_certificate_provider_->UpdateRootCertNameAndDistributor( + root_provider_cert_name, root_certificate_provider_->distributor()); + xds_certificate_provider_->UpdateIdentityCertNameAndDistributor( + identity_provider_cert_name, + identity_certificate_provider_->distributor()); + } else { + // Existing xDS certificate provider does not have mTLS configuration. + // Create new certificate provider so that new subchannel connectors are + // created. + xds_certificate_provider_ = MakeRefCounted( + root_provider_cert_name, root_certificate_provider_->distributor(), + identity_provider_cert_name, + identity_certificate_provider_->distributor()); + } + } else if (!root_provider_instance_name.empty()) { + // Using TLS configuration + if (xds_certificate_provider_ != nullptr && + xds_certificate_provider_->ProvidesRootCerts() && + !xds_certificate_provider_->ProvidesIdentityCerts()) { + xds_certificate_provider_->UpdateRootCertNameAndDistributor( + root_provider_cert_name, root_certificate_provider_->distributor()); + } else { + // Existing xDS certificate provider does not have TLS configuration. + // Create new certificate provider so that new subchannel connectors are + // created. + xds_certificate_provider_ = MakeRefCounted( + root_provider_cert_name, root_certificate_provider_->distributor(), + "", nullptr); + } + } else { + // No configuration provided. + xds_certificate_provider_ = nullptr; + } + return GRPC_ERROR_NONE; +} + // // factory // diff --git a/src/core/ext/xds/certificate_provider_store.cc b/src/core/ext/xds/certificate_provider_store.cc index dedb2bbba00..dd66b97a0af 100644 --- a/src/core/ext/xds/certificate_provider_store.cc +++ b/src/core/ext/xds/certificate_provider_store.cc @@ -36,13 +36,16 @@ CertificateProviderStore::CreateOrGetCertificateProvider( MutexLock lock(&mu_); auto it = certificate_providers_map_.find(key); if (it == certificate_providers_map_.end()) { - it = certificate_providers_map_.insert({key, nullptr}).first; + result = CreateCertificateProviderLocked(key); + if (result != nullptr) { + certificate_providers_map_.insert({result->key(), result.get()}); + } } else { result = it->second->RefIfNonZero(); - } - if (result == nullptr) { - result = CreateCertificateProviderLocked(key); - it->second = result.get(); + if (result == nullptr) { + result = CreateCertificateProviderLocked(key); + it->second = result.get(); + } } return result; } @@ -66,8 +69,8 @@ CertificateProviderStore::CreateCertificateProviderLocked( return nullptr; } return MakeRefCounted( - factory->CreateCertificateProvider(plugin_config_it->second.config), this, - plugin_config_it->first); + factory->CreateCertificateProvider(plugin_config_it->second.config), + Ref(), plugin_config_it->first); } void CertificateProviderStore::ReleaseCertificateProvider( diff --git a/src/core/ext/xds/certificate_provider_store.h b/src/core/ext/xds/certificate_provider_store.h index ef22d365c61..0954bc5e809 100644 --- a/src/core/ext/xds/certificate_provider_store.h +++ b/src/core/ext/xds/certificate_provider_store.h @@ -26,15 +26,16 @@ #include "absl/strings/string_view.h" #include "src/core/ext/xds/certificate_provider_factory.h" +#include "src/core/lib/gprpp/orphanable.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/sync.h" #include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h" namespace grpc_core { -// Map for xDS based grpc_tls_certificate_provider instances. The store should -// outlive the refs taken via `CreateOrGetCertificateProvider()`. -class CertificateProviderStore { +// Map for xDS based grpc_tls_certificate_provider instances. +class CertificateProviderStore + : public InternallyRefCounted { public: struct PluginDefinition { std::string plugin_name; @@ -44,7 +45,7 @@ class CertificateProviderStore { // Maps plugin instance (opaque) name to plugin defition. typedef std::map PluginDefinitionMap; - CertificateProviderStore(PluginDefinitionMap plugin_config_map) + explicit CertificateProviderStore(PluginDefinitionMap plugin_config_map) : plugin_config_map_(std::move(plugin_config_map)) {} // If a certificate provider corresponding to the instance name \a key is @@ -55,6 +56,8 @@ class CertificateProviderStore { RefCountedPtr CreateOrGetCertificateProvider( absl::string_view key); + void Orphan() override { Unref(); } + private: // A thin wrapper around `grpc_tls_certificate_provider` which allows removing // the entry from the CertificateProviderStore when the refcount reaches zero. @@ -62,9 +65,9 @@ class CertificateProviderStore { public: CertificateProviderWrapper( RefCountedPtr certificate_provider, - CertificateProviderStore* store, absl::string_view key) + RefCountedPtr store, absl::string_view key) : certificate_provider_(std::move(certificate_provider)), - store_(store), + store_(std::move(store)), key_(key) {} ~CertificateProviderWrapper() override { @@ -80,9 +83,11 @@ class CertificateProviderStore { return certificate_provider_->interested_parties(); } + absl::string_view key() const { return key_; } + private: RefCountedPtr certificate_provider_; - CertificateProviderStore* store_; + RefCountedPtr store_; absl::string_view key_; }; diff --git a/src/core/ext/xds/xds_certificate_provider.cc b/src/core/ext/xds/xds_certificate_provider.cc index e6ad4dd9134..1cc98d5a03e 100644 --- a/src/core/ext/xds/xds_certificate_provider.cc +++ b/src/core/ext/xds/xds_certificate_provider.cc @@ -18,10 +18,12 @@ #include +#include "src/core/ext/xds/xds_certificate_provider.h" + #include "absl/functional/bind_front.h" #include "absl/strings/str_cat.h" -#include "src/core/ext/xds/xds_certificate_provider.h" +#include "src/core/lib/gpr/useful.h" namespace grpc_core { @@ -110,10 +112,18 @@ XdsCertificateProvider::XdsCertificateProvider( absl::bind_front(&XdsCertificateProvider::WatchStatusCallback, this)); } +XdsCertificateProvider::~XdsCertificateProvider() { + distributor_->SetWatchStatusCallback(nullptr); +} + void XdsCertificateProvider::UpdateRootCertNameAndDistributor( absl::string_view root_cert_name, RefCountedPtr root_cert_distributor) { MutexLock lock(&mu_); + if (root_cert_name_ == root_cert_name && + root_cert_distributor_ == root_cert_distributor) { + return; + } root_cert_name_ = std::string(root_cert_name); if (watching_root_certs_) { // The root certificates are being watched. Swap out the watcher. @@ -139,6 +149,10 @@ void XdsCertificateProvider::UpdateIdentityCertNameAndDistributor( absl::string_view identity_cert_name, RefCountedPtr identity_cert_distributor) { MutexLock lock(&mu_); + if (identity_cert_name_ == identity_cert_name && + identity_cert_distributor_ == identity_cert_distributor) { + return; + } identity_cert_name_ = std::string(identity_cert_name); if (watching_identity_certs_) { // The identity certificates are being watched. Swap out the watcher. @@ -237,4 +251,41 @@ void XdsCertificateProvider::UpdateIdentityCertWatcher( std::move(watcher), absl::nullopt, identity_cert_name_); } +namespace { + +void* XdsCertificateProviderArgCopy(void* p) { + XdsCertificateProvider* xds_certificate_provider = + static_cast(p); + return xds_certificate_provider->Ref().release(); +} + +void XdsCertificateProviderArgDestroy(void* p) { + XdsCertificateProvider* xds_certificate_provider = + static_cast(p); + xds_certificate_provider->Unref(); +} + +int XdsCertificateProviderArgCmp(void* p, void* q) { return GPR_ICMP(p, q); } + +const grpc_arg_pointer_vtable kChannelArgVtable = { + XdsCertificateProviderArgCopy, XdsCertificateProviderArgDestroy, + XdsCertificateProviderArgCmp}; + +} // namespace + +grpc_arg XdsCertificateProvider::MakeChannelArg() const { + return grpc_channel_arg_pointer_create( + const_cast(GRPC_ARG_XDS_CERTIFICATE_PROVIDER), + const_cast(this), &kChannelArgVtable); +} + +RefCountedPtr +XdsCertificateProvider::GetFromChannelArgs(const grpc_channel_args* args) { + XdsCertificateProvider* xds_certificate_provider = + grpc_channel_args_find_pointer( + args, GRPC_ARG_XDS_CERTIFICATE_PROVIDER); + return xds_certificate_provider != nullptr ? xds_certificate_provider->Ref() + : nullptr; +} + } // namespace grpc_core diff --git a/src/core/ext/xds/xds_certificate_provider.h b/src/core/ext/xds/xds_certificate_provider.h index caf0e5cc8ae..391137595e8 100644 --- a/src/core/ext/xds/xds_certificate_provider.h +++ b/src/core/ext/xds/xds_certificate_provider.h @@ -23,6 +23,9 @@ #include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h" +#define GRPC_ARG_XDS_CERTIFICATE_PROVIDER \ + "grpc.internal.xds_certificate_provider" + namespace grpc_core { class XdsCertificateProvider : public grpc_tls_certificate_provider { @@ -34,6 +37,8 @@ class XdsCertificateProvider : public grpc_tls_certificate_provider { RefCountedPtr identity_cert_distributor); + ~XdsCertificateProvider() override; + void UpdateRootCertNameAndDistributor( absl::string_view root_cert_name, RefCountedPtr root_cert_distributor); @@ -47,6 +52,15 @@ class XdsCertificateProvider : public grpc_tls_certificate_provider { return distributor_; } + bool ProvidesRootCerts() { return root_cert_distributor_ != nullptr; } + + bool ProvidesIdentityCerts() { return identity_cert_distributor_ != nullptr; } + + grpc_arg MakeChannelArg() const; + + static RefCountedPtr GetFromChannelArgs( + const grpc_channel_args* args); + private: void WatchStatusCallback(std::string cert_name, bool root_being_watched, bool identity_being_watched); diff --git a/src/core/ext/xds/xds_client.cc b/src/core/ext/xds/xds_client.cc index 979ebd7ab74..3d2832e2873 100644 --- a/src/core/ext/xds/xds_client.cc +++ b/src/core/ext/xds/xds_client.cc @@ -1744,6 +1744,10 @@ XdsClient::XdsClient(grpc_error** error) interested_parties_(grpc_pollset_set_create()), bootstrap_( XdsBootstrap::ReadFromFile(this, &grpc_xds_client_trace, error)), + certificate_provider_store_(MakeOrphanable( + bootstrap_ == nullptr + ? CertificateProviderStore::PluginDefinitionMap() + : bootstrap_->certificate_providers())), api_(this, &grpc_xds_client_trace, bootstrap_ == nullptr ? nullptr : bootstrap_->node()) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) { diff --git a/src/core/ext/xds/xds_client.h b/src/core/ext/xds/xds_client.h index 49ec9dcc32a..2f7ce13e8fc 100644 --- a/src/core/ext/xds/xds_client.h +++ b/src/core/ext/xds/xds_client.h @@ -88,6 +88,10 @@ class XdsClient : public DualRefCounted { explicit XdsClient(grpc_error** error); ~XdsClient() override; + CertificateProviderStore& certificate_provider_store() { + return *certificate_provider_store_; + } + grpc_pollset_set* interested_parties() const { return interested_parties_; } // TODO(roth): When we add federation, there will be multiple channels @@ -292,6 +296,7 @@ class XdsClient : public DualRefCounted { const grpc_millis request_timeout_; grpc_pollset_set* interested_parties_; std::unique_ptr bootstrap_; + OrphanablePtr certificate_provider_store_; XdsApi api_; Mutex mu_; diff --git a/src/core/lib/security/credentials/xds/xds_credentials.cc b/src/core/lib/security/credentials/xds/xds_credentials.cc index 682d49badb0..4cd7f48d26a 100644 --- a/src/core/lib/security/credentials/xds/xds_credentials.cc +++ b/src/core/lib/security/credentials/xds/xds_credentials.cc @@ -20,26 +20,86 @@ #include "src/core/lib/security/credentials/xds/xds_credentials.h" +#include "src/core/ext/xds/xds_certificate_provider.h" +#include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h" +#include "src/core/lib/security/credentials/tls/tls_credentials.h" +#include "src/core/lib/uri/uri_parser.h" + namespace grpc_core { constexpr const char XdsCredentials::kCredentialsTypeXds[]; -grpc_core::RefCountedPtr +namespace { + +int ServerAuthCheckSchedule(void* /* config_user_data */, + grpc_tls_server_authorization_check_arg* arg) { + // TODO(yashykt): To be filled + arg->success = 1; + arg->status = GRPC_STATUS_OK; + return 0; /* synchronous check */ +} + +void ServerAuthCheckDestroy(void* config_user_data) { + XdsCertificateProvider* xds_certificate_provider = + static_cast(config_user_data); + xds_certificate_provider->Unref(); +} + +} // namespace + +RefCountedPtr XdsCredentials::create_security_connector( - grpc_core::RefCountedPtr call_creds, - const char* target_name, const grpc_channel_args* args, - grpc_channel_args** new_args) { - /* TODO(yashkt) : To be filled */ - if (fallback_credentials_ != nullptr) { - return fallback_credentials_->create_security_connector( - std::move(call_creds), target_name, args, new_args); + RefCountedPtr call_creds, const char* target_name, + const grpc_channel_args* args, grpc_channel_args** new_args) { + auto xds_certificate_provider = + XdsCertificateProvider::GetFromChannelArgs(args); + // TODO(yashykt): This arg will no longer need to be added after b/173119596 + // is fixed. + grpc_arg override_arg = grpc_channel_arg_string_create( + const_cast(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG), + const_cast(target_name)); + const char* override_arg_name = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG; + const grpc_channel_args* temp_args = args; + if (grpc_channel_args_find(args, override_arg_name) == nullptr) { + temp_args = grpc_channel_args_copy_and_add_and_remove( + args, &override_arg_name, 1, &override_arg, 1); + } + RefCountedPtr security_connector; + if (xds_certificate_provider != nullptr) { + auto tls_credentials_options = + MakeRefCounted(); + tls_credentials_options->set_certificate_provider(xds_certificate_provider); + if (xds_certificate_provider->ProvidesRootCerts()) { + tls_credentials_options->set_watch_root_cert(true); + } + if (xds_certificate_provider->ProvidesIdentityCerts()) { + tls_credentials_options->set_watch_identity_pair(true); + } + tls_credentials_options->set_server_verification_option( + GRPC_TLS_SKIP_HOSTNAME_VERIFICATION); + tls_credentials_options->set_server_authorization_check_config( + MakeRefCounted( + xds_certificate_provider->Ref().release(), ServerAuthCheckSchedule, + nullptr, ServerAuthCheckDestroy)); + auto tls_credentials = + MakeRefCounted(std::move(tls_credentials_options)); + security_connector = tls_credentials->create_security_connector( + std::move(call_creds), target_name, temp_args, new_args); + } else { + GPR_ASSERT(fallback_credentials_ != nullptr); + security_connector = fallback_credentials_->create_security_connector( + std::move(call_creds), target_name, temp_args, new_args); + } + if (temp_args != args) { + grpc_channel_args_destroy(temp_args); } - return nullptr; + return security_connector; } } // namespace grpc_core grpc_channel_credentials* grpc_xds_credentials_create( grpc_channel_credentials* fallback_credentials) { + GPR_ASSERT(fallback_credentials != nullptr); return new grpc_core::XdsCredentials(fallback_credentials->Ref()); } diff --git a/src/cpp/client/xds_credentials.cc b/src/cpp/client/xds_credentials.cc index ff9aa616d0e..63b48837ef0 100644 --- a/src/cpp/client/xds_credentials.cc +++ b/src/cpp/client/xds_credentials.cc @@ -23,6 +23,7 @@ namespace experimental { std::shared_ptr XdsCredentials( const std::shared_ptr& fallback_creds) { + GPR_ASSERT(fallback_creds != nullptr); if (fallback_creds->IsInsecure()) { grpc_channel_credentials* insecure_creds = grpc_insecure_credentials_create(); diff --git a/src/proto/grpc/testing/xds/v3/BUILD b/src/proto/grpc/testing/xds/v3/BUILD index 62e2813ee29..f274e8ae443 100644 --- a/src/proto/grpc/testing/xds/v3/BUILD +++ b/src/proto/grpc/testing/xds/v3/BUILD @@ -83,6 +83,7 @@ grpc_proto_library( ], well_known_protos = True, deps = [ + "base_proto", "config_source_proto", ], ) @@ -187,3 +188,22 @@ grpc_proto_library( "route_proto", ], ) + +grpc_proto_library( + name = "string_proto", + srcs = [ + "string.proto", + ], + well_known_protos = True, +) + +grpc_proto_library( + name = "tls_proto", + srcs = [ + "tls.proto", + ], + well_known_protos = True, + deps = [ + "string_proto", + ], +) diff --git a/src/proto/grpc/testing/xds/v3/base.proto b/src/proto/grpc/testing/xds/v3/base.proto index acecdda806a..b5acb0945b7 100644 --- a/src/proto/grpc/testing/xds/v3/base.proto +++ b/src/proto/grpc/testing/xds/v3/base.proto @@ -20,6 +20,7 @@ package envoy.config.core.v3; import "src/proto/grpc/testing/xds/v3/percent.proto"; +import "google/protobuf/any.proto"; import "google/protobuf/struct.proto"; // Identifies location of where either Envoy runs or where upstream hosts run. @@ -109,3 +110,19 @@ message RuntimeFractionalPercent { // Default value if the runtime value's for the numerator/denominator keys are not available. type.v3.FractionalPercent default_value = 1; } + +// Configuration for transport socket in :ref:`listeners ` and +// :ref:`clusters `. If the configuration is +// empty, a default transport socket implementation and configuration will be +// chosen based on the platform and existence of tls_context. +message TransportSocket { + // The name of the transport socket to instantiate. The name must match a supported transport + // socket implementation. + string name = 1; + + // Implementation specific configuration which depends on the implementation being instantiated. + // See the supported transport socket implementations for further documentation. + oneof config_type { + google.protobuf.Any typed_config = 3; + } +} diff --git a/src/proto/grpc/testing/xds/v3/cluster.proto b/src/proto/grpc/testing/xds/v3/cluster.proto index c20d887bc21..c493e865973 100644 --- a/src/proto/grpc/testing/xds/v3/cluster.proto +++ b/src/proto/grpc/testing/xds/v3/cluster.proto @@ -18,6 +18,7 @@ syntax = "proto3"; package envoy.config.cluster.v3; +import "src/proto/grpc/testing/xds/v3/base.proto"; import "src/proto/grpc/testing/xds/v3/config_source.proto"; import "google/protobuf/wrappers.proto"; @@ -144,6 +145,13 @@ message Cluster { CircuitBreakers circuit_breakers = 10; + // Optional custom transport socket implementation to use for upstream connections. + // To setup TLS, set a transport socket with name `tls` and + // :ref:`UpstreamTlsContexts ` in the `typed_config`. + // If no transport socket configuration is specified, new connections + // will be set up with plaintext. + core.v3.TransportSocket transport_socket = 24; + // [#not-implemented-hide:] // If present, tells the client where to send load reports via LRS. If not present, the // client will fall back to a client-side default, which may be either (a) don't send any diff --git a/src/proto/grpc/testing/xds/v3/string.proto b/src/proto/grpc/testing/xds/v3/string.proto new file mode 100644 index 00000000000..0ab098c7fbd --- /dev/null +++ b/src/proto/grpc/testing/xds/v3/string.proto @@ -0,0 +1,35 @@ +// Copyright 2020 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Local copy of Envoy xDS proto file, used for testing only. + +syntax = "proto3"; + +package envoy.type.matcher.v3; + +message StringMatcher { + oneof match_pattern { + // The input string must match exactly the string specified here. + // + // Examples: + // + // * *abc* only matches the value *abc*. + string exact = 1; + } + + // If true, indicates the exact/prefix/suffix matching should be case insensitive. This has no + // effect for the safe_regex match. + // For example, the matcher *data* will match both input string *Data* and *data* if set to true. + bool ignore_case = 6; +} diff --git a/src/proto/grpc/testing/xds/v3/tls.proto b/src/proto/grpc/testing/xds/v3/tls.proto new file mode 100644 index 00000000000..47db0a0b9c6 --- /dev/null +++ b/src/proto/grpc/testing/xds/v3/tls.proto @@ -0,0 +1,102 @@ +// Copyright 2020 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Local copy of Envoy xDS proto file, used for testing only. + +syntax = "proto3"; + +package envoy.extensions.transport_sockets.tls.v3; + +import "src/proto/grpc/testing/xds/v3/string.proto"; + +message CertificateValidationContext { + // An optional list of Subject Alternative name matchers. If specified, Envoy will verify that the + // Subject Alternative Name of the presented certificate matches one of the specified matchers. + // + // When a certificate has wildcard DNS SAN entries, to match a specific client, it should be + // configured with exact match type in the :ref:`string matcher `. + // For example if the certificate has "\*.example.com" as DNS SAN entry, to allow only "api.example.com", + // it should be configured as shown below. + // + // .. code-block:: yaml + // + // match_subject_alt_names: + // exact: "api.example.com" + // + // .. attention:: + // + // Subject Alternative Names are easily spoofable and verifying only them is insecure, + // therefore this option must be used together with :ref:`trusted_ca + // `. + repeated type.matcher.v3.StringMatcher match_subject_alt_names = 9; +} + +message UpstreamTlsContext { + // Common TLS context settings. + // + // .. attention:: + // + // Server certificate verification is not enabled by default. Configure + // :ref:`trusted_ca` to enable + // verification. + CommonTlsContext common_tls_context = 1; +} + +// TLS context shared by both client and server TLS contexts. +// [#next-free-field: 14] +message CommonTlsContext { + // Similar to CertificateProvider above, but allows the provider instances to be configured on + // the client side instead of being sent from the control plane. + message CertificateProviderInstance { + // Provider instance name. This name must be defined in the client's configuration (e.g., a + // bootstrap file) to correspond to a provider instance (i.e., the same data in the typed_config + // field that would be sent in the CertificateProvider message if the config was sent by the + // control plane). If not present, defaults to "default". + // + // Instance names should generally be defined not in terms of the underlying provider + // implementation (e.g., "file_watcher") but rather in terms of the function of the + // certificates (e.g., "foo_deployment_identity"). + string instance_name = 1; + + // Opaque name used to specify certificate instances or types. For example, "ROOTCA" to specify + // a root-certificate (validation context) or "example.com" to specify a certificate for a + // particular domain. Not all provider instances will actually use this field, so the value + // defaults to the empty string. + string certificate_name = 2; + } + + message CombinedCertificateValidationContext { + // How to validate peer certificates. + CertificateValidationContext default_validation_context = 1; + + // Certificate provider instance for fetching validation context. + // Only one of validation_context_sds_secret_config, validation_context_certificate_provider, + // or validation_context_certificate_provider_instance may be used. + CertificateProviderInstance validation_context_certificate_provider_instance = 4; + } + + // Certificate provider instance for fetching TLS certificates. + CertificateProviderInstance tls_certificate_certificate_provider_instance = 11; + + oneof validation_context_type { + // Combined certificate validation context holds a default CertificateValidationContext + // and SDS config. When SDS server returns dynamic CertificateValidationContext, both dynamic + // and default CertificateValidationContext are merged into a new CertificateValidationContext + // for validation. This merge is done by Message::MergeFrom(), so dynamic + // CertificateValidationContext overwrites singular fields in default + // CertificateValidationContext, and concatenates repeated fields to default + // CertificateValidationContext, and logical OR is applied to boolean fields. + CombinedCertificateValidationContext combined_validation_context = 8; + } +} diff --git a/test/core/xds/certificate_provider_store_test.cc b/test/core/xds/certificate_provider_store_test.cc index 4e31e913ce2..0866ed4bc2b 100644 --- a/test/core/xds/certificate_provider_store_test.cc +++ b/test/core/xds/certificate_provider_store_test.cc @@ -109,21 +109,21 @@ TEST_F(CertificateProviderStoreTest, Basic) { {"fake1", fake_factory_1->CreateCertificateProviderConfig(Json::Object(), nullptr)}}, }; - CertificateProviderStore store(std::move(map)); + auto store = MakeOrphanable(std::move(map)); // Test for creating certificate providers with known plugin configuration. - auto cert_provider_1 = store.CreateOrGetCertificateProvider("fake_plugin_1"); + auto cert_provider_1 = store->CreateOrGetCertificateProvider("fake_plugin_1"); ASSERT_NE(cert_provider_1, nullptr); - auto cert_provider_3 = store.CreateOrGetCertificateProvider("fake_plugin_3"); + auto cert_provider_3 = store->CreateOrGetCertificateProvider("fake_plugin_3"); ASSERT_NE(cert_provider_3, nullptr); // Test for creating certificate provider with known plugin configuration but // unregistered factory. - ASSERT_EQ(store.CreateOrGetCertificateProvider("fake_plugin_2"), nullptr); + ASSERT_EQ(store->CreateOrGetCertificateProvider("fake_plugin_2"), nullptr); // Test for creating certificate provider with unknown plugin configuration. - ASSERT_EQ(store.CreateOrGetCertificateProvider("unknown"), nullptr); + ASSERT_EQ(store->CreateOrGetCertificateProvider("unknown"), nullptr); // Test for getting previously created certificate providers. - ASSERT_EQ(store.CreateOrGetCertificateProvider("fake_plugin_1"), + ASSERT_EQ(store->CreateOrGetCertificateProvider("fake_plugin_1"), cert_provider_1); - ASSERT_EQ(store.CreateOrGetCertificateProvider("fake_plugin_3"), + ASSERT_EQ(store->CreateOrGetCertificateProvider("fake_plugin_3"), cert_provider_3); // Release previously created certificate providers so that the store outlasts // the certificate providers. @@ -139,14 +139,14 @@ TEST_F(CertificateProviderStoreTest, Multithreaded) { {"fake_plugin_1", {"fake1", fake_factory_1->CreateCertificateProviderConfig(Json::Object(), nullptr)}}}; - CertificateProviderStore store(std::move(map)); + auto store = MakeOrphanable(std::move(map)); // Test concurrent `CreateOrGetCertificateProvider()` with the same key. std::vector threads; threads.reserve(1000); for (auto i = 0; i < 1000; i++) { threads.emplace_back([&store]() { for (auto i = 0; i < 10; ++i) { - ASSERT_NE(store.CreateOrGetCertificateProvider("fake_plugin_1"), + ASSERT_NE(store->CreateOrGetCertificateProvider("fake_plugin_1"), nullptr); } }); diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD index d140a359b10..23a9af58d09 100644 --- a/test/cpp/end2end/BUILD +++ b/test/cpp/end2end/BUILD @@ -503,6 +503,15 @@ grpc_cc_test( name = "xds_end2end_test", size = "large", srcs = ["xds_end2end_test.cc"], + data = [ + "//src/core/tsi/test_creds:badclient.key", + "//src/core/tsi/test_creds:badclient.pem", + "//src/core/tsi/test_creds:ca.pem", + "//src/core/tsi/test_creds:client.key", + "//src/core/tsi/test_creds:client.pem", + "//src/core/tsi/test_creds:server1.key", + "//src/core/tsi/test_creds:server1.pem", + ], external_deps = [ "gtest", ], @@ -534,6 +543,7 @@ grpc_cc_test( "//src/proto/grpc/testing/xds/v3:listener_proto", "//src/proto/grpc/testing/xds/v3:lrs_proto", "//src/proto/grpc/testing/xds/v3:route_proto", + "//src/proto/grpc/testing/xds/v3:tls_proto", "//test/core/util:grpc_test_util", "//test/cpp/util:test_util", ], diff --git a/test/cpp/end2end/xds_end2end_test.cc b/test/cpp/end2end/xds_end2end_test.cc index 2af66f85f8b..cdc5f6bc944 100644 --- a/test/cpp/end2end/xds_end2end_test.cc +++ b/test/cpp/end2end/xds_end2end_test.cc @@ -34,18 +34,21 @@ #include "absl/types/optional.h" #include +#include #include #include #include #include #include #include +#include #include #include #include "src/core/ext/filters/client_channel/backup_poller.h" #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" #include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/ext/xds/certificate_provider_registry.h" #include "src/core/ext/xds/xds_api.h" #include "src/core/ext/xds/xds_channel_args.h" #include "src/core/ext/xds/xds_client.h" @@ -55,6 +58,7 @@ #include "src/core/lib/gprpp/map.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/sync.h" +#include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/iomgr/parse_address.h" #include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/security/credentials/fake/fake_credentials.h" @@ -81,6 +85,7 @@ #include "src/proto/grpc/testing/xds/v3/listener.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/lrs.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/route.grpc.pb.h" +#include "src/proto/grpc/testing/xds/v3/tls.grpc.pb.h" namespace grpc { namespace testing { @@ -97,6 +102,7 @@ using ::envoy::config::listener::v3::Listener; using ::envoy::config::route::v3::RouteConfiguration; using ::envoy::extensions::filters::network::http_connection_manager::v3:: HttpConnectionManager; +using ::envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext; using ::envoy::type::v3::FractionalPercent; constexpr char kLdsTypeUrl[] = @@ -171,6 +177,14 @@ constexpr char kBootstrapFileV3[] = " \"zone\": \"svl\",\n" " \"subzone\": \"mp3\"\n" " }\n" + " },\n" + " \"certificate_providers\": {\n" + " \"fake_plugin1\": {\n" + " \"plugin_name\": \"fake1\"\n" + " },\n" + " \"fake_plugin2\": {\n" + " \"plugin_name\": \"fake2\"\n" + " }\n" " }\n" "}\n"; @@ -199,6 +213,13 @@ constexpr char kBootstrapFileV2[] = " }\n" " }\n" "}\n"; +constexpr char kCaCertPath[] = "src/core/tsi/test_creds/ca.pem"; +constexpr char kServerCertPath[] = "src/core/tsi/test_creds/server1.pem"; +constexpr char kServerKeyPath[] = "src/core/tsi/test_creds/server1.key"; +constexpr char kClientCertPath[] = "src/core/tsi/test_creds/client.pem"; +constexpr char kClientKeyPath[] = "src/core/tsi/test_creds/client.key"; +constexpr char kBadClientCertPath[] = "src/core/tsi/test_creds/badclient.pem"; +constexpr char kBadClientKeyPath[] = "src/core/tsi/test_creds/badclient.key"; char* g_bootstrap_file_v3; char* g_bootstrap_file_v2; @@ -268,9 +289,6 @@ class CountedService : public ServiceType { size_t response_count_ = 0; }; -const char g_kCallCredsMdKey[] = "Balancer should not ..."; -const char g_kCallCredsMdValue[] = "... receive me"; - template class BackendServiceImpl : public CountedService> { @@ -279,19 +297,20 @@ class BackendServiceImpl Status Echo(ServerContext* context, const EchoRequest* request, EchoResponse* response) override { - // Backend should receive the call credentials metadata. - auto call_credentials_entry = - context->client_metadata().find(g_kCallCredsMdKey); - EXPECT_NE(call_credentials_entry, context->client_metadata().end()); - if (call_credentials_entry != context->client_metadata().end()) { - EXPECT_EQ(call_credentials_entry->second, g_kCallCredsMdValue); - } + auto peer_identity = context->auth_context()->GetPeerIdentity(); CountedService>::IncreaseRequestCount(); const auto status = TestMultipleServiceImpl::Echo(context, request, response); CountedService< TestMultipleServiceImpl>::IncreaseResponseCount(); - AddClient(context->peer()); + { + grpc_core::MutexLock lock(&mu_); + clients_.insert(context->peer()); + last_peer_identity_.clear(); + for (const auto& entry : peer_identity) { + last_peer_identity_.emplace_back(entry.data(), entry.size()); + } + } return status; } @@ -309,18 +328,19 @@ class BackendServiceImpl void Shutdown() {} std::set clients() { - grpc_core::MutexLock lock(&clients_mu_); + grpc_core::MutexLock lock(&mu_); return clients_; } - private: - void AddClient(const std::string& client) { - grpc_core::MutexLock lock(&clients_mu_); - clients_.insert(client); + const std::vector& last_peer_identity() { + grpc_core::MutexLock lock(&mu_); + return last_peer_identity_; } - grpc_core::Mutex clients_mu_; + private: + grpc_core::Mutex mu_; std::set clients_; + std::vector last_peer_identity_; }; class ClientStats { @@ -670,9 +690,6 @@ class AdsServiceImpl : public std::enable_shared_from_this { } else { parent_->seen_v3_client_ = true; } - // Balancer shouldn't receive the call credentials metadata. - EXPECT_EQ(context->client_metadata().find(g_kCallCredsMdKey), - context->client_metadata().end()); // Take a reference of the AdsServiceImpl object, which will go // out of scope when this request handler returns. This ensures // that the parent won't be destroyed until this stream is complete. @@ -1286,22 +1303,26 @@ class LrsServiceImpl : public std::enable_shared_from_this { class TestType { public: TestType(bool use_xds_resolver, bool enable_load_reporting, - bool enable_rds_testing = false, bool use_v2 = false) + bool enable_rds_testing = false, bool use_v2 = false, + bool use_xds_credentials = false) : use_xds_resolver_(use_xds_resolver), enable_load_reporting_(enable_load_reporting), enable_rds_testing_(enable_rds_testing), - use_v2_(use_v2) {} + use_v2_(use_v2), + use_xds_credentials_(use_xds_credentials) {} bool use_xds_resolver() const { return use_xds_resolver_; } bool enable_load_reporting() const { return enable_load_reporting_; } bool enable_rds_testing() const { return enable_rds_testing_; } bool use_v2() const { return use_v2_; } + bool use_xds_credentials() const { return use_xds_credentials_; } std::string AsString() const { std::string retval = (use_xds_resolver_ ? "XdsResolver" : "FakeResolver"); retval += (use_v2_ ? "V2" : "V3"); if (enable_load_reporting_) retval += "WithLoadReporting"; if (enable_rds_testing_) retval += "Rds"; + if (use_xds_credentials_) retval += "XdsCreds"; return retval; } @@ -1310,8 +1331,163 @@ class TestType { const bool enable_load_reporting_; const bool enable_rds_testing_; const bool use_v2_; + const bool use_xds_credentials_; }; +std::string ReadFile(const char* file_path) { + grpc_slice slice; + GPR_ASSERT( + GRPC_LOG_IF_ERROR("load_file", grpc_load_file(file_path, 0, &slice))); + std::string file_contents(grpc_core::StringViewFromSlice(slice)); + grpc_slice_unref(slice); + return file_contents; +} + +grpc_core::PemKeyCertPairList ReadTlsIdentityPair(const char* key_path, + const char* cert_path) { + grpc_ssl_pem_key_cert_pair* ssl_pair = + static_cast( + gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair))); + ssl_pair->private_key = gpr_strdup(ReadFile(key_path).c_str()); + ssl_pair->cert_chain = gpr_strdup(ReadFile(cert_path).c_str()); + return grpc_core::PemKeyCertPairList{grpc_core::PemKeyCertPair(ssl_pair)}; +} + +// Based on StaticDataCertificateProvider, but provides alternate certificates +// if the certificate name is not empty. +class FakeCertificateProvider final : public grpc_tls_certificate_provider { + public: + struct CertData { + std::string root_certificate; + grpc_core::PemKeyCertPairList identity_key_cert_pairs; + }; + + using CertDataMap = std::map; + + explicit FakeCertificateProvider(CertDataMap cert_data_map) + : distributor_( + grpc_core::MakeRefCounted()), + cert_data_map_(std::move(cert_data_map)) { + distributor_->SetWatchStatusCallback([this](std::string cert_name, + bool root_being_watched, + bool identity_being_watched) { + if (!root_being_watched && !identity_being_watched) return; + auto it = cert_data_map_.find(cert_name); + if (it == cert_data_map_.end()) { + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING( + absl::StrCat("No certificates available for cert_name \"", + cert_name, "\"") + .c_str()); + distributor_->SetErrorForCert(cert_name, GRPC_ERROR_REF(error), + GRPC_ERROR_REF(error)); + GRPC_ERROR_UNREF(error); + } else { + absl::optional root_certificate; + absl::optional pem_key_cert_pairs; + if (root_being_watched) { + root_certificate = cert_data_map_[cert_name].root_certificate; + } + if (identity_being_watched) { + pem_key_cert_pairs = + cert_data_map_[cert_name].identity_key_cert_pairs; + } + distributor_->SetKeyMaterials(cert_name, std::move(root_certificate), + std::move(pem_key_cert_pairs)); + } + }); + } + + ~FakeCertificateProvider() override { + distributor_->SetWatchStatusCallback(nullptr); + } + + grpc_core::RefCountedPtr distributor() + const override { + return distributor_; + } + + private: + grpc_core::RefCountedPtr distributor_; + CertDataMap cert_data_map_; +}; + +class FakeCertificateProviderFactory + : public grpc_core::CertificateProviderFactory { + public: + class Config : public grpc_core::CertificateProviderFactory::Config { + public: + explicit Config(const char* name) : name_(name) {} + + const char* name() const override { return name_; } + + std::string ToString() const override { return "{}"; } + + private: + const char* name_; + }; + + FakeCertificateProviderFactory( + const char* name, FakeCertificateProvider::CertDataMap** cert_data_map) + : name_(name), cert_data_map_(cert_data_map) { + GPR_ASSERT(cert_data_map != nullptr); + } + + const char* name() const override { return name_; } + + grpc_core::RefCountedPtr + CreateCertificateProviderConfig(const grpc_core::Json& config_json, + grpc_error** error) override { + return grpc_core::MakeRefCounted(name_); + } + + grpc_core::RefCountedPtr + CreateCertificateProvider( + grpc_core::RefCountedPtr + config) override { + return grpc_core::MakeRefCounted( + *cert_data_map_ == nullptr ? FakeCertificateProvider::CertDataMap() + : *(*cert_data_map_)); + } + + private: + const char* name_; + FakeCertificateProvider::CertDataMap** cert_data_map_; +}; + +// Global variables for each provider. +FakeCertificateProvider::CertDataMap* g_fake1_cert_data_map = nullptr; +FakeCertificateProvider::CertDataMap* g_fake2_cert_data_map = nullptr; + +int ServerAuthCheckSchedule(void* /* config_user_data */, + grpc_tls_server_authorization_check_arg* arg) { + arg->success = 1; + arg->status = GRPC_STATUS_OK; + return 0; /* synchronous check */ +} + +std::shared_ptr CreateTlsFallbackCredentials() { + // TODO(yashykt): Switch to using C++ API once b/173823806 is fixed. + grpc_tls_credentials_options* options = grpc_tls_credentials_options_create(); + grpc_tls_credentials_options_set_server_verification_option( + options, GRPC_TLS_SKIP_HOSTNAME_VERIFICATION); + grpc_tls_credentials_options_set_certificate_provider( + options, + grpc_core::MakeRefCounted( + ReadFile(kCaCertPath), + ReadTlsIdentityPair(kServerKeyPath, kServerCertPath)) + .get()); + grpc_tls_credentials_options_watch_root_certs(options); + grpc_tls_credentials_options_watch_identity_key_cert_pairs(options); + grpc_tls_server_authorization_check_config* check_config = + grpc_tls_server_authorization_check_config_create( + nullptr, ServerAuthCheckSchedule, nullptr, nullptr); + grpc_tls_credentials_options_set_server_authorization_check_config( + options, check_config); + auto channel_creds = std::make_shared( + grpc_tls_credentials_create(options)); + return channel_creds; +} + class XdsEnd2endTest : public ::testing::TestWithParam { protected: XdsEnd2endTest(size_t num_backends, size_t num_balancers, @@ -1457,18 +1633,12 @@ class XdsEnd2endTest : public ::testing::TestWithParam { } std::string uri = absl::StrCat( GetParam().use_xds_resolver() ? "xds" : "fake", ":///", server_name); - // TODO(dgq): templatize tests to run everything using both secure and - // insecure channel credentials. - grpc_channel_credentials* channel_creds = - grpc_fake_transport_security_credentials_create(); - grpc_call_credentials* call_creds = grpc_md_only_test_credentials_create( - g_kCallCredsMdKey, g_kCallCredsMdValue, false); - std::shared_ptr creds( - new SecureChannelCredentials(grpc_composite_channel_credentials_create( - channel_creds, call_creds, nullptr))); - call_creds->Unref(); - channel_creds->Unref(); - return ::grpc::CreateCustomChannel(uri, creds, args); + std::shared_ptr channel_creds = + GetParam().use_xds_credentials() + ? experimental::XdsCredentials(CreateTlsFallbackCredentials()) + : std::make_shared( + grpc_fake_transport_security_credentials_create()); + return ::grpc::CreateCustomChannel(uri, channel_creds, args); } enum RpcService { @@ -1901,9 +2071,7 @@ class XdsEnd2endTest : public ::testing::TestWithParam { std::ostringstream server_address; server_address << "localhost:" << port_; ServerBuilder builder; - std::shared_ptr creds(new SecureServerCredentials( - grpc_fake_transport_security_server_credentials_create())); - builder.AddListeningPort(server_address.str(), creds); + builder.AddListeningPort(server_address.str(), Credentials()); RegisterAllServices(&builder); server_ = builder.BuildAndStart(); cond->Signal(); @@ -1919,6 +2087,11 @@ class XdsEnd2endTest : public ::testing::TestWithParam { running_ = false; } + virtual std::shared_ptr Credentials() { + return std::make_shared( + grpc_fake_transport_security_server_credentials_create()); + } + int port() const { return port_; } private: @@ -1949,6 +2122,27 @@ class XdsEnd2endTest : public ::testing::TestWithParam { return &backend_service2_; } + std::shared_ptr Credentials() override { + if (GetParam().use_xds_credentials()) { + std::string root_cert = ReadFile(kCaCertPath); + std::string identity_cert = ReadFile(kServerCertPath); + std::string private_key = ReadFile(kServerKeyPath); + std::vector identity_key_cert_pairs = + {{private_key, identity_cert}}; + auto certificate_provider = + std::make_shared( + root_cert, identity_key_cert_pairs); + grpc::experimental::TlsServerCredentialsOptions options( + certificate_provider); + options.watch_root_certs(); + options.watch_identity_key_cert_pairs(); + options.set_cert_request_type( + GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY); + return grpc::experimental::TlsServerCredentials(options); + } + return ServerThread::Credentials(); + } + private: void RegisterAllServices(ServerBuilder* builder) override { builder->RegisterService(&backend_service_); @@ -5075,6 +5269,367 @@ TEST_P(CdsTest, WrongLrsServer) { EXPECT_EQ(response_state.error_message, "LRS ConfigSource is not self."); } +class XdsSecurityTest : public BasicTest { + protected: + static void SetUpTestCase() { + gpr_setenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT", "true"); + grpc_core::CertificateProviderRegistry::RegisterCertificateProviderFactory( + absl::make_unique( + "fake1", &g_fake1_cert_data_map)); + grpc_core::CertificateProviderRegistry::RegisterCertificateProviderFactory( + absl::make_unique( + "fake2", &g_fake2_cert_data_map)); + BasicTest::SetUpTestCase(); + } + + static void TearDownTestCase() { + BasicTest::TearDownTestCase(); + gpr_unsetenv("GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT"); + } + + void SetUp() override { + BasicTest::SetUp(); + root_cert_ = ReadFile(kCaCertPath); + bad_root_cert_ = ReadFile(kBadClientCertPath); + identity_pair_1_ = ReadTlsIdentityPair(kClientKeyPath, kClientCertPath); + identity_pair_2_ = ReadTlsIdentityPair(kServerKeyPath, kServerCertPath); + bad_identity_pair_ = + ReadTlsIdentityPair(kBadClientKeyPath, kBadClientCertPath); + authenticated_identity_1_ = {"testclient"}; + authenticated_identity_2_ = {"*.test.google.fr", "waterzooi.test.google.be", + "*.test.youtube.com", "192.168.1.3"}; + AdsServiceImpl::EdsResourceArgs args({ + {"locality0", GetBackendPorts(0, 1)}, + }); + balancers_[0]->ads_service()->SetEdsResource( + BuildEdsResource(args, DefaultEdsServiceName())); + SetNextResolutionForLbChannelAllBalancers(); + } + + // Sends CDS updates with the new security configuration and verifies that + // after propagation, this new configuration is used for connections. If \a + // identity_instance_name and \a root_instance_name are both empty, + // connections are expected to use fallback credentials. + void UpdateAndVerifyXdsSecurityConfiguration( + absl::string_view root_instance_name, + absl::string_view root_certificate_name, + absl::string_view identity_instance_name, + absl::string_view identity_certificate_name, + const std::vector& expected_authenticated_identity, + bool test_expects_failure = false) { + auto cluster = default_cluster_; + if (!identity_instance_name.empty() || !root_instance_name.empty()) { + auto* transport_socket = cluster.mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + UpstreamTlsContext upstream_tls_context; + if (!identity_instance_name.empty()) { + upstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_certificate_provider_instance() + ->set_instance_name(std::string(identity_instance_name)); + upstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_certificate_provider_instance() + ->set_certificate_name(std::string(identity_certificate_name)); + } + if (!root_instance_name.empty()) { + upstream_tls_context.mutable_common_tls_context() + ->mutable_combined_validation_context() + ->mutable_validation_context_certificate_provider_instance() + ->set_instance_name(std::string(root_instance_name)); + upstream_tls_context.mutable_common_tls_context() + ->mutable_combined_validation_context() + ->mutable_validation_context_certificate_provider_instance() + ->set_certificate_name(std::string(root_certificate_name)); + } + transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); + } + balancers_[0]->ads_service()->SetCdsResource(cluster); + // The updates might take time to have an effect, so use a retry loop. + constexpr int kRetryCount = 10; + int num_tries = 0; + for (; num_tries < kRetryCount; num_tries++) { + // Give some time for the updates to propagate. + gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(100)); + ShutdownBackend(0); + StartBackend(0); + ResetBackendCounters(); + if (test_expects_failure) { + if (!SendRpc().ok()) break; + } else { + WaitForBackend(0); + if (SendRpc().ok() && + backends_[0]->backend_service()->request_count() == 1UL && + backends_[0]->backend_service()->last_peer_identity() == + expected_authenticated_identity) { + break; + } + } + } + EXPECT_TRUE(num_tries < kRetryCount); + } + + std::string root_cert_; + std::string bad_root_cert_; + grpc_core::PemKeyCertPairList identity_pair_1_; + grpc_core::PemKeyCertPairList identity_pair_2_; + grpc_core::PemKeyCertPairList bad_identity_pair_; + std::vector authenticated_identity_1_; + std::vector authenticated_identity_2_; +}; + +TEST_P(XdsSecurityTest, UnknownRootCertificateProvider) { + auto cluster = default_cluster_; + auto* transport_socket = cluster.mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + UpstreamTlsContext upstream_tls_context; + upstream_tls_context.mutable_common_tls_context() + ->mutable_combined_validation_context() + ->mutable_validation_context_certificate_provider_instance() + ->set_instance_name("unknown"); + transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); + balancers_[0]->ads_service()->SetCdsResource(cluster); + CheckRpcSendFailure(1, RpcOptions(), StatusCode::UNAVAILABLE); +} + +TEST_P(XdsSecurityTest, UnknownIdentityCertificateProvider) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + auto cluster = default_cluster_; + auto* transport_socket = cluster.mutable_transport_socket(); + transport_socket->set_name("envoy.transport_sockets.tls"); + UpstreamTlsContext upstream_tls_context; + upstream_tls_context.mutable_common_tls_context() + ->mutable_tls_certificate_certificate_provider_instance() + ->set_instance_name("unknown"); + upstream_tls_context.mutable_common_tls_context() + ->mutable_combined_validation_context() + ->mutable_validation_context_certificate_provider_instance() + ->set_instance_name("fake_plugin1"); + transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); + balancers_[0]->ads_service()->SetCdsResource(cluster); + CheckRpcSendFailure(1, RpcOptions(), StatusCode::UNAVAILABLE); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsConfiguration) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsConfigurationWithRootPluginUpdate) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + FakeCertificateProvider::CertDataMap fake2_cert_map = { + {"", {bad_root_cert_, bad_identity_pair_}}}; + g_fake2_cert_data_map = &fake2_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin2", "", + "fake_plugin1" /* bad root */, "", {}, + true /* failure */); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + g_fake1_cert_data_map = nullptr; + g_fake2_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsConfigurationWithIdentityPluginUpdate) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + FakeCertificateProvider::CertDataMap fake2_cert_map = { + {"", {root_cert_, identity_pair_2_}}}; + g_fake2_cert_data_map = &fake2_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin2", + "", authenticated_identity_2_); + g_fake1_cert_data_map = nullptr; + g_fake2_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsConfigurationWithBothPluginsUpdated) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + FakeCertificateProvider::CertDataMap fake2_cert_map = { + {"", {bad_root_cert_, bad_identity_pair_}}, + {"good", {root_cert_, identity_pair_2_}}}; + g_fake2_cert_data_map = &fake2_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin2", "", "fake_plugin2", + "", {}, true /* failure */); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin2", "good", + "fake_plugin2", "good", + authenticated_identity_2_); + g_fake1_cert_data_map = nullptr; + g_fake2_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsConfigurationWithRootCertificateNameUpdate) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}, + {"bad", {bad_root_cert_, bad_identity_pair_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "bad", "fake_plugin1", + "", {}, true /* failure */); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, + TestMtlsConfigurationWithIdentityCertificateNameUpdate) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}, + {"bad", {bad_root_cert_, bad_identity_pair_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "bad", {}, true /* failure */); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, + TestMtlsConfigurationWithIdentityCertificateNameUpdateGoodCerts) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}, + {"good", {root_cert_, identity_pair_2_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "good", authenticated_identity_2_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsConfigurationWithBothCertificateNamesUpdated) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}, + {"bad", {bad_root_cert_, bad_identity_pair_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "bad", "fake_plugin1", + "bad", {}, true /* failure */); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestTlsConfiguration) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", + {} /* unauthenticated */); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestTlsConfigurationWithRootCertificateNameUpdate) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}, + {"bad", {bad_root_cert_, bad_identity_pair_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", + {} /* unauthenticated */); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "bad", "", "", {}, + true /* failure */); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestTlsConfigurationWithRootPluginUpdate) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + FakeCertificateProvider::CertDataMap fake2_cert_map = { + {"", {bad_root_cert_, bad_identity_pair_}}}; + g_fake2_cert_data_map = &fake2_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", + {} /* unauthenticated */); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin2", "", "", "", {}, + true /* failure */); + g_fake1_cert_data_map = nullptr; + g_fake2_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestFallbackConfiguration) { + UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", + authenticated_identity_2_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsToTls) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", + {} /* unauthenticated */); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestMtlsToFallback) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", + authenticated_identity_2_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestTlsToMtls) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", + {} /* unauthenticated */); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestTlsToFallback) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", + {} /* unauthenticated */); + UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", + authenticated_identity_2_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestFallbackToMtls) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", + authenticated_identity_2_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", + "", authenticated_identity_1_); + g_fake1_cert_data_map = nullptr; +} + +TEST_P(XdsSecurityTest, TestFallbackToTls) { + FakeCertificateProvider::CertDataMap fake1_cert_map = { + {"", {root_cert_, identity_pair_1_}}}; + g_fake1_cert_data_map = &fake1_cert_map; + UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", + authenticated_identity_2_); + UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", + {} /* unauthenticated */); + g_fake1_cert_data_map = nullptr; +} + using EdsTest = BasicTest; // Tests that EDS client should send a NACK if the EDS update contains @@ -6358,6 +6913,7 @@ std::string TestTypeName(const ::testing::TestParamInfo& info) { // - enable_load_reporting // - enable_rds_testing = false // - use_v2 = false +// - use_xds_credentials = false INSTANTIATE_TEST_SUITE_P(XdsTest, BasicTest, ::testing::Values(TestType(false, true), @@ -6396,6 +6952,15 @@ INSTANTIATE_TEST_SUITE_P(XdsTest, CdsTest, TestType(true, true)), &TestTypeName); +// CDS depends on XdsResolver. +// Security depends on v3. +// Not enabling load reporting or RDS, since those are irrelevant to these +// tests. +INSTANTIATE_TEST_SUITE_P(XdsTest, XdsSecurityTest, + ::testing::Values(TestType(true, false, false, false, + true)), + &TestTypeName); + // EDS could be tested with or without XdsResolver, but the tests would // be the same either way, so we test it only with XdsResolver. INSTANTIATE_TEST_SUITE_P(XdsTest, EdsTest,