Add XdsCertificateProvider

pull/24579/head
Yash Tibrewal 4 years ago
parent 93fc14a17f
commit e11a670736
  1. 6
      BUILD
  2. 3
      BUILD.gn
  3. 43
      CMakeLists.txt
  4. 2
      Makefile
  5. 16
      build_autogenerated.yaml
  6. 1
      config.m4
  7. 1
      config.w32
  8. 3
      gRPC-C++.podspec
  9. 4
      gRPC-Core.podspec
  10. 4
      grpc.gemspec
  11. 2
      grpc.gyp
  12. 4
      package.xml
  13. 240
      src/core/ext/xds/xds_certificate_provider.cc
  14. 74
      src/core/ext/xds/xds_certificate_provider.h
  15. 8
      src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.cc
  16. 1
      src/python/grpcio/grpc_core_dependencies.py
  17. 12
      test/core/xds/BUILD
  18. 529
      test/core/xds/xds_certificate_provider_test.cc
  19. 2
      tools/doxygen/Doxyfile.c++.internal
  20. 2
      tools/doxygen/Doxyfile.core.internal
  21. 24
      tools/run_tests/generated/tests.json

@ -1337,14 +1337,20 @@ grpc_cc_library(
srcs = [
"src/core/ext/xds/certificate_provider_registry.cc",
"src/core/ext/xds/certificate_provider_store.cc",
"src/core/ext/xds/xds_certificate_provider.cc",
"src/core/lib/security/credentials/xds/xds_credentials.cc",
],
hdrs = [
"src/core/ext/xds/certificate_provider_factory.h",
"src/core/ext/xds/certificate_provider_registry.h",
"src/core/ext/xds/certificate_provider_store.h",
"src/core/ext/xds/xds_certificate_provider.h",
"src/core/lib/security/credentials/xds/xds_credentials.h",
],
external_deps = [
"absl/functional:bind_front",
],
language = "c++",
deps = [
"grpc_secure",
],

@ -731,6 +731,8 @@ config("grpc_config") {
"src/core/ext/xds/xds_api.h",
"src/core/ext/xds/xds_bootstrap.cc",
"src/core/ext/xds/xds_bootstrap.h",
"src/core/ext/xds/xds_certificate_provider.cc",
"src/core/ext/xds/xds_certificate_provider.h",
"src/core/ext/xds/xds_channel_args.h",
"src/core/ext/xds/xds_client.cc",
"src/core/ext/xds/xds_client.h",
@ -1207,6 +1209,7 @@ config("grpc_config") {
":absl/types:optional",
":absl/strings:strings",
":absl/status:status",
":absl/functional:bind_front",
":absl/container:inlined_vector",
":absl/container:flat_hash_set",
"//third_party/cares",

@ -109,6 +109,7 @@ set(gRPC_ABSL_USED_TARGETS
absl_bad_variant_access
absl_base
absl_base_internal
absl_bind_front
absl_bits
absl_city
absl_civil_time
@ -931,6 +932,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx writes_per_rpc_test)
endif()
add_dependencies(buildtests_cxx xds_bootstrap_test)
add_dependencies(buildtests_cxx xds_certificate_provider_test)
add_dependencies(buildtests_cxx xds_credentials_end2end_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx xds_end2end_test)
@ -1701,6 +1703,7 @@ add_library(grpc
src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc
src/core/ext/xds/xds_api.cc
src/core/ext/xds/xds_bootstrap.cc
src/core/ext/xds/xds_certificate_provider.cc
src/core/ext/xds/xds_client.cc
src/core/ext/xds/xds_client_stats.cc
src/core/lib/avl/avl.cc
@ -1992,6 +1995,7 @@ target_link_libraries(grpc
absl::optional
absl::strings
absl::status
absl::bind_front
absl::inlined_vector
absl::flat_hash_set
)
@ -15248,6 +15252,45 @@ target_link_libraries(xds_bootstrap_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(xds_certificate_provider_test
test/core/xds/xds_certificate_provider_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(xds_certificate_provider_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(xds_certificate_provider_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc
gpr
address_sorting
upb
${_gRPC_GFLAGS_LIBRARIES}
)
endif()
if(gRPC_BUILD_TESTS)

@ -2111,6 +2111,7 @@ LIBGRPC_SRC = \
src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \
src/core/ext/xds/xds_api.cc \
src/core/ext/xds/xds_bootstrap.cc \
src/core/ext/xds/xds_certificate_provider.cc \
src/core/ext/xds/xds_client.cc \
src/core/ext/xds/xds_client_stats.cc \
src/core/lib/avl/avl.cc \
@ -4775,6 +4776,7 @@ src/core/ext/xds/certificate_provider_store.cc: $(OPENSSL_DEP)
src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc: $(OPENSSL_DEP)
src/core/ext/xds/xds_api.cc: $(OPENSSL_DEP)
src/core/ext/xds/xds_bootstrap.cc: $(OPENSSL_DEP)
src/core/ext/xds/xds_certificate_provider.cc: $(OPENSSL_DEP)
src/core/ext/xds/xds_client.cc: $(OPENSSL_DEP)
src/core/ext/xds/xds_client_stats.cc: $(OPENSSL_DEP)
src/core/lib/http/httpcli_security_connector.cc: $(OPENSSL_DEP)

@ -632,6 +632,7 @@ libs:
- src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h
- src/core/ext/xds/xds_api.h
- src/core/ext/xds/xds_bootstrap.h
- src/core/ext/xds/xds_certificate_provider.h
- src/core/ext/xds/xds_channel_args.h
- src/core/ext/xds/xds_client.h
- src/core/ext/xds/xds_client_stats.h
@ -1128,6 +1129,7 @@ libs:
- src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc
- src/core/ext/xds/xds_api.cc
- src/core/ext/xds/xds_bootstrap.cc
- src/core/ext/xds/xds_certificate_provider.cc
- src/core/ext/xds/xds_client.cc
- src/core/ext/xds/xds_client_stats.cc
- src/core/lib/avl/avl.cc
@ -1381,6 +1383,7 @@ libs:
- absl/types:optional
- absl/strings:strings
- absl/status:status
- absl/functional:bind_front
- absl/container:inlined_vector
- absl/container:flat_hash_set
baselib: true
@ -7768,6 +7771,19 @@ targets:
- gpr
- address_sorting
- upb
- name: xds_certificate_provider_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/xds/xds_certificate_provider_test.cc
deps:
- grpc_test_util
- grpc
- gpr
- address_sorting
- upb
- name: xds_credentials_end2end_test
gtest: true
build: test

@ -315,6 +315,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc \
src/core/ext/xds/xds_api.cc \
src/core/ext/xds/xds_bootstrap.cc \
src/core/ext/xds/xds_certificate_provider.cc \
src/core/ext/xds/xds_client.cc \
src/core/ext/xds/xds_client_stats.cc \
src/core/lib/avl/avl.cc \

@ -282,6 +282,7 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\xds\\google_mesh_ca_certificate_provider_factory.cc " +
"src\\core\\ext\\xds\\xds_api.cc " +
"src\\core\\ext\\xds\\xds_bootstrap.cc " +
"src\\core\\ext\\xds\\xds_certificate_provider.cc " +
"src\\core\\ext\\xds\\xds_client.cc " +
"src\\core\\ext\\xds\\xds_client_stats.cc " +
"src\\core\\lib\\avl\\avl.cc " +

@ -188,6 +188,7 @@ Pod::Spec.new do |s|
ss.dependency 'abseil/base/base', abseil_version
ss.dependency 'abseil/container/flat_hash_set', abseil_version
ss.dependency 'abseil/container/inlined_vector', abseil_version
ss.dependency 'abseil/functional/bind_front', abseil_version
ss.dependency 'abseil/memory/memory', abseil_version
ss.dependency 'abseil/status/status', abseil_version
ss.dependency 'abseil/strings/str_format', abseil_version
@ -448,6 +449,7 @@ Pod::Spec.new do |s|
'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h',
'src/core/ext/xds/xds_api.h',
'src/core/ext/xds/xds_bootstrap.h',
'src/core/ext/xds/xds_certificate_provider.h',
'src/core/ext/xds/xds_channel_args.h',
'src/core/ext/xds/xds_client.h',
'src/core/ext/xds/xds_client_stats.h',
@ -1049,6 +1051,7 @@ Pod::Spec.new do |s|
'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h',
'src/core/ext/xds/xds_api.h',
'src/core/ext/xds/xds_bootstrap.h',
'src/core/ext/xds/xds_certificate_provider.h',
'src/core/ext/xds/xds_channel_args.h',
'src/core/ext/xds/xds_client.h',
'src/core/ext/xds/xds_client_stats.h',

@ -177,6 +177,7 @@ Pod::Spec.new do |s|
ss.dependency 'abseil/base/base', abseil_version
ss.dependency 'abseil/container/flat_hash_set', abseil_version
ss.dependency 'abseil/container/inlined_vector', abseil_version
ss.dependency 'abseil/functional/bind_front', abseil_version
ss.dependency 'abseil/memory/memory', abseil_version
ss.dependency 'abseil/status/status', abseil_version
ss.dependency 'abseil/strings/str_format', abseil_version
@ -714,6 +715,8 @@ Pod::Spec.new do |s|
'src/core/ext/xds/xds_api.h',
'src/core/ext/xds/xds_bootstrap.cc',
'src/core/ext/xds/xds_bootstrap.h',
'src/core/ext/xds/xds_certificate_provider.cc',
'src/core/ext/xds/xds_certificate_provider.h',
'src/core/ext/xds/xds_channel_args.h',
'src/core/ext/xds/xds_client.cc',
'src/core/ext/xds/xds_client.h',
@ -1575,6 +1578,7 @@ Pod::Spec.new do |s|
'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.h',
'src/core/ext/xds/xds_api.h',
'src/core/ext/xds/xds_bootstrap.h',
'src/core/ext/xds/xds_certificate_provider.h',
'src/core/ext/xds/xds_channel_args.h',
'src/core/ext/xds/xds_client.h',
'src/core/ext/xds/xds_client_stats.h',

@ -632,6 +632,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/xds/xds_api.h )
s.files += %w( src/core/ext/xds/xds_bootstrap.cc )
s.files += %w( src/core/ext/xds/xds_bootstrap.h )
s.files += %w( src/core/ext/xds/xds_certificate_provider.cc )
s.files += %w( src/core/ext/xds/xds_certificate_provider.h )
s.files += %w( src/core/ext/xds/xds_channel_args.h )
s.files += %w( src/core/ext/xds/xds_client.cc )
s.files += %w( src/core/ext/xds/xds_client.h )
@ -1269,7 +1271,9 @@ Gem::Specification.new do |s|
s.files += %w( third_party/abseil-cpp/absl/debugging/symbolize_elf.inc )
s.files += %w( third_party/abseil-cpp/absl/debugging/symbolize_unimplemented.inc )
s.files += %w( third_party/abseil-cpp/absl/debugging/symbolize_win32.inc )
s.files += %w( third_party/abseil-cpp/absl/functional/bind_front.h )
s.files += %w( third_party/abseil-cpp/absl/functional/function_ref.h )
s.files += %w( third_party/abseil-cpp/absl/functional/internal/front_binder.h )
s.files += %w( third_party/abseil-cpp/absl/functional/internal/function_ref.h )
s.files += %w( third_party/abseil-cpp/absl/hash/hash.h )
s.files += %w( third_party/abseil-cpp/absl/hash/internal/city.cc )

@ -443,6 +443,7 @@
'absl/types:optional',
'absl/strings:strings',
'absl/status:status',
'absl/functional:bind_front',
'absl/container:inlined_vector',
'absl/container:flat_hash_set',
],
@ -722,6 +723,7 @@
'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc',
'src/core/ext/xds/xds_api.cc',
'src/core/ext/xds/xds_bootstrap.cc',
'src/core/ext/xds/xds_certificate_provider.cc',
'src/core/ext/xds/xds_client.cc',
'src/core/ext/xds/xds_client_stats.cc',
'src/core/lib/avl/avl.cc',

@ -612,6 +612,8 @@
<file baseinstalldir="/" name="src/core/ext/xds/xds_api.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_bootstrap.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_bootstrap.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_certificate_provider.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_certificate_provider.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_channel_args.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_client.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_client.h" role="src" />
@ -1271,7 +1273,9 @@
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/debugging/symbolize_elf.inc" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/debugging/symbolize_unimplemented.inc" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/debugging/symbolize_win32.inc" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/functional/bind_front.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/functional/function_ref.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/functional/internal/front_binder.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/functional/internal/function_ref.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/hash/hash.h" role="src" />
<file baseinstalldir="/" name="third_party/abseil-cpp/absl/hash/internal/city.cc" role="src" />

@ -0,0 +1,240 @@
//
//
// Copyright 2020 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.
//
//
#include <grpc/support/port_platform.h>
#include "absl/functional/bind_front.h"
#include "absl/strings/str_cat.h"
#include "src/core/ext/xds/xds_certificate_provider.h"
namespace grpc_core {
namespace {
class RootCertificatesWatcher
: public grpc_tls_certificate_distributor::TlsCertificatesWatcherInterface {
public:
// Takes a ref to \a parent instead of a raw pointer since the watcher is
// owned by the root certificate distributor and not by \a parent. Note that
// presently, the watcher is immediately deleted when
// CancelTlsCertificatesWatch() is called, but that can potentially change in
// the future.
explicit RootCertificatesWatcher(
RefCountedPtr<grpc_tls_certificate_distributor> parent)
: parent_(std::move(parent)) {}
void OnCertificatesChanged(absl::optional<absl::string_view> root_certs,
absl::optional<PemKeyCertPairList>
/* key_cert_pairs */) override {
if (root_certs.has_value()) {
parent_->SetKeyMaterials("", std::string(root_certs.value()),
absl::nullopt);
}
}
void OnError(grpc_error* root_cert_error,
grpc_error* identity_cert_error) override {
if (root_cert_error != GRPC_ERROR_NONE) {
parent_->SetErrorForCert("", root_cert_error /* pass the ref */,
absl::nullopt);
}
GRPC_ERROR_UNREF(identity_cert_error);
}
private:
RefCountedPtr<grpc_tls_certificate_distributor> parent_;
};
class IdentityCertificatesWatcher
: public grpc_tls_certificate_distributor::TlsCertificatesWatcherInterface {
public:
// Takes a ref to \a parent instead of a raw pointer since the watcher is
// owned by the root certificate distributor and not by \a parent. Note that
// presently, the watcher is immediately deleted when
// CancelTlsCertificatesWatch() is called, but that can potentially change in
// the future.
explicit IdentityCertificatesWatcher(
RefCountedPtr<grpc_tls_certificate_distributor> parent)
: parent_(std::move(parent)) {}
void OnCertificatesChanged(
absl::optional<absl::string_view> /* root_certs */,
absl::optional<PemKeyCertPairList> key_cert_pairs) override {
if (key_cert_pairs.has_value()) {
parent_->SetKeyMaterials("", absl::nullopt, key_cert_pairs);
}
}
void OnError(grpc_error* root_cert_error,
grpc_error* identity_cert_error) override {
if (identity_cert_error != GRPC_ERROR_NONE) {
parent_->SetErrorForCert("", absl::nullopt,
identity_cert_error /* pass the ref */);
}
GRPC_ERROR_UNREF(root_cert_error);
}
private:
RefCountedPtr<grpc_tls_certificate_distributor> parent_;
};
} // namespace
XdsCertificateProvider::XdsCertificateProvider(
absl::string_view root_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor> root_cert_distributor,
absl::string_view identity_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor> identity_cert_distributor)
: root_cert_name_(root_cert_name),
identity_cert_name_(identity_cert_name),
root_cert_distributor_(std::move(root_cert_distributor)),
identity_cert_distributor_(std::move(identity_cert_distributor)),
distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()) {
distributor_->SetWatchStatusCallback(
absl::bind_front(&XdsCertificateProvider::WatchStatusCallback, this));
}
void XdsCertificateProvider::UpdateRootCertNameAndDistributor(
absl::string_view root_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor> root_cert_distributor) {
MutexLock lock(&mu_);
root_cert_name_ = std::string(root_cert_name);
if (watching_root_certs_) {
// The root certificates are being watched. Swap out the watcher.
if (root_cert_distributor_ != nullptr) {
root_cert_distributor_->CancelTlsCertificatesWatch(root_cert_watcher_);
}
if (root_cert_distributor != nullptr) {
UpdateRootCertWatcher(root_cert_distributor.get());
} else {
root_cert_watcher_ = nullptr;
distributor_->SetErrorForCert(
"",
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No certificate provider available for root certificates"),
absl::nullopt);
}
}
// Swap out the root certificate distributor
root_cert_distributor_ = std::move(root_cert_distributor);
}
void XdsCertificateProvider::UpdateIdentityCertNameAndDistributor(
absl::string_view identity_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor> identity_cert_distributor) {
MutexLock lock(&mu_);
identity_cert_name_ = std::string(identity_cert_name);
if (watching_identity_certs_) {
// The identity certificates are being watched. Swap out the watcher.
if (identity_cert_distributor_ != nullptr) {
identity_cert_distributor_->CancelTlsCertificatesWatch(
identity_cert_watcher_);
}
if (identity_cert_distributor != nullptr) {
UpdateIdentityCertWatcher(identity_cert_distributor.get());
} else {
identity_cert_watcher_ = nullptr;
distributor_->SetErrorForCert(
"", absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No certificate provider available for identity certificates"));
}
}
// Swap out the identity certificate distributor
identity_cert_distributor_ = std::move(identity_cert_distributor);
}
void XdsCertificateProvider::WatchStatusCallback(std::string cert_name,
bool root_being_watched,
bool identity_being_watched) {
// We aren't specially handling the case where root_cert_distributor is same
// as identity_cert_distributor. Always using two separate watchers
// irrespective of the fact results in a straightforward design, and using a
// single watcher does not seem to provide any benefit other than cutting down
// on the number of callbacks.
MutexLock lock(&mu_);
if (!cert_name.empty()) {
grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrCat("Illegal certificate name: \'", cert_name,
"\'. Should be empty.")
.c_str());
distributor_->SetErrorForCert(cert_name, GRPC_ERROR_REF(error),
GRPC_ERROR_REF(error));
GRPC_ERROR_UNREF(error);
return;
}
if (root_being_watched && !watching_root_certs_) {
// We need to start watching root certs.
watching_root_certs_ = true;
if (root_cert_distributor_ == nullptr) {
distributor_->SetErrorForCert(
"",
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No certificate provider available for root certificates"),
absl::nullopt);
} else {
UpdateRootCertWatcher(root_cert_distributor_.get());
}
} else if (!root_being_watched && watching_root_certs_) {
// We need to cancel root certs watch.
watching_root_certs_ = false;
if (root_cert_distributor_ != nullptr) {
root_cert_distributor_->CancelTlsCertificatesWatch(root_cert_watcher_);
root_cert_watcher_ = nullptr;
}
GPR_ASSERT(root_cert_watcher_ == nullptr);
}
if (identity_being_watched && !watching_identity_certs_) {
watching_identity_certs_ = true;
if (identity_cert_distributor_ == nullptr) {
distributor_->SetErrorForCert(
"", absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No certificate provider available for identity certificates"));
} else {
UpdateIdentityCertWatcher(identity_cert_distributor_.get());
}
} else if (!identity_being_watched && watching_identity_certs_) {
watching_identity_certs_ = false;
if (identity_cert_distributor_ != nullptr) {
identity_cert_distributor_->CancelTlsCertificatesWatch(
identity_cert_watcher_);
identity_cert_watcher_ = nullptr;
}
GPR_ASSERT(identity_cert_watcher_ == nullptr);
}
}
void XdsCertificateProvider::UpdateRootCertWatcher(
grpc_tls_certificate_distributor* root_cert_distributor) {
auto watcher = absl::make_unique<RootCertificatesWatcher>(distributor());
root_cert_watcher_ = watcher.get();
root_cert_distributor->WatchTlsCertificates(std::move(watcher),
root_cert_name_, absl::nullopt);
}
void XdsCertificateProvider::UpdateIdentityCertWatcher(
grpc_tls_certificate_distributor* identity_cert_distributor) {
auto watcher = absl::make_unique<IdentityCertificatesWatcher>(distributor());
identity_cert_watcher_ = watcher.get();
identity_cert_distributor->WatchTlsCertificates(
std::move(watcher), absl::nullopt, identity_cert_name_);
}
} // namespace grpc_core

@ -0,0 +1,74 @@
//
//
// Copyright 2020 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.
//
//
#ifndef GRPC_CORE_EXT_XDS_XDS_CERTIFICATE_PROVIDER_H
#define GRPC_CORE_EXT_XDS_XDS_CERTIFICATE_PROVIDER_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
namespace grpc_core {
class XdsCertificateProvider : public grpc_tls_certificate_provider {
public:
XdsCertificateProvider(
absl::string_view root_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor> root_cert_distributor,
absl::string_view identity_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor>
identity_cert_distributor);
void UpdateRootCertNameAndDistributor(
absl::string_view root_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor> root_cert_distributor);
void UpdateIdentityCertNameAndDistributor(
absl::string_view identity_cert_name,
RefCountedPtr<grpc_tls_certificate_distributor>
identity_cert_distributor);
grpc_core::RefCountedPtr<grpc_tls_certificate_distributor> distributor()
const override {
return distributor_;
}
private:
void WatchStatusCallback(std::string cert_name, bool root_being_watched,
bool identity_being_watched);
void UpdateRootCertWatcher(
grpc_tls_certificate_distributor* root_cert_distributor);
void UpdateIdentityCertWatcher(
grpc_tls_certificate_distributor* identity_cert_distributor);
Mutex mu_;
bool watching_root_certs_ = false;
bool watching_identity_certs_ = false;
std::string root_cert_name_;
std::string identity_cert_name_;
RefCountedPtr<grpc_tls_certificate_distributor> root_cert_distributor_;
RefCountedPtr<grpc_tls_certificate_distributor> identity_cert_distributor_;
RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
grpc_tls_certificate_distributor::TlsCertificatesWatcherInterface*
root_cert_watcher_ = nullptr;
grpc_tls_certificate_distributor::TlsCertificatesWatcherInterface*
identity_cert_watcher_ = nullptr;
};
} // namespace grpc_core
#endif // GRPC_CORE_EXT_XDS_XDS_CERTIFICATE_PROVIDER_H

@ -46,7 +46,9 @@ void grpc_tls_certificate_distributor::SetKeyMaterials(
} else if (watcher_it->second.identity_cert_name.has_value()) {
auto& identity_cert_info =
certificate_info_map_[*watcher_it->second.identity_cert_name];
pem_key_cert_pairs_to_report = identity_cert_info.pem_key_cert_pairs;
if (!identity_cert_info.pem_key_cert_pairs.empty()) {
pem_key_cert_pairs_to_report = identity_cert_info.pem_key_cert_pairs;
}
}
watcher_ptr->OnCertificatesChanged(
pem_root_certs, std::move(pem_key_cert_pairs_to_report));
@ -70,7 +72,9 @@ void grpc_tls_certificate_distributor::SetKeyMaterials(
} else if (watcher_it->second.root_cert_name.has_value()) {
auto& root_cert_info =
certificate_info_map_[*watcher_it->second.root_cert_name];
pem_root_certs_to_report = root_cert_info.pem_root_certs;
if (!root_cert_info.pem_root_certs.empty()) {
pem_root_certs_to_report = root_cert_info.pem_root_certs;
}
}
watcher_ptr->OnCertificatesChanged(pem_root_certs_to_report,
pem_key_cert_pairs);

@ -291,6 +291,7 @@ CORE_SOURCE_FILES = [
'src/core/ext/xds/google_mesh_ca_certificate_provider_factory.cc',
'src/core/ext/xds/xds_api.cc',
'src/core/ext/xds/xds_bootstrap.cc',
'src/core/ext/xds/xds_certificate_provider.cc',
'src/core/ext/xds/xds_client.cc',
'src/core/ext/xds/xds_client_stats.cc',
'src/core/lib/avl/avl.cc',

@ -55,3 +55,15 @@ grpc_cc_test(
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "xds_certificate_provider_test",
srcs = ["xds_certificate_provider_test.cc"],
external_deps = ["gtest"],
language = "C++",
deps = [
"//:gpr",
"//:grpc",
"//test/core/util:grpc_test_util",
],
)

@ -0,0 +1,529 @@
//
//
// Copyright 2020 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.
//
//
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/core/ext/xds/xds_certificate_provider.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
namespace {
constexpr const char* kRootCert1 = "root_cert_1_contents";
constexpr const char* kRootCert2 = "root_cert_2_contents";
constexpr const char* kIdentityCert1PrivateKey = "identity_private_key_1";
constexpr const char* kIdentityCert1 = "identity_cert_1_contents";
constexpr const char* kIdentityCert2PrivateKey = "identity_private_key_2";
constexpr const char* kIdentityCert2 = "identity_cert_2_contents";
constexpr const char* kRootErrorMessage = "root_error_message";
constexpr const char* kIdentityErrorMessage = "identity_error_message";
PemKeyCertPairList MakeKeyCertPairs(const char* private_key,
const char* certs) {
if (strcmp(private_key, "") == 0 && strcmp(certs, "") == 0) {
return {};
}
grpc_ssl_pem_key_cert_pair* ssl_pair =
static_cast<grpc_ssl_pem_key_cert_pair*>(
gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair)));
ssl_pair->private_key = gpr_strdup(private_key);
ssl_pair->cert_chain = gpr_strdup(certs);
return PemKeyCertPairList{PemKeyCertPair(ssl_pair)};
}
PemKeyCertPairList MakeKeyCertPairsType1() {
return MakeKeyCertPairs(kIdentityCert1PrivateKey, kIdentityCert1);
}
PemKeyCertPairList MakeKeyCertPairsType2() {
return MakeKeyCertPairs(kIdentityCert2PrivateKey, kIdentityCert2);
}
class TestCertificatesWatcher
: public grpc_tls_certificate_distributor::TlsCertificatesWatcherInterface {
public:
~TestCertificatesWatcher() override {
GRPC_ERROR_UNREF(root_cert_error_);
GRPC_ERROR_UNREF(identity_cert_error_);
}
void OnCertificatesChanged(
absl::optional<absl::string_view> root_certs,
absl::optional<PemKeyCertPairList> key_cert_pairs) override {
if (root_certs.has_value()) {
if (!root_certs_.has_value() ||
(root_certs_.has_value() &&
std::string(root_certs.value()) != root_certs_.value())) {
GRPC_ERROR_UNREF(root_cert_error_);
root_cert_error_ = GRPC_ERROR_NONE;
}
root_certs_.emplace(std::string(root_certs.value()));
}
if (key_cert_pairs.has_value()) {
if (key_cert_pairs != key_cert_pairs_) {
GRPC_ERROR_UNREF(identity_cert_error_);
identity_cert_error_ = GRPC_ERROR_NONE;
key_cert_pairs_ = key_cert_pairs;
}
}
}
void OnError(grpc_error* root_cert_error,
grpc_error* identity_cert_error) override {
GRPC_ERROR_UNREF(root_cert_error_);
root_cert_error_ = root_cert_error;
GRPC_ERROR_UNREF(identity_cert_error_);
identity_cert_error_ = identity_cert_error;
}
const absl::optional<std::string>& root_certs() const { return root_certs_; }
const absl::optional<PemKeyCertPairList>& key_cert_pairs() const {
return key_cert_pairs_;
}
grpc_error* root_cert_error() const { return root_cert_error_; }
grpc_error* identity_cert_error() const { return identity_cert_error_; }
private:
absl::optional<std::string> root_certs_;
absl::optional<PemKeyCertPairList> key_cert_pairs_;
grpc_error* root_cert_error_ = GRPC_ERROR_NONE;
grpc_error* identity_cert_error_ = GRPC_ERROR_NONE;
};
TEST(
XdsCertificateProviderTest,
RootCertDistributorDifferentFromIdentityCertDistributorDifferentCertNames) {
auto root_cert_distributor =
MakeRefCounted<grpc_tls_certificate_distributor>();
auto identity_cert_distributor =
MakeRefCounted<grpc_tls_certificate_distributor>();
XdsCertificateProvider provider("root", root_cert_distributor, "identity",
identity_cert_distributor);
auto* watcher = new TestCertificatesWatcher;
provider.distributor()->WatchTlsCertificates(
std::unique_ptr<TestCertificatesWatcher>(watcher), "", "");
EXPECT_EQ(watcher->root_certs(), absl::nullopt);
EXPECT_EQ(watcher->key_cert_pairs(), absl::nullopt);
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Update both root certs and identity certs
root_cert_distributor->SetKeyMaterials("root", kRootCert1, absl::nullopt);
identity_cert_distributor->SetKeyMaterials("identity", absl::nullopt,
MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for just root certs
root_cert_distributor->SetKeyMaterials(
"root", kRootCert2,
MakeKeyCertPairsType2() /* does not have an effect */);
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for identity certs
identity_cert_distributor->SetKeyMaterials(
"identity", kRootCert1 /* does not have an effect */,
MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Set error for both root and identity
root_cert_distributor->SetErrorForCert(
"root", GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
identity_cert_distributor->SetErrorForCert(
"identity", absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(kRootErrorMessage));
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for root certs. Test that the root cert error is reset.
root_cert_distributor->SetKeyMaterials("root", kRootCert1, absl::nullopt);
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for identity certs. Test that the identity cert error is
// reset.
identity_cert_distributor->SetKeyMaterials("identity", absl::nullopt,
MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
}
TEST(XdsCertificateProviderTest,
RootCertDistributorDifferentFromIdentityCertDistributorSameCertNames) {
auto root_cert_distributor =
MakeRefCounted<grpc_tls_certificate_distributor>();
auto identity_cert_distributor =
MakeRefCounted<grpc_tls_certificate_distributor>();
XdsCertificateProvider provider("test", root_cert_distributor, "test",
identity_cert_distributor);
auto* watcher = new TestCertificatesWatcher;
provider.distributor()->WatchTlsCertificates(
std::unique_ptr<TestCertificatesWatcher>(watcher), "", "");
EXPECT_EQ(watcher->root_certs(), absl::nullopt);
EXPECT_EQ(watcher->key_cert_pairs(), absl::nullopt);
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Update both root certs and identity certs
root_cert_distributor->SetKeyMaterials("test", kRootCert1, absl::nullopt);
identity_cert_distributor->SetKeyMaterials("test", absl::nullopt,
MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for just root certs
root_cert_distributor->SetKeyMaterials("test", kRootCert2, absl::nullopt);
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for identity certs
identity_cert_distributor->SetKeyMaterials("test", absl::nullopt,
MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Set error for both root and identity
root_cert_distributor->SetErrorForCert(
"test", GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
identity_cert_distributor->SetErrorForCert(
"test", absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(kRootErrorMessage));
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for root certs. Test that the root cert error is reset.
root_cert_distributor->SetKeyMaterials("test", kRootCert1, absl::nullopt);
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for identity certs. Test that the identity cert error is
// reset.
identity_cert_distributor->SetKeyMaterials("test", absl::nullopt,
MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Test update on unwatched cert name
identity_cert_distributor->SetKeyMaterials("identity", kRootCert2,
MakeKeyCertPairsType2());
root_cert_distributor->SetKeyMaterials("root", kRootCert1,
MakeKeyCertPairsType1());
}
TEST(XdsCertificateProviderTest,
RootCertDistributorSameAsIdentityCertDistributorDifferentCertNames) {
auto distributor = MakeRefCounted<grpc_tls_certificate_distributor>();
XdsCertificateProvider provider("root", distributor, "identity", distributor);
auto* watcher = new TestCertificatesWatcher;
provider.distributor()->WatchTlsCertificates(
std::unique_ptr<TestCertificatesWatcher>(watcher), "", "");
EXPECT_EQ(watcher->root_certs(), absl::nullopt);
EXPECT_EQ(watcher->key_cert_pairs(), absl::nullopt);
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Update both root certs and identity certs
distributor->SetKeyMaterials("root", kRootCert1, MakeKeyCertPairsType2());
distributor->SetKeyMaterials("identity", kRootCert2, MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for just root certs
distributor->SetKeyMaterials("root", kRootCert2, MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for identity certs
distributor->SetKeyMaterials("identity", kRootCert1, MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Set error for root
distributor->SetErrorForCert(
"root", GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage));
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(kRootErrorMessage));
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
distributor->SetErrorForCert(
"identity", GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(kRootErrorMessage));
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for root
distributor->SetKeyMaterials("root", kRootCert1, MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for identity
distributor->SetKeyMaterials("identity", kRootCert2, MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
}
TEST(XdsCertificateProviderTest,
RootCertDistributorSameAsIdentityCertDistributorSameCertNames) {
auto distributor = MakeRefCounted<grpc_tls_certificate_distributor>();
XdsCertificateProvider provider("", distributor, "", distributor);
auto* watcher = new TestCertificatesWatcher;
provider.distributor()->WatchTlsCertificates(
std::unique_ptr<TestCertificatesWatcher>(watcher), "", "");
EXPECT_EQ(watcher->root_certs(), absl::nullopt);
EXPECT_EQ(watcher->key_cert_pairs(), absl::nullopt);
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Update both root certs and identity certs
distributor->SetKeyMaterials("", kRootCert1, MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for just root certs
distributor->SetKeyMaterials("", kRootCert2, absl::nullopt);
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Second update for identity certs
distributor->SetKeyMaterials("", absl::nullopt, MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Set error for root
distributor->SetErrorForCert(
"", GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(kRootErrorMessage));
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Set error for identity
distributor->SetErrorForCert(
"", absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(kRootErrorMessage));
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for root
distributor->SetKeyMaterials("", kRootCert1, absl::nullopt);
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update for identity
distributor->SetKeyMaterials("", absl::nullopt, MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
}
TEST(XdsCertificateProviderTest, SwapOutDistributorsMultipleTimes) {
auto distributor = MakeRefCounted<grpc_tls_certificate_distributor>();
distributor->SetKeyMaterials("", kRootCert1, MakeKeyCertPairsType1());
XdsCertificateProvider provider("", nullptr, "", nullptr);
auto* watcher = new TestCertificatesWatcher;
provider.distributor()->WatchTlsCertificates(
std::unique_ptr<TestCertificatesWatcher>(watcher), "", "");
// Initially there are no certificate providers.
EXPECT_EQ(watcher->root_certs(), absl::nullopt);
EXPECT_EQ(watcher->key_cert_pairs(), absl::nullopt);
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(
"No certificate provider available for root certificates"));
EXPECT_THAT(
grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(
"No certificate provider available for identity certificates"));
// Update root cert distributor.
provider.UpdateRootCertNameAndDistributor("", distributor);
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), absl::nullopt);
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_THAT(
grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(
"No certificate provider available for identity certificates"));
// Update identity cert distributor
provider.UpdateIdentityCertNameAndDistributor("", distributor);
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Update both root and identity certs
distributor->SetKeyMaterials("", kRootCert2, MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Set error for both root and identity
distributor->SetErrorForCert(
"", GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(kRootErrorMessage));
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(kIdentityErrorMessage));
// Send an update again
distributor->SetKeyMaterials("", kRootCert1, MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Remove root cert provider
provider.UpdateRootCertNameAndDistributor("", nullptr);
distributor->SetKeyMaterials("", kRootCert2, MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert1); // not updated
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(
"No certificate provider available for root certificates"));
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Remove identity cert provider too
provider.UpdateIdentityCertNameAndDistributor("", nullptr);
distributor->SetKeyMaterials("", kRootCert1, MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2()); // not updated
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(
"No certificate provider available for root certificates"));
EXPECT_THAT(
grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(
"No certificate provider available for identity certificates"));
// Change certificate names being watched, without any certificate updates.
provider.UpdateRootCertNameAndDistributor("root", distributor);
provider.UpdateIdentityCertNameAndDistributor("identity", distributor);
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr(
"No certificate provider available for root certificates"));
EXPECT_THAT(
grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr(
"No certificate provider available for identity certificates"));
// Send out certificate updates.
distributor->SetKeyMaterials("root", kRootCert2, absl::nullopt);
distributor->SetKeyMaterials("identity", absl::nullopt,
MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Swap in new certificate distributors with different certificate names and
// existing updates.
auto root_cert_distributor =
MakeRefCounted<grpc_tls_certificate_distributor>();
auto identity_cert_distributor =
MakeRefCounted<grpc_tls_certificate_distributor>();
provider.UpdateRootCertNameAndDistributor("root", root_cert_distributor);
provider.UpdateIdentityCertNameAndDistributor("identity",
identity_cert_distributor);
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Change certificate names without any certificate updates.
provider.UpdateRootCertNameAndDistributor("test", root_cert_distributor);
provider.UpdateIdentityCertNameAndDistributor("test",
identity_cert_distributor);
EXPECT_EQ(watcher->root_certs(), kRootCert2);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType1());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
// Send out certificate updates.
root_cert_distributor->SetKeyMaterials("test", kRootCert1,
MakeKeyCertPairsType1());
identity_cert_distributor->SetKeyMaterials("test", kRootCert2,
MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_certs(), kRootCert1);
EXPECT_EQ(watcher->key_cert_pairs(), MakeKeyCertPairsType2());
EXPECT_EQ(watcher->root_cert_error(), GRPC_ERROR_NONE);
EXPECT_EQ(watcher->identity_cert_error(), GRPC_ERROR_NONE);
}
TEST(XdsCertificateProviderTest, CertificateNameNotEmpty) {
XdsCertificateProvider provider("", nullptr, "", nullptr);
auto* watcher = new TestCertificatesWatcher;
provider.distributor()->WatchTlsCertificates(
std::unique_ptr<TestCertificatesWatcher>(watcher), "test", "test");
EXPECT_THAT(grpc_error_string(watcher->root_cert_error()),
::testing::HasSubstr("Illegal certificate name: \'test\'"));
EXPECT_THAT(grpc_error_string(watcher->identity_cert_error()),
::testing::HasSubstr("Illegal certificate name: \'test\'"));
}
} // namespace
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(argc, argv);
grpc_init();
auto result = RUN_ALL_TESTS();
grpc_shutdown();
return result;
}

@ -1564,6 +1564,8 @@ src/core/ext/xds/xds_api.cc \
src/core/ext/xds/xds_api.h \
src/core/ext/xds/xds_bootstrap.cc \
src/core/ext/xds/xds_bootstrap.h \
src/core/ext/xds/xds_certificate_provider.cc \
src/core/ext/xds/xds_certificate_provider.h \
src/core/ext/xds/xds_channel_args.h \
src/core/ext/xds/xds_client.cc \
src/core/ext/xds/xds_client.h \

@ -1401,6 +1401,8 @@ src/core/ext/xds/xds_api.cc \
src/core/ext/xds/xds_api.h \
src/core/ext/xds/xds_bootstrap.cc \
src/core/ext/xds/xds_bootstrap.h \
src/core/ext/xds/xds_certificate_provider.cc \
src/core/ext/xds/xds_certificate_provider.h \
src/core/ext/xds/xds_channel_args.h \
src/core/ext/xds/xds_client.cc \
src/core/ext/xds/xds_client.h \

@ -6163,6 +6163,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "xds_certificate_provider_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save