Add File Watcher Certificate Provider API

pull/24600/head
ZhenLian 4 years ago
parent e6e6be4b0b
commit d74e43da95
  1. 42
      CMakeLists.txt
  2. 23
      build_autogenerated.yaml
  3. 1
      grpc.def
  4. 25
      include/grpc/grpc_security.h
  5. 52
      include/grpcpp/security/tls_certificate_provider.h
  6. 7
      src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h
  7. 332
      src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.cc
  8. 64
      src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h
  9. 1
      src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h
  10. 5
      src/core/lib/security/security_connector/tls/tls_security_connector.h
  11. 14
      src/cpp/common/tls_certificate_provider.cc
  12. 2
      src/ruby/ext/grpc/rb_grpc_imports.generated.c
  13. 3
      src/ruby/ext/grpc/rb_grpc_imports.generated.h
  14. 74
      test/core/end2end/fixtures/h2_tls.cc
  15. 38
      test/core/security/BUILD
  16. 217
      test/core/security/grpc_tls_certificate_distributor_test.cc
  17. 509
      test/core/security/grpc_tls_certificate_provider_test.cc
  18. 418
      test/core/security/grpc_tls_credentials_options_test.cc
  19. 83
      test/core/security/tls_utils.cc
  20. 47
      test/core/security/tls_utils.h
  21. 1
      test/core/surface/public_headers_must_be_c89.c
  22. 5
      test/cpp/client/BUILD
  23. 51
      test/cpp/client/credentials_test.cc
  24. 5
      test/cpp/server/BUILD
  25. 37
      test/cpp/server/credentials_test.cc
  26. 24
      tools/run_tests/generated/tests.json

@ -830,6 +830,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx google_mesh_ca_certificate_provider_factory_test)
add_dependencies(buildtests_cxx grpc_cli)
add_dependencies(buildtests_cxx grpc_tls_certificate_distributor_test)
add_dependencies(buildtests_cxx grpc_tls_certificate_provider_test)
add_dependencies(buildtests_cxx grpc_tls_credentials_options_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx grpc_tool_test)
@ -11696,6 +11697,7 @@ if(gRPC_BUILD_TESTS)
add_executable(grpc_tls_certificate_distributor_test
test/core/security/grpc_tls_certificate_distributor_test.cc
test/core/security/tls_utils.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
@ -11729,11 +11731,51 @@ target_link_libraries(grpc_tls_certificate_distributor_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(grpc_tls_certificate_provider_test
test/core/security/grpc_tls_certificate_provider_test.cc
test/core/security/tls_utils.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(grpc_tls_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(grpc_tls_certificate_provider_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc
gpr
address_sorting
upb
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(grpc_tls_credentials_options_test
test/core/security/grpc_tls_credentials_options_test.cc
test/core/security/tls_utils.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)

@ -6208,9 +6208,26 @@ targets:
gtest: true
build: test
language: c++
headers: []
headers:
- test/core/security/tls_utils.h
src:
- test/core/security/grpc_tls_certificate_distributor_test.cc
- test/core/security/tls_utils.cc
deps:
- grpc_test_util
- grpc
- gpr
- address_sorting
- upb
- name: grpc_tls_certificate_provider_test
gtest: true
build: test
language: c++
headers:
- test/core/security/tls_utils.h
src:
- test/core/security/grpc_tls_certificate_provider_test.cc
- test/core/security/tls_utils.cc
deps:
- grpc_test_util
- grpc
@ -6221,9 +6238,11 @@ targets:
gtest: true
build: test
language: c++
headers: []
headers:
- test/core/security/tls_utils.h
src:
- test/core/security/grpc_tls_credentials_options_test.cc
- test/core/security/tls_utils.cc
deps:
- grpc_test_util
- grpc

@ -139,6 +139,7 @@ EXPORTS
grpc_tls_identity_pairs_add_pair
grpc_tls_identity_pairs_destroy
grpc_tls_certificate_provider_static_data_create
grpc_tls_certificate_provider_file_watcher_create
grpc_tls_certificate_provider_release
grpc_tls_credentials_options_create
grpc_tls_credentials_options_set_cert_request_type

@ -807,6 +807,31 @@ GRPCAPI grpc_tls_certificate_provider*
grpc_tls_certificate_provider_static_data_create(
const char* root_certificate, grpc_tls_identity_pairs* pem_key_cert_pairs);
/**
* Creates a grpc_tls_certificate_provider that will watch the credential
* changes on the file system. This provider will always return the up-to-date
* cert data for all the cert names callers set through
* |grpc_tls_credentials_options|. Note that this API only supports one key-cert
* file and hence one set of identity key-cert pair, so SNI(Server Name
* Indication) is not supported.
* - private_key_path is the file path of the private key. This must be set if
* |identity_certificate_path| is set. Otherwise, it could be null if no
* identity credentials are needed.
* - identity_certificate_path is the file path of the identity certificate
* chain. This must be set if |private_key_path| is set. Otherwise, it could
* be null if no identity credentials are needed.
* - root_cert_path is the file path to the root certificate bundle. This
* may be null if no root certs are needed.
* - refresh_interval_sec is the refreshing interval that we will check the
* files for updates.
* It does not take ownership of parameters.
* It is used for experimental purpose for now and subject to change.
*/
GRPCAPI grpc_tls_certificate_provider*
grpc_tls_certificate_provider_file_watcher_create(
const char* private_key_path, const char* identity_certificate_path,
const char* root_cert_path, unsigned int refresh_interval_sec);
/**
* Releases a grpc_tls_certificate_provider object. The creator of the
* grpc_tls_certificate_provider object is responsible for its release. It is

@ -66,7 +66,57 @@ class StaticDataCertificateProvider : public CertificateProviderInterface {
const std::vector<IdentityKeyCertPair>& identity_key_cert_pairs)
: StaticDataCertificateProvider("", identity_key_cert_pairs) {}
~StaticDataCertificateProvider();
~StaticDataCertificateProvider() override;
grpc_tls_certificate_provider* c_provider() override { return c_provider_; }
private:
grpc_tls_certificate_provider* c_provider_ = nullptr;
};
// A CertificateProviderInterface implementation that will watch the credential
// changes on the file system. This provider will always return the up-to-date
// cert data for all the cert names callers set through |TlsCredentialsOptions|.
// Several things to note:
// 1. This API only supports one key-cert file and hence one set of identity
// key-cert pair, so SNI(Server Name Indication) is not supported.
// 2. The private key and identity certificate should always match. This API
// guarantees atomic read, and it is the callers' responsibility to do atomic
// updates. There are many ways to atomically update the key and certs in the
// file system. To name a few:
// 1) creating a new directory, renaming the old directory to a new name, and
// then renaming the new directory to the original name of the old directory.
// 2) using a symlink for the directory. When need to change, put new
// credential data in a new directory, and change symlink.
class FileWatcherCertificateProvider final
: public CertificateProviderInterface {
public:
// Constructor to get credential updates from root and identity file paths.
//
// @param private_key_path is the file path of the private key.
// @param identity_certificate_path is the file path of the identity
// certificate chain.
// @param root_cert_path is the file path to the root certificate bundle.
// @param refresh_interval_sec is the refreshing interval that we will check
// the files for updates.
FileWatcherCertificateProvider(const std::string& private_key_path,
const std::string& identity_certificate_path,
const std::string& root_cert_path,
unsigned int refresh_interval_sec);
// Constructor to get credential updates from identity file paths only.
FileWatcherCertificateProvider(const std::string& private_key_path,
const std::string& identity_certificate_path,
unsigned int refresh_interval_sec)
: FileWatcherCertificateProvider(private_key_path,
identity_certificate_path, "",
refresh_interval_sec) {}
// Constructor to get credential updates from root file path only.
FileWatcherCertificateProvider(const std::string& root_cert_path,
unsigned int refresh_interval_sec)
: FileWatcherCertificateProvider("", "", root_cert_path,
refresh_interval_sec) {}
~FileWatcherCertificateProvider() override;
grpc_tls_certificate_provider* c_provider() override { return c_provider_; }

@ -72,12 +72,7 @@ struct grpc_tls_certificate_distributor
grpc_error* identity_cert_error) = 0;
};
// Sets the key materials based on their certificate name. Note that we are
// not doing any copies for pem_root_certs and pem_key_cert_pairs. For
// pem_root_certs, the original string contents need to outlive the
// distributor; for pem_key_cert_pairs, internally it is taking two
// unique_ptr(s) to the credential string, so the ownership is actually
// transferred.
// Sets the key materials based on their certificate name.
//
// @param cert_name The name of the certificates being updated.
// @param pem_root_certs The content of root certificates.

@ -22,6 +22,8 @@
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/lib/gprpp/stat.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/surface/api_trace.h"
namespace grpc_core {
@ -35,20 +37,334 @@ StaticDataCertificateProvider::StaticDataCertificateProvider(
distributor_->SetWatchStatusCallback([this](std::string cert_name,
bool root_being_watched,
bool identity_being_watched) {
if (!root_being_watched && !identity_being_watched) return;
grpc_core::MutexLock lock(&mu_);
absl::optional<std::string> root_certificate;
absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
if (root_being_watched) {
StaticDataCertificateProvider::WatcherInfo& info = watcher_info_[cert_name];
if (!info.root_being_watched && root_being_watched &&
!root_certificate_.empty()) {
root_certificate = root_certificate_;
}
if (identity_being_watched) {
info.root_being_watched = root_being_watched;
if (!info.identity_being_watched && identity_being_watched &&
!pem_key_cert_pairs_.empty()) {
pem_key_cert_pairs = pem_key_cert_pairs_;
}
info.identity_being_watched = identity_being_watched;
if (!info.root_being_watched && !info.identity_being_watched) {
watcher_info_.erase(cert_name);
}
const bool root_has_update = root_certificate.has_value();
const bool identity_has_update = pem_key_cert_pairs.has_value();
if (root_has_update || identity_has_update) {
distributor_->SetKeyMaterials(cert_name, std::move(root_certificate),
std::move(pem_key_cert_pairs));
}
grpc_error* root_cert_error = GRPC_ERROR_NONE;
grpc_error* identity_cert_error = GRPC_ERROR_NONE;
if (root_being_watched && !root_has_update) {
root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unable to get latest root certificates.");
}
if (identity_being_watched && !identity_has_update) {
identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unable to get latest identity certificates.");
}
if (root_cert_error != GRPC_ERROR_NONE ||
identity_cert_error != GRPC_ERROR_NONE) {
distributor_->SetErrorForCert(cert_name, root_cert_error,
identity_cert_error);
}
});
}
StaticDataCertificateProvider::~StaticDataCertificateProvider() {
// Reset distributor's callback to make sure the callback won't be invoked
// again after this object(provider) is destroyed.
distributor_->SetWatchStatusCallback(nullptr);
}
namespace {
gpr_timespec TimeoutSecondsToDeadline(int64_t seconds) {
return gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(seconds, GPR_TIMESPAN));
}
} // namespace
FileWatcherCertificateProvider::FileWatcherCertificateProvider(
std::string private_key_path, std::string identity_certificate_path,
std::string root_cert_path, unsigned int refresh_interval_sec)
: private_key_path_(std::move(private_key_path)),
identity_certificate_path_(std::move(identity_certificate_path)),
root_cert_path_(std::move(root_cert_path)),
refresh_interval_sec_(refresh_interval_sec),
distributor_(MakeRefCounted<grpc_tls_certificate_distributor>()) {
// Private key and identity cert files must be both set or both unset.
GPR_ASSERT(private_key_path_.empty() == identity_certificate_path_.empty());
// Must be watching either root or identity certs.
GPR_ASSERT(!private_key_path_.empty() || !root_cert_path_.empty());
gpr_event_init(&shutdown_event_);
ForceUpdate();
auto thread_lambda = [](void* arg) {
FileWatcherCertificateProvider* provider =
static_cast<FileWatcherCertificateProvider*>(arg);
GPR_ASSERT(provider != nullptr);
while (true) {
void* value = gpr_event_wait(
&provider->shutdown_event_,
TimeoutSecondsToDeadline(provider->refresh_interval_sec_));
if (value != nullptr) {
return;
};
provider->ForceUpdate();
}
};
refresh_thread_ = grpc_core::Thread(
"FileWatcherCertificateProvider_refreshing_thread", thread_lambda, this);
refresh_thread_.Start();
distributor_->SetWatchStatusCallback([this](std::string cert_name,
bool root_being_watched,
bool identity_being_watched) {
grpc_core::MutexLock lock(&mu_);
absl::optional<std::string> root_certificate;
absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
FileWatcherCertificateProvider::WatcherInfo& info =
watcher_info_[cert_name];
if (!info.root_being_watched && root_being_watched &&
!root_certificate_.empty()) {
root_certificate = root_certificate_;
}
info.root_being_watched = root_being_watched;
if (!info.identity_being_watched && identity_being_watched &&
!pem_key_cert_pairs_.empty()) {
pem_key_cert_pairs = pem_key_cert_pairs_;
}
info.identity_being_watched = identity_being_watched;
if (!info.root_being_watched && !info.identity_being_watched) {
watcher_info_.erase(cert_name);
}
ExecCtx exec_ctx;
if (root_certificate.has_value() || pem_key_cert_pairs.has_value()) {
distributor_->SetKeyMaterials(cert_name, root_certificate,
pem_key_cert_pairs);
}
grpc_error* root_cert_error = GRPC_ERROR_NONE;
grpc_error* identity_cert_error = GRPC_ERROR_NONE;
if (root_being_watched && !root_certificate.has_value()) {
root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unable to get latest root certificates.");
}
if (identity_being_watched && !pem_key_cert_pairs.has_value()) {
identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unable to get latest identity certificates.");
}
if (root_cert_error != GRPC_ERROR_NONE ||
identity_cert_error != GRPC_ERROR_NONE) {
distributor_->SetErrorForCert(cert_name, root_cert_error,
identity_cert_error);
}
});
}
FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
// Reset distributor's callback to make sure the callback won't be invoked
// again after this object(provider) is destroyed.
distributor_->SetWatchStatusCallback(nullptr);
gpr_event_set(&shutdown_event_, (void*)(1));
refresh_thread_.Join();
}
void FileWatcherCertificateProvider::ForceUpdate() {
absl::optional<std::string> root_certificate;
absl::optional<grpc_core::PemKeyCertPairList> pem_key_cert_pairs;
if (!root_cert_path_.empty()) {
root_certificate = ReadRootCertificatesFromFile(root_cert_path_);
}
if (!private_key_path_.empty()) {
pem_key_cert_pairs = ReadIdentityKeyCertPairFromFiles(
private_key_path_, identity_certificate_path_);
}
grpc_core::MutexLock lock(&mu_);
const bool root_cert_changed =
(!root_certificate.has_value() && !root_certificate_.empty()) ||
(root_certificate.has_value() && root_certificate_ != *root_certificate);
if (root_cert_changed) {
if (root_certificate.has_value()) {
root_certificate_ = std::move(*root_certificate);
} else {
root_certificate_ = "";
}
}
const bool identity_cert_changed =
(!pem_key_cert_pairs.has_value() && !pem_key_cert_pairs_.empty()) ||
(pem_key_cert_pairs.has_value() &&
pem_key_cert_pairs_ != *pem_key_cert_pairs);
if (identity_cert_changed) {
if (pem_key_cert_pairs.has_value()) {
pem_key_cert_pairs_ = std::move(*pem_key_cert_pairs);
} else {
pem_key_cert_pairs_ = {};
}
}
if (root_cert_changed || identity_cert_changed) {
ExecCtx exec_ctx;
grpc_error* root_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unable to get latest root certificates.");
grpc_error* identity_cert_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Unable to get latest identity certificates.");
for (const auto& p : watcher_info_) {
const std::string& cert_name = p.first;
const WatcherInfo& info = p.second;
absl::optional<std::string> root_to_report;
absl::optional<grpc_core::PemKeyCertPairList> identity_to_report;
// Set key materials to the distributor if their contents changed.
if (info.root_being_watched && !root_certificate_.empty() &&
root_cert_changed) {
root_to_report = root_certificate_;
}
if (info.identity_being_watched && !pem_key_cert_pairs_.empty() &&
identity_cert_changed) {
identity_to_report = pem_key_cert_pairs_;
}
if (root_to_report.has_value() || identity_to_report.has_value()) {
distributor_->SetKeyMaterials(cert_name, std::move(root_to_report),
std::move(identity_to_report));
}
// Report errors to the distributor if the contents are empty.
const bool report_root_error =
info.root_being_watched && root_certificate_.empty();
const bool report_identity_error =
info.identity_being_watched && pem_key_cert_pairs_.empty();
if (report_root_error || report_identity_error) {
distributor_->SetErrorForCert(
cert_name,
report_root_error ? GRPC_ERROR_REF(root_cert_error)
: GRPC_ERROR_NONE,
report_identity_error ? GRPC_ERROR_REF(identity_cert_error)
: GRPC_ERROR_NONE);
}
}
GRPC_ERROR_UNREF(root_cert_error);
GRPC_ERROR_UNREF(identity_cert_error);
}
}
absl::optional<std::string>
FileWatcherCertificateProvider::ReadRootCertificatesFromFile(
const std::string& root_cert_full_path) {
// Read the root file.
grpc_slice root_slice = grpc_empty_slice();
grpc_error* root_error =
grpc_load_file(root_cert_full_path.c_str(), 0, &root_slice);
if (root_error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "Reading file %s failed: %s",
root_cert_full_path.c_str(), grpc_error_string(root_error));
GRPC_ERROR_UNREF(root_error);
return absl::nullopt;
}
std::string root_cert(StringViewFromSlice(root_slice));
grpc_slice_unref_internal(root_slice);
return root_cert;
}
namespace {
// This helper function gets the last-modified time of |filename|. When failed,
// it logs the error and returns 0.
time_t GetModificationTime(const char* filename) {
time_t ts = 0;
absl::Status status = grpc_core::GetFileModificationTime(filename, &ts);
return ts;
}
} // namespace
absl::optional<PemKeyCertPairList>
FileWatcherCertificateProvider::ReadIdentityKeyCertPairFromFiles(
const std::string& private_key_path,
const std::string& identity_certificate_path) {
struct SliceWrapper {
grpc_slice slice = grpc_empty_slice();
~SliceWrapper() { grpc_slice_unref_internal(slice); }
};
const int kNumRetryAttempts = 3;
for (int i = 0; i < kNumRetryAttempts; ++i) {
// TODO(ZhenLian): replace the timestamp approach with key-match approach
// once the latter is implemented.
// Checking the last modification of identity files before reading.
time_t identity_key_ts_before =
GetModificationTime(private_key_path.c_str());
if (identity_key_ts_before == 0) {
gpr_log(
GPR_ERROR,
"Failed to get the file's modification time of %s. Start retrying...",
private_key_path.c_str());
continue;
}
time_t identity_cert_ts_before =
GetModificationTime(identity_certificate_path.c_str());
if (identity_cert_ts_before == 0) {
gpr_log(
GPR_ERROR,
"Failed to get the file's modification time of %s. Start retrying...",
identity_certificate_path.c_str());
continue;
}
// Read the identity files.
SliceWrapper key_slice, cert_slice;
grpc_error* key_error =
grpc_load_file(private_key_path.c_str(), 0, &key_slice.slice);
if (key_error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
private_key_path.c_str(), grpc_error_string(key_error));
GRPC_ERROR_UNREF(key_error);
continue;
}
grpc_error* cert_error =
grpc_load_file(identity_certificate_path.c_str(), 0, &cert_slice.slice);
if (cert_error != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "Reading file %s failed: %s. Start retrying...",
identity_certificate_path.c_str(), grpc_error_string(cert_error));
GRPC_ERROR_UNREF(cert_error);
continue;
}
std::string private_key(StringViewFromSlice(key_slice.slice));
std::string cert_chain(StringViewFromSlice(cert_slice.slice));
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.c_str());
ssl_pair->cert_chain = gpr_strdup(cert_chain.c_str());
PemKeyCertPairList identity_pairs;
identity_pairs.emplace_back(ssl_pair);
// Checking the last modification of identity files before reading.
time_t identity_key_ts_after =
GetModificationTime(private_key_path.c_str());
if (identity_key_ts_before != identity_key_ts_after) {
gpr_log(GPR_ERROR,
"Last modified time before and after reading %s is not the same. "
"Start retrying...",
private_key_path.c_str());
continue;
}
time_t identity_cert_ts_after =
GetModificationTime(identity_certificate_path.c_str());
if (identity_cert_ts_before != identity_cert_ts_after) {
gpr_log(GPR_ERROR,
"Last modified time before and after reading %s is not the same. "
"Start retrying...",
identity_certificate_path.c_str());
continue;
}
return identity_pairs;
}
gpr_log(GPR_ERROR,
"All retry attempts failed. Will try again after the next interval.");
return absl::nullopt;
}
} // namespace grpc_core
/** -- Wrapper APIs declared in grpc_security.h -- **/
@ -69,6 +385,16 @@ grpc_tls_certificate_provider* grpc_tls_certificate_provider_static_data_create(
std::move(root_cert_core), std::move(identity_pairs_core));
}
grpc_tls_certificate_provider*
grpc_tls_certificate_provider_file_watcher_create(
const char* private_key_path, const char* identity_certificate_path,
const char* root_cert_path, unsigned int refresh_interval_sec) {
return new grpc_core::FileWatcherCertificateProvider(
private_key_path == nullptr ? "" : private_key_path,
identity_certificate_path == nullptr ? "" : identity_certificate_path,
root_cert_path == nullptr ? "" : root_cert_path, refresh_interval_sec);
}
void grpc_tls_certificate_provider_release(
grpc_tls_certificate_provider* provider) {
GRPC_API_TRACE("grpc_tls_certificate_provider_release(provider=%p)", 1,

@ -26,6 +26,8 @@
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/thd.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/iomgr/pollset_set.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_distributor.h"
#include "src/core/lib/security/security_connector/ssl_utils.h"
@ -59,14 +61,76 @@ class StaticDataCertificateProvider final
std::string root_certificate,
grpc_core::PemKeyCertPairList pem_key_cert_pairs);
~StaticDataCertificateProvider() override;
RefCountedPtr<grpc_tls_certificate_distributor> distributor() const override {
return distributor_;
}
private:
struct WatcherInfo {
bool root_being_watched = false;
bool identity_being_watched = false;
};
RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
std::string root_certificate_;
grpc_core::PemKeyCertPairList pem_key_cert_pairs_;
// Guards members below.
grpc_core::Mutex mu_;
// Stores each cert_name we get from the distributor callback and its watcher
// information.
std::map<std::string, WatcherInfo> watcher_info_;
};
// A provider class that will watch the credential changes on the file system.
class FileWatcherCertificateProvider final
: public grpc_tls_certificate_provider {
public:
FileWatcherCertificateProvider(std::string private_key_path,
std::string identity_certificate_path,
std::string root_cert_path,
unsigned int refresh_interval_sec);
~FileWatcherCertificateProvider() override;
RefCountedPtr<grpc_tls_certificate_distributor> distributor() const override {
return distributor_;
}
private:
struct WatcherInfo {
bool root_being_watched = false;
bool identity_being_watched = false;
};
// Force an update from the file system regardless of the interval.
void ForceUpdate();
// Read the root certificates from files and update the distributor.
absl::optional<std::string> ReadRootCertificatesFromFile(
const std::string& root_cert_full_path);
// Read the root certificates from files and update the distributor.
absl::optional<PemKeyCertPairList> ReadIdentityKeyCertPairFromFiles(
const std::string& private_key_file_name,
const std::string& identity_certificate_file_name);
// Information that is used by the refreshing thread.
std::string private_key_path_;
std::string identity_certificate_path_;
std::string root_cert_path_;
unsigned int refresh_interval_sec_ = 0;
RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
grpc_core::Thread refresh_thread_;
gpr_event shutdown_event_;
// Guards members below.
grpc_core::Mutex mu_;
// The most-recent credential data. It will be empty if the most recent read
// attempt failed.
std::string root_certificate_;
grpc_core::PemKeyCertPairList pem_key_cert_pairs_;
// Stores each cert_name we get from the distributor callback and its watcher
// information.
std::map<std::string, WatcherInfo> watcher_info_;
};
} // namespace grpc_core

@ -147,7 +147,6 @@ struct grpc_tls_credentials_options
server_authorization_check_config_ = std::move(config);
}
// Sets the provider in the options.
// This should only be used by C-core API for Tls*Creds case.
void set_certificate_provider(
grpc_core::RefCountedPtr<grpc_tls_certificate_provider> provider) {
provider_ = std::move(provider);

@ -74,13 +74,12 @@ class TlsChannelSecurityConnector final
return client_handshaker_factory_;
};
const absl::optional<absl::string_view>& RootCertsForTesting() {
absl::optional<absl::string_view> RootCertsForTesting() {
grpc_core::MutexLock lock(&mu_);
return pem_root_certs_;
}
const absl::optional<grpc_core::PemKeyCertPairList>&
KeyCertPairListForTesting() {
absl::optional<grpc_core::PemKeyCertPairList> KeyCertPairListForTesting() {
grpc_core::MutexLock lock(&mu_);
return pem_key_cert_pair_list_;
}

@ -41,5 +41,19 @@ StaticDataCertificateProvider::~StaticDataCertificateProvider() {
grpc_tls_certificate_provider_release(c_provider_);
};
FileWatcherCertificateProvider::FileWatcherCertificateProvider(
const std::string& private_key_path,
const std::string& identity_certificate_path,
const std::string& root_cert_path, unsigned int refresh_interval_sec) {
c_provider_ = grpc_tls_certificate_provider_file_watcher_create(
private_key_path.c_str(), identity_certificate_path.c_str(),
root_cert_path.c_str(), refresh_interval_sec);
GPR_ASSERT(c_provider_ != nullptr);
};
FileWatcherCertificateProvider::~FileWatcherCertificateProvider() {
grpc_tls_certificate_provider_release(c_provider_);
};
} // namespace experimental
} // namespace grpc

@ -162,6 +162,7 @@ grpc_tls_identity_pairs_create_type grpc_tls_identity_pairs_create_import;
grpc_tls_identity_pairs_add_pair_type grpc_tls_identity_pairs_add_pair_import;
grpc_tls_identity_pairs_destroy_type grpc_tls_identity_pairs_destroy_import;
grpc_tls_certificate_provider_static_data_create_type grpc_tls_certificate_provider_static_data_create_import;
grpc_tls_certificate_provider_file_watcher_create_type grpc_tls_certificate_provider_file_watcher_create_import;
grpc_tls_certificate_provider_release_type grpc_tls_certificate_provider_release_import;
grpc_tls_credentials_options_create_type grpc_tls_credentials_options_create_import;
grpc_tls_credentials_options_set_cert_request_type_type grpc_tls_credentials_options_set_cert_request_type_import;
@ -440,6 +441,7 @@ void grpc_rb_load_imports(HMODULE library) {
grpc_tls_identity_pairs_add_pair_import = (grpc_tls_identity_pairs_add_pair_type) GetProcAddress(library, "grpc_tls_identity_pairs_add_pair");
grpc_tls_identity_pairs_destroy_import = (grpc_tls_identity_pairs_destroy_type) GetProcAddress(library, "grpc_tls_identity_pairs_destroy");
grpc_tls_certificate_provider_static_data_create_import = (grpc_tls_certificate_provider_static_data_create_type) GetProcAddress(library, "grpc_tls_certificate_provider_static_data_create");
grpc_tls_certificate_provider_file_watcher_create_import = (grpc_tls_certificate_provider_file_watcher_create_type) GetProcAddress(library, "grpc_tls_certificate_provider_file_watcher_create");
grpc_tls_certificate_provider_release_import = (grpc_tls_certificate_provider_release_type) GetProcAddress(library, "grpc_tls_certificate_provider_release");
grpc_tls_credentials_options_create_import = (grpc_tls_credentials_options_create_type) GetProcAddress(library, "grpc_tls_credentials_options_create");
grpc_tls_credentials_options_set_cert_request_type_import = (grpc_tls_credentials_options_set_cert_request_type_type) GetProcAddress(library, "grpc_tls_credentials_options_set_cert_request_type");

@ -461,6 +461,9 @@ extern grpc_tls_identity_pairs_destroy_type grpc_tls_identity_pairs_destroy_impo
typedef grpc_tls_certificate_provider*(*grpc_tls_certificate_provider_static_data_create_type)(const char* root_certificate, grpc_tls_identity_pairs* pem_key_cert_pairs);
extern grpc_tls_certificate_provider_static_data_create_type grpc_tls_certificate_provider_static_data_create_import;
#define grpc_tls_certificate_provider_static_data_create grpc_tls_certificate_provider_static_data_create_import
typedef grpc_tls_certificate_provider*(*grpc_tls_certificate_provider_file_watcher_create_type)(const char* private_key_path, const char* identity_certificate_path, const char* root_cert_path, unsigned int refresh_interval_sec);
extern grpc_tls_certificate_provider_file_watcher_create_type grpc_tls_certificate_provider_file_watcher_create_import;
#define grpc_tls_certificate_provider_file_watcher_create grpc_tls_certificate_provider_file_watcher_create_import
typedef void(*grpc_tls_certificate_provider_release_type)(grpc_tls_certificate_provider* provider);
extern grpc_tls_certificate_provider_release_type grpc_tls_certificate_provider_release_import;
#define grpc_tls_certificate_provider_release grpc_tls_certificate_provider_release_import

@ -39,6 +39,7 @@
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
// For normal TLS connections.
#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
@ -60,7 +61,7 @@ struct fullstack_secure_fixture_data {
grpc_tls_certificate_provider* server_provider = nullptr;
};
static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
static grpc_end2end_test_fixture chttp2_create_fixture_static_data(
grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/,
grpc_tls_version tls_version) {
grpc_end2end_test_fixture f;
@ -101,15 +102,46 @@ static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack(
return f;
}
static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_2(
static grpc_end2end_test_fixture chttp2_create_fixture_cert_watcher(
grpc_channel_args* /*client_args*/, grpc_channel_args* /*server_args*/,
grpc_tls_version tls_version) {
grpc_end2end_test_fixture f;
int port = grpc_pick_unused_port_or_die();
fullstack_secure_fixture_data* ffd = new fullstack_secure_fixture_data();
memset(&f, 0, sizeof(f));
ffd->localaddr = grpc_core::JoinHostPort("localhost", port);
ffd->tls_version = tls_version;
ffd->client_provider = grpc_tls_certificate_provider_file_watcher_create(
SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
ffd->server_provider = grpc_tls_certificate_provider_file_watcher_create(
SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
f.fixture_data = ffd;
f.cq = grpc_completion_queue_create_for_next(nullptr);
f.shutdown_cq = grpc_completion_queue_create_for_pluck(nullptr);
return f;
}
static grpc_end2end_test_fixture chttp2_create_fixture_static_data_tls1_2(
grpc_channel_args* client_args, grpc_channel_args* server_args) {
return chttp2_create_fixture_secure_fullstack(client_args, server_args,
return chttp2_create_fixture_static_data(client_args, server_args,
grpc_tls_version::TLS1_2);
}
static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack_tls1_3(
static grpc_end2end_test_fixture chttp2_create_fixture_static_data_tls1_3(
grpc_channel_args* client_args, grpc_channel_args* server_args) {
return chttp2_create_fixture_secure_fullstack(client_args, server_args,
return chttp2_create_fixture_static_data(client_args, server_args,
grpc_tls_version::TLS1_3);
}
static grpc_end2end_test_fixture chttp2_create_fixture_cert_watcher_tls1_2(
grpc_channel_args* client_args, grpc_channel_args* server_args) {
return chttp2_create_fixture_cert_watcher(client_args, server_args,
grpc_tls_version::TLS1_2);
}
static grpc_end2end_test_fixture chttp2_create_fixture_cert_watcher_tls1_3(
grpc_channel_args* client_args, grpc_channel_args* server_args) {
return chttp2_create_fixture_cert_watcher(client_args, server_args,
grpc_tls_version::TLS1_3);
}
@ -262,21 +294,47 @@ static void chttp2_init_server(grpc_end2end_test_fixture* f,
}
static grpc_end2end_test_config configs[] = {
/* client sync reload async authz + server sync reload. */
// client: static data provider + async custom verification
// server: static data provider
// extra: TLS 1.2
{"chttp2/simple_ssl_fullstack_tls1_2",
FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
"foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_2,
"foo.test.google.fr", chttp2_create_fixture_static_data_tls1_2,
chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
// client: static data provider + async custom verification
// server: static data provider
// extra: TLS 1.3
{"chttp2/simple_ssl_fullstack_tls1_3",
FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
"foo.test.google.fr", chttp2_create_fixture_secure_fullstack_tls1_3,
"foo.test.google.fr", chttp2_create_fixture_static_data_tls1_3,
chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
// client: certificate watcher provider + async custom verification
// server: certificate watcher provider
// extra: TLS 1.2
{"chttp2/reloading_from_files_ssl_fullstack_tls1_2",
FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
"foo.test.google.fr", chttp2_create_fixture_cert_watcher_tls1_2,
chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
// client: certificate watcher provider + async custom verification
// server: certificate watcher provider
// extra: TLS 1.3
{"chttp2/reloading_from_files_ssl_fullstack_tls1_3",
FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION |
FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS |
FEATURE_MASK_SUPPORTS_CLIENT_CHANNEL |
FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER,
"foo.test.google.fr", chttp2_create_fixture_cert_watcher_tls1_3,
chttp2_init_client, chttp2_init_server, chttp2_tear_down_secure_fullstack},
};
int main(int argc, char** argv) {

@ -287,6 +287,15 @@ grpc_cc_test(
],
)
grpc_cc_library(
name = "tls_utils",
srcs = ["tls_utils.cc"],
hdrs = ["tls_utils.h"],
language = "C++",
visibility = ["//test/cpp:__subpackages__"],
deps = ["//:grpc"],
)
grpc_cc_test(
name = "tls_security_connector_test",
srcs = ["tls_security_connector_test.cc"],
@ -318,12 +327,17 @@ grpc_cc_test(
srcs = ["grpc_tls_credentials_options_test.cc"],
data = [
"//src/core/tsi/test_creds:ca.pem",
"//src/core/tsi/test_creds:multi-domain.key",
"//src/core/tsi/test_creds:multi-domain.pem",
"//src/core/tsi/test_creds:server0.key",
"//src/core/tsi/test_creds:server0.pem",
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
external_deps = ["gtest"],
language = "C++",
deps = [
":tls_utils",
"//:gpr",
"//:grpc",
"//:grpc_secure",
@ -337,6 +351,30 @@ grpc_cc_test(
external_deps = ["gtest"],
language = "C++",
deps = [
":tls_utils",
"//:gpr",
"//:grpc",
"//:grpc_secure",
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "grpc_tls_certificate_provider_test",
srcs = ["grpc_tls_certificate_provider_test.cc"],
data = [
"//src/core/tsi/test_creds:ca.pem",
"//src/core/tsi/test_creds:multi-domain.key",
"//src/core/tsi/test_creds:multi-domain.pem",
"//src/core/tsi/test_creds:server0.key",
"//src/core/tsi/test_creds:server0.pem",
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
external_deps = ["gtest"],
language = "C++",
deps = [
":tls_utils",
"//:gpr",
"//:grpc",
"//:grpc_secure",

@ -28,8 +28,11 @@
#include <thread>
#include "src/core/lib/slice/slice_internal.h"
#include "test/core/security/tls_utils.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
constexpr const char* kCertName1 = "cert_1_name";
@ -53,29 +56,14 @@ class GrpcTlsCertificateDistributorTest : public ::testing::Test {
// Forward declaration.
class TlsCertificatesTestWatcher;
static grpc_core::PemKeyCertPairList MakeCertKeyPairs(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);
grpc_core::PemKeyCertPairList pem_key_cert_pairs;
pem_key_cert_pairs.emplace_back(ssl_pair);
return pem_key_cert_pairs;
}
// CredentialInfo contains the parameters when calling OnCertificatesChanged
// of a watcher. When OnCertificatesChanged is invoked, we will push a
// CredentialInfo to the cert_update_queue of state_, and check in each test
// if the status updates are correct.
struct CredentialInfo {
std::string root_certs;
grpc_core::PemKeyCertPairList key_cert_pairs;
CredentialInfo(std::string root, grpc_core::PemKeyCertPairList key_cert)
PemKeyCertPairList key_cert_pairs;
CredentialInfo(std::string root, PemKeyCertPairList key_cert)
: root_certs(std::move(root)), key_cert_pairs(std::move(key_cert)) {}
bool operator==(const CredentialInfo& other) const {
return root_certs == other.root_certs &&
@ -128,12 +116,12 @@ class GrpcTlsCertificateDistributorTest : public ::testing::Test {
void OnCertificatesChanged(
absl::optional<absl::string_view> root_certs,
absl::optional<grpc_core::PemKeyCertPairList> key_cert_pairs) override {
absl::optional<PemKeyCertPairList> key_cert_pairs) override {
std::string updated_root;
if (root_certs.has_value()) {
updated_root = std::string(*root_certs);
}
grpc_core::PemKeyCertPairList updated_identity;
PemKeyCertPairList updated_identity;
if (key_cert_pairs.has_value()) {
updated_identity = std::move(*key_cert_pairs);
}
@ -151,8 +139,7 @@ class GrpcTlsCertificateDistributorTest : public ::testing::Test {
grpc_slice root_error_slice;
GPR_ASSERT(grpc_error_get_str(
root_cert_error, GRPC_ERROR_STR_DESCRIPTION, &root_error_slice));
root_error_str =
std::string(grpc_core::StringViewFromSlice(root_error_slice));
root_error_str = std::string(StringViewFromSlice(root_error_slice));
}
if (identity_cert_error != GRPC_ERROR_NONE) {
grpc_slice identity_error_slice;
@ -160,7 +147,7 @@ class GrpcTlsCertificateDistributorTest : public ::testing::Test {
GRPC_ERROR_STR_DESCRIPTION,
&identity_error_slice));
identity_error_str =
std::string(grpc_core::StringViewFromSlice(identity_error_slice));
std::string(StringViewFromSlice(identity_error_slice));
}
state_->error_queue.emplace_back(std::move(root_error_str),
std::move(identity_error_str));
@ -202,7 +189,7 @@ class GrpcTlsCertificateDistributorTest : public ::testing::Test {
WatcherState* MakeWatcher(absl::optional<std::string> root_cert_name,
absl::optional<std::string> identity_cert_name) {
grpc_core::MutexLock lock(&mu_);
MutexLock lock(&mu_);
watchers_.emplace_back();
// TlsCertificatesTestWatcher ctor takes a pointer to the WatcherState.
// It sets WatcherState::watcher to point to itself.
@ -217,7 +204,7 @@ class GrpcTlsCertificateDistributorTest : public ::testing::Test {
}
void CancelWatch(WatcherState* state) {
grpc_core::MutexLock lock(&mu_);
MutexLock lock(&mu_);
distributor_.CancelTlsCertificatesWatch(state->watcher);
EXPECT_EQ(state->watcher, nullptr);
}
@ -234,7 +221,7 @@ class GrpcTlsCertificateDistributorTest : public ::testing::Test {
std::list<WatcherState> watchers_;
std::deque<CallbackStatus> callback_queue_;
// This is to make watchers_ and callback_queue_ thread-safe.
grpc_core::Mutex mu_;
Mutex mu_;
};
TEST_F(GrpcTlsCertificateDistributorTest, BasicCredentialBehaviors) {
@ -257,21 +244,21 @@ TEST_F(GrpcTlsCertificateDistributorTest, BasicCredentialBehaviors) {
TEST_F(GrpcTlsCertificateDistributorTest, UpdateCredentialsOnAnySide) {
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// SetKeyMaterials should trigger watcher's OnCertificatesChanged method.
distributor_.SetKeyMaterials(
kCertName1, kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Set root certs should trigger watcher's OnCertificatesChanged again.
distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt);
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Set identity certs should trigger watcher's OnCertificatesChanged again.
@ -280,7 +267,7 @@ TEST_F(GrpcTlsCertificateDistributorTest, UpdateCredentialsOnAnySide) {
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents));
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents))));
CancelWatch(watcher_state_1);
@ -292,12 +279,12 @@ TEST_F(GrpcTlsCertificateDistributorTest, SameIdentityNameDiffRootName) {
MakeWatcher(kRootCert1Name, kIdentityCert1Name);
EXPECT_THAT(
GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
::testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
CallbackStatus(kIdentityCert1Name, false, true)));
// Register watcher 2.
WatcherState* watcher_state_2 =
MakeWatcher(kRootCert2Name, kIdentityCert1Name);
EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kRootCert2Name, true, false)));
// Push credential updates to kRootCert1Name and check if the status works as
// expected.
@ -305,13 +292,13 @@ TEST_F(GrpcTlsCertificateDistributorTest, SameIdentityNameDiffRootName) {
absl::nullopt);
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Push credential updates to kRootCert2Name.
distributor_.SetKeyMaterials(kRootCert2Name, kRootCert2Contents,
absl::nullopt);
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Push credential updates to kIdentityCert1Name and check if the status works
// as expected.
distributor_.SetKeyMaterials(
@ -320,23 +307,23 @@ TEST_F(GrpcTlsCertificateDistributorTest, SameIdentityNameDiffRootName) {
// Check the updates are delivered to watcher 1 and watcher 2.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
EXPECT_THAT(
watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kRootCert1Name, false, false)));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(
GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kRootCert2Name, false, false),
::testing::ElementsAre(CallbackStatus(kRootCert2Name, false, false),
CallbackStatus(kIdentityCert1Name, false, false)));
}
@ -346,12 +333,12 @@ TEST_F(GrpcTlsCertificateDistributorTest, SameRootNameDiffIdentityName) {
MakeWatcher(kRootCert1Name, kIdentityCert1Name);
EXPECT_THAT(
GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
::testing::ElementsAre(CallbackStatus(kRootCert1Name, true, false),
CallbackStatus(kIdentityCert1Name, false, true)));
// Register watcher 2.
WatcherState* watcher_state_2 =
MakeWatcher(kRootCert1Name, kIdentityCert2Name);
EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kIdentityCert2Name, false, true)));
// Push credential updates to kRootCert1Name and check if the status works as
// expected.
@ -359,10 +346,10 @@ TEST_F(GrpcTlsCertificateDistributorTest, SameRootNameDiffIdentityName) {
absl::nullopt);
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Push credential updates to SetKeyMaterials.
distributor_.SetKeyMaterials(
kIdentityCert1Name, absl::nullopt,
@ -370,7 +357,7 @@ TEST_F(GrpcTlsCertificateDistributorTest, SameRootNameDiffIdentityName) {
// Check the updates are delivered to watcher 1.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Push credential updates to kIdentityCert2Name.
@ -380,18 +367,18 @@ TEST_F(GrpcTlsCertificateDistributorTest, SameRootNameDiffIdentityName) {
// Check the updates are delivered to watcher 2.
EXPECT_THAT(
watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents))));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre(CallbackStatus(
EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre(CallbackStatus(
kIdentityCert1Name, false, false)));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(
GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kRootCert1Name, false, false),
::testing::ElementsAre(CallbackStatus(kRootCert1Name, false, false),
CallbackStatus(kIdentityCert2Name, false, false)));
}
@ -400,7 +387,7 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// Register watcher 1 watching kCertName1 for both root and identity certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1 and check if the status works as
// expected.
distributor_.SetKeyMaterials(
@ -409,13 +396,13 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// Check the updates are delivered to watcher 1.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest,
@ -423,11 +410,11 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// Register watcher 1 watching kCertName1 for root certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, absl::nullopt);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
// Register watcher 2 watching kCertName1 for identity certs.
WatcherState* watcher_state_2 = MakeWatcher(absl::nullopt, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1 and check if the status works as
// expected.
distributor_.SetKeyMaterials(
@ -435,39 +422,39 @@ TEST_F(GrpcTlsCertificateDistributorTest,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert1PrivateKey,
kIdentityCert1Contents))));
// Push root cert updates to kCertName1.
distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt);
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Check the updates are not delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre());
// Push identity cert updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, absl::nullopt,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents));
// Check the updates are not delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert2PrivateKey,
kIdentityCert2Contents))));
watcher_state_2->cert_update_queue.clear();
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, false)));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest,
@ -475,11 +462,11 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// Register watcher 1 watching kCertName1 for identity certs.
WatcherState* watcher_state_1 = MakeWatcher(absl::nullopt, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
// Register watcher 2 watching kCertName1 for root certs.
WatcherState* watcher_state_2 = MakeWatcher(kCertName1, absl::nullopt);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1 and check if the status works as
// expected.
distributor_.SetKeyMaterials(
@ -487,38 +474,38 @@ TEST_F(GrpcTlsCertificateDistributorTest,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert1PrivateKey,
kIdentityCert1Contents))));
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert1Contents, {})));
// Push root cert updates to kCertName1.
distributor_.SetKeyMaterials(kCertName1, kRootCert2Contents, absl::nullopt);
// Check the updates are delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Check the updates are not delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
// Push identity cert updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, absl::nullopt,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents));
// Check the updates are not delivered to watcher 2.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre());
// Check the updates are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert2PrivateKey,
kIdentityCert2Contents))));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, false, true)));
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest,
@ -527,24 +514,24 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
WatcherState* watcher_state_2 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre());
EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre());
// Push credential updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents));
// Cancel watcher 2.
CancelWatch(watcher_state_2);
EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre());
EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre());
// Cancel watcher 1.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
// Register watcher 3 watching kCertName for root and identity certs.
WatcherState* watcher_state_3 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Push credential updates to kCertName1.
distributor_.SetKeyMaterials(
kCertName1, kRootCert2Contents,
@ -552,25 +539,25 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// Check the updates are delivered to watcher 3.
EXPECT_THAT(
watcher_state_3->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert2Contents,
MakeCertKeyPairs(kIdentityCert2PrivateKey, kIdentityCert2Contents))));
// Cancel watcher 3.
CancelWatch(watcher_state_3);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
::testing::ElementsAre(CallbackStatus(kCertName1, false, false)));
}
TEST_F(GrpcTlsCertificateDistributorTest, ResetCallbackToNull) {
// Register watcher 1 watching kCertName1 for root and identity certs.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName1);
EXPECT_THAT(GetCallbackQueue(),
testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
::testing::ElementsAre(CallbackStatus(kCertName1, true, true)));
// Reset callback to nullptr.
distributor_.SetWatchStatusCallback(nullptr);
// Cancel watcher 1 shouldn't trigger any callback.
CancelWatch(watcher_state_1);
EXPECT_THAT(GetCallbackQueue(), testing::ElementsAre());
EXPECT_THAT(GetCallbackQueue(), ::testing::ElementsAre());
}
TEST_F(GrpcTlsCertificateDistributorTest, SetKeyMaterialsInCallback) {
@ -586,7 +573,7 @@ TEST_F(GrpcTlsCertificateDistributorTest, SetKeyMaterialsInCallback) {
// Check the updates are delivered to watcher 1.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents, MakeCertKeyPairs(kIdentityCert1PrivateKey,
kIdentityCert1Contents))));
CancelWatch(watcher_state_1);
@ -621,7 +608,7 @@ TEST_F(GrpcTlsCertificateDistributorTest, WatchACertInfoWithValidCredentials) {
// watcher 1 should receive the credentials right away.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
CancelWatch(watcher_state_1);
@ -629,13 +616,13 @@ TEST_F(GrpcTlsCertificateDistributorTest, WatchACertInfoWithValidCredentials) {
WatcherState* watcher_state_2 = MakeWatcher(kRootCert2Name, absl::nullopt);
// watcher 2 should receive the root credentials right away.
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
::testing::ElementsAre(CredentialInfo(kRootCert2Contents, {})));
// Register watcher 3.
WatcherState* watcher_state_3 =
MakeWatcher(absl::nullopt, kIdentityCert2Name);
// watcher 3 should received the identity credentials right away.
EXPECT_THAT(watcher_state_3->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(kIdentityCert2PrivateKey,
kIdentityCert2Contents))));
CancelWatch(watcher_state_2);
@ -652,7 +639,7 @@ TEST_F(GrpcTlsCertificateDistributorTest,
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(
::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
// Calling SetErrorForCert on root cert name should call OnError
// on watcher 1 again.
@ -661,14 +648,14 @@ TEST_F(GrpcTlsCertificateDistributorTest,
absl::nullopt);
EXPECT_THAT(
watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kErrorMessage, kIdentityErrorMessage)));
::testing::ElementsAre(ErrorInfo(kErrorMessage, kIdentityErrorMessage)));
// Calling SetErrorForCert on identity cert name should call OnError
// on watcher 1 again.
distributor_.SetErrorForCert(
kCertName1, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
::testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
distributor_.CancelTlsCertificatesWatch(watcher_state_1->watcher);
EXPECT_EQ(watcher_state_1->watcher, nullptr);
}
@ -682,18 +669,18 @@ TEST_F(GrpcTlsCertificateDistributorTest, SetErrorForCertForRootOrIdentity) {
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
// Calling SetErrorForCert on identity name should do nothing.
distributor_.SetErrorForCert(
kCertName1, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on both names should still get one OnError call.
distributor_.SetErrorForCert(
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
CancelWatch(watcher_state_1);
// Register watcher 2.
WatcherState* watcher_state_2 = MakeWatcher(absl::nullopt, kCertName1);
@ -703,18 +690,18 @@ TEST_F(GrpcTlsCertificateDistributorTest, SetErrorForCertForRootOrIdentity) {
kCertName1, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
// Calling SetErrorForCert on root name should do nothing.
distributor_.SetErrorForCert(
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on both names should still get one OnError call.
distributor_.SetErrorForCert(
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
CancelWatch(watcher_state_2);
}
@ -728,14 +715,14 @@ TEST_F(GrpcTlsCertificateDistributorTest,
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName2);
// Should trigger OnError call right away since kCertName1 has error.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
// Calling SetErrorForCert on kCertName2 should trigger OnError with both
// errors, because kCertName1 also has error.
distributor_.SetErrorForCert(
kCertName2, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(
::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
}
@ -750,14 +737,14 @@ TEST_F(GrpcTlsCertificateDistributorTest,
WatcherState* watcher_state_1 = MakeWatcher(kCertName2, kCertName1);
// Should trigger OnError call right away since kCertName2 has error.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
// Calling SetErrorForCert on kCertName2 should trigger OnError with both
// errors, because kCertName1 also has error.
distributor_.SetErrorForCert(
kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(
::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
}
@ -767,25 +754,25 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// Register watcher 1 for kCertName1 as root and kCertName2 as identity.
WatcherState* watcher_state_1 = MakeWatcher(kCertName1, kCertName2);
// Should not trigger OnError.
EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
// Register watcher 2 for kCertName2 as identity and a non-existing name
// kRootCert1Name as root.
WatcherState* watcher_state_2 = MakeWatcher(kRootCert1Name, kCertName2);
// Should not trigger OnError.
EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, absl::nullopt,
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_2->error_queue,
testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
::testing::ElementsAre(ErrorInfo("", kIdentityErrorMessage)));
CancelWatch(watcher_state_2);
}
@ -793,25 +780,25 @@ TEST_F(GrpcTlsCertificateDistributorTest,
SetErrorForRootNameWithPreexistingErrorForIdentityName) {
WatcherState* watcher_state_1 = MakeWatcher(kCertName2, kCertName1);
// Should not trigger OnError.
EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
CancelWatch(watcher_state_1);
// Register watcher 2 for kCertName2 as root and a non-existing name
// kIdentityCert1Name as identity.
WatcherState* watcher_state_2 = MakeWatcher(kCertName2, kIdentityCert1Name);
// Should not trigger OnError.
EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
// Calling SetErrorForCert on kCertName2 should trigger OnError.
distributor_.SetErrorForCert(
kCertName2, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
absl::nullopt);
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
::testing::ElementsAre(ErrorInfo(kRootErrorMessage, "")));
CancelWatch(watcher_state_2);
}
@ -825,14 +812,14 @@ TEST_F(GrpcTlsCertificateDistributorTest,
kCertName1, GRPC_ERROR_CREATE_FROM_STATIC_STRING(kRootErrorMessage),
GRPC_ERROR_CREATE_FROM_STATIC_STRING(kIdentityErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(
::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
// When watcher 1 is removed, the cert info entry should be removed.
CancelWatch(watcher_state_1);
// Register watcher 2 on the same cert name.
WatcherState* watcher_state_2 = MakeWatcher(kCertName1, kCertName1);
// Should not trigger OnError call on watcher 2 right away.
EXPECT_THAT(watcher_state_2->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_2->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_2);
}
@ -851,11 +838,11 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// watcher 1 should receive both the old credentials and the error right away.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(
::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
}
@ -876,10 +863,10 @@ TEST_F(GrpcTlsCertificateDistributorTest,
// the previous error is wiped out by a successful update.
EXPECT_THAT(
watcher_state_1->GetCredentialQueue(),
testing::ElementsAre(CredentialInfo(
::testing::ElementsAre(CredentialInfo(
kRootCert1Contents,
MakeCertKeyPairs(kIdentityCert1PrivateKey, kIdentityCert1Contents))));
EXPECT_THAT(watcher_state_1->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_1->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_1);
}
@ -893,11 +880,11 @@ TEST_F(GrpcTlsCertificateDistributorTest, WatchCertInfoThenInvokeSetError) {
MakeWatcher(absl::nullopt, kIdentityCert1Name);
distributor_.SetError(GRPC_ERROR_CREATE_FROM_STATIC_STRING(kErrorMessage));
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
::testing::ElementsAre(ErrorInfo(kErrorMessage, kErrorMessage)));
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
testing::ElementsAre(ErrorInfo(kErrorMessage, "")));
::testing::ElementsAre(ErrorInfo(kErrorMessage, "")));
EXPECT_THAT(watcher_state_3->GetErrorQueue(),
testing::ElementsAre(ErrorInfo("", kErrorMessage)));
::testing::ElementsAre(ErrorInfo("", kErrorMessage)));
CancelWatch(watcher_state_1);
CancelWatch(watcher_state_2);
CancelWatch(watcher_state_3);
@ -915,12 +902,12 @@ TEST_F(GrpcTlsCertificateDistributorTest, WatchErroredCertInfoBySetError) {
// Register watcher 3 watching kCertName1 as root and kCertName2 as identity
// should not get the error updates.
WatcherState* watcher_state_3 = MakeWatcher(kCertName1, kCertName2);
EXPECT_THAT(watcher_state_3->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_3->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_3);
// Register watcher 4 watching kCertName2 as root and kCertName1 as identity
// should not get the error updates.
WatcherState* watcher_state_4 = MakeWatcher(kCertName2, kCertName1);
EXPECT_THAT(watcher_state_4->GetErrorQueue(), testing::ElementsAre());
EXPECT_THAT(watcher_state_4->GetErrorQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_4);
}
@ -936,7 +923,7 @@ TEST_F(GrpcTlsCertificateDistributorTest, SetErrorForCertInCallback) {
WatcherState* watcher_state_1 = MakeWatcher(cert_name, cert_name);
// Check the errors are delivered to watcher 1.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
testing::ElementsAre(
::testing::ElementsAre(
ErrorInfo(kRootErrorMessage, kIdentityErrorMessage)));
CancelWatch(watcher_state_1);
};
@ -955,6 +942,8 @@ TEST_F(GrpcTlsCertificateDistributorTest, SetErrorForCertInCallback) {
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(argc, argv);
::testing::InitGoogleTest(&argc, argv);

@ -0,0 +1,509 @@
//
// 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 "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
#include <gmock/gmock.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <gtest/gtest.h>
#include <deque>
#include <list>
#include "src/core/lib/gpr/tmpfile.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/slice/slice_internal.h"
#include "test/core/security/tls_utils.h"
#include "test/core/util/test_config.h"
#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
#define CA_CERT_PATH_2 "src/core/tsi/test_creds/multi-domain.pem"
#define SERVER_CERT_PATH_2 "src/core/tsi/test_creds/server0.pem"
#define SERVER_KEY_PATH_2 "src/core/tsi/test_creds/server0.key"
#define INVALID_PATH "invalid/path"
namespace grpc_core {
namespace testing {
constexpr const char* kCertName = "cert_name";
constexpr const char* kRootError = "Unable to get latest root certificates.";
constexpr const char* kIdentityError =
"Unable to get latest identity certificates.";
class GrpcTlsCertificateProviderTest : public ::testing::Test {
protected:
// Forward declaration.
class TlsCertificatesTestWatcher;
// CredentialInfo contains the parameters when calling OnCertificatesChanged
// of a watcher. When OnCertificatesChanged is invoked, we will push a
// CredentialInfo to the cert_update_queue of state_, and check in each test
// if the status updates are correct.
struct CredentialInfo {
std::string root_certs;
PemKeyCertPairList key_cert_pairs;
CredentialInfo(std::string root, PemKeyCertPairList key_cert)
: root_certs(std::move(root)), key_cert_pairs(std::move(key_cert)) {}
bool operator==(const CredentialInfo& other) const {
return root_certs == other.root_certs &&
key_cert_pairs == other.key_cert_pairs;
}
};
// ErrorInfo contains the parameters when calling OnError of a watcher. When
// OnError is invoked, we will push a ErrorInfo to the error_queue of state_,
// and check in each test if the status updates are correct.
struct ErrorInfo {
std::string root_cert_str;
std::string identity_cert_str;
ErrorInfo(std::string root, std::string identity)
: root_cert_str(std::move(root)),
identity_cert_str(std::move(identity)) {}
bool operator==(const ErrorInfo& other) const {
return root_cert_str == other.root_cert_str &&
identity_cert_str == other.identity_cert_str;
}
};
struct WatcherState {
TlsCertificatesTestWatcher* watcher = nullptr;
std::deque<CredentialInfo> cert_update_queue;
std::deque<ErrorInfo> error_queue;
Mutex mu;
std::deque<CredentialInfo> GetCredentialQueue() {
// We move the data member value so the data member will be re-initiated
// with size 0, and ready for the next check.
MutexLock lock(&mu);
return std::move(cert_update_queue);
}
std::deque<ErrorInfo> GetErrorQueue() {
// We move the data member value so the data member will be re-initiated
// with size 0, and ready for the next check.
MutexLock lock(&mu);
return std::move(error_queue);
}
};
class TlsCertificatesTestWatcher : public grpc_tls_certificate_distributor::
TlsCertificatesWatcherInterface {
public:
// ctor sets state->watcher to this.
explicit TlsCertificatesTestWatcher(WatcherState* state) : state_(state) {
state_->watcher = this;
}
// dtor sets state->watcher to nullptr.
~TlsCertificatesTestWatcher() override { state_->watcher = nullptr; }
void OnCertificatesChanged(
absl::optional<absl::string_view> root_certs,
absl::optional<PemKeyCertPairList> key_cert_pairs) override {
MutexLock lock(&state_->mu);
std::string updated_root;
if (root_certs.has_value()) {
updated_root = std::string(*root_certs);
}
PemKeyCertPairList updated_identity;
if (key_cert_pairs.has_value()) {
updated_identity = std::move(*key_cert_pairs);
}
state_->cert_update_queue.emplace_back(std::move(updated_root),
std::move(updated_identity));
}
void OnError(grpc_error* root_cert_error,
grpc_error* identity_cert_error) override {
MutexLock lock(&state_->mu);
GPR_ASSERT(root_cert_error != GRPC_ERROR_NONE ||
identity_cert_error != GRPC_ERROR_NONE);
std::string root_error_str;
std::string identity_error_str;
if (root_cert_error != GRPC_ERROR_NONE) {
grpc_slice root_error_slice;
GPR_ASSERT(grpc_error_get_str(
root_cert_error, GRPC_ERROR_STR_DESCRIPTION, &root_error_slice));
root_error_str = std::string(StringViewFromSlice(root_error_slice));
}
if (identity_cert_error != GRPC_ERROR_NONE) {
grpc_slice identity_error_slice;
GPR_ASSERT(grpc_error_get_str(identity_cert_error,
GRPC_ERROR_STR_DESCRIPTION,
&identity_error_slice));
identity_error_str =
std::string(StringViewFromSlice(identity_error_slice));
}
state_->error_queue.emplace_back(std::move(root_error_str),
std::move(identity_error_str));
GRPC_ERROR_UNREF(root_cert_error);
GRPC_ERROR_UNREF(identity_cert_error);
}
private:
WatcherState* state_;
};
void SetUp() override {
root_cert_ = GetFileContents(CA_CERT_PATH);
cert_chain_ = GetFileContents(SERVER_CERT_PATH);
private_key_ = GetFileContents(SERVER_KEY_PATH);
root_cert_2_ = GetFileContents(CA_CERT_PATH_2);
cert_chain_2_ = GetFileContents(SERVER_CERT_PATH_2);
private_key_2_ = GetFileContents(SERVER_KEY_PATH_2);
}
WatcherState* MakeWatcher(
RefCountedPtr<grpc_tls_certificate_distributor> distributor,
absl::optional<std::string> root_cert_name,
absl::optional<std::string> identity_cert_name) {
MutexLock lock(&mu_);
distributor_ = distributor;
watchers_.emplace_back();
// TlsCertificatesTestWatcher ctor takes a pointer to the WatcherState.
// It sets WatcherState::watcher to point to itself.
// The TlsCertificatesTestWatcher dtor will set WatcherState::watcher back
// to nullptr to indicate that it's been destroyed.
auto watcher =
absl::make_unique<TlsCertificatesTestWatcher>(&watchers_.back());
distributor_->WatchTlsCertificates(std::move(watcher),
std::move(root_cert_name),
std::move(identity_cert_name));
return &watchers_.back();
}
void CancelWatch(WatcherState* state) {
MutexLock lock(&mu_);
distributor_->CancelTlsCertificatesWatch(state->watcher);
EXPECT_EQ(state->watcher, nullptr);
}
std::string root_cert_;
std::string private_key_;
std::string cert_chain_;
std::string root_cert_2_;
std::string private_key_2_;
std::string cert_chain_2_;
RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
// Use a std::list<> here to avoid the address invalidation caused by internal
// reallocation of std::vector<>.
std::list<WatcherState> watchers_;
// This is to make watchers_ thread-safe.
Mutex mu_;
};
TEST_F(GrpcTlsCertificateProviderTest, StaticDataCertificateProviderCreation) {
StaticDataCertificateProvider provider(
root_cert_, MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
// Watcher watching both root and identity certs.
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
CancelWatch(watcher_state_1);
// Watcher watching only root certs.
WatcherState* watcher_state_2 =
MakeWatcher(provider.distributor(), kCertName, absl::nullopt);
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(root_cert_, {})));
CancelWatch(watcher_state_2);
// Watcher watching only identity certs.
WatcherState* watcher_state_3 =
MakeWatcher(provider.distributor(), absl::nullopt, kCertName);
EXPECT_THAT(
watcher_state_3->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()))));
CancelWatch(watcher_state_3);
}
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderWithGoodPaths) {
FileWatcherCertificateProvider provider(SERVER_KEY_PATH, SERVER_CERT_PATH,
CA_CERT_PATH, 1);
// Watcher watching both root and identity certs.
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
CancelWatch(watcher_state_1);
// Watcher watching only root certs.
WatcherState* watcher_state_2 =
MakeWatcher(provider.distributor(), kCertName, absl::nullopt);
EXPECT_THAT(watcher_state_2->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(root_cert_, {})));
CancelWatch(watcher_state_2);
// Watcher watching only identity certs.
WatcherState* watcher_state_3 =
MakeWatcher(provider.distributor(), absl::nullopt, kCertName);
EXPECT_THAT(
watcher_state_3->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
"", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()))));
CancelWatch(watcher_state_3);
}
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderWithBadPaths) {
FileWatcherCertificateProvider provider(INVALID_PATH, INVALID_PATH,
INVALID_PATH, 1);
// Watcher watching both root and identity certs.
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
::testing::ElementsAre(ErrorInfo(kRootError, kIdentityError)));
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_1);
// Watcher watching only root certs.
WatcherState* watcher_state_2 =
MakeWatcher(provider.distributor(), kCertName, absl::nullopt);
EXPECT_THAT(watcher_state_2->GetErrorQueue(),
::testing::ElementsAre(ErrorInfo(kRootError, "")));
EXPECT_THAT(watcher_state_2->GetCredentialQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_2);
// Watcher watching only identity certs.
WatcherState* watcher_state_3 =
MakeWatcher(provider.distributor(), absl::nullopt, kCertName);
EXPECT_THAT(watcher_state_3->GetErrorQueue(),
::testing::ElementsAre(ErrorInfo("", kIdentityError)));
EXPECT_THAT(watcher_state_3->GetCredentialQueue(), ::testing::ElementsAre());
CancelWatch(watcher_state_3);
}
// The following tests write credential data to temporary files to test the
// transition behavior of the provider.
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderOnBothCertsRefreshed) {
// Create temporary files and copy cert data into them.
TmpFile tmp_root_cert(root_cert_);
TmpFile tmp_identity_key(private_key_);
TmpFile tmp_identity_cert(cert_chain_);
// Create FileWatcherCertificateProvider.
FileWatcherCertificateProvider provider(tmp_identity_key.name(),
tmp_identity_cert.name(),
tmp_root_cert.name(), 1);
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
// Expect to see the credential data.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
// Copy new data to files.
// TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
// update when the directory renaming is added in gpr.
tmp_root_cert.RewriteFile(root_cert_2_);
tmp_identity_key.RewriteFile(private_key_2_);
tmp_identity_cert.RewriteFile(cert_chain_2_);
// Wait 2 seconds for the provider's refresh thread to read the updated files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// Expect to see the new credential data.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_2_, MakeCertKeyPairs(private_key_2_.c_str(),
cert_chain_2_.c_str()))));
// Clean up.
CancelWatch(watcher_state_1);
}
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderOnRootCertsRefreshed) {
// Create temporary files and copy cert data into them.
TmpFile tmp_root_cert(root_cert_);
TmpFile tmp_identity_key(private_key_);
TmpFile tmp_identity_cert(cert_chain_);
// Create FileWatcherCertificateProvider.
FileWatcherCertificateProvider provider(tmp_identity_key.name(),
tmp_identity_cert.name(),
tmp_root_cert.name(), 1);
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
// Expect to see the credential data.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
// Copy new data to files.
// TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
// update when the directory renaming is added in gpr.
tmp_root_cert.RewriteFile(root_cert_2_);
// Wait 2 seconds for the provider's refresh thread to read the updated files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// Expect to see the new credential data.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_2_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
// Clean up.
CancelWatch(watcher_state_1);
}
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderOnIdentityCertsRefreshed) {
// Create temporary files and copy cert data into them.
TmpFile tmp_root_cert(root_cert_);
TmpFile tmp_identity_key(private_key_);
TmpFile tmp_identity_cert(cert_chain_);
// Create FileWatcherCertificateProvider.
FileWatcherCertificateProvider provider(tmp_identity_key.name(),
tmp_identity_cert.name(),
tmp_root_cert.name(), 1);
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
// Expect to see the credential data.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
// Copy new data to files.
// TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
// update when the directory renaming is added in gpr.
tmp_identity_key.RewriteFile(private_key_2_);
tmp_identity_cert.RewriteFile(cert_chain_2_);
// Wait 2 seconds for the provider's refresh thread to read the updated files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// Expect to see the new credential data.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_2_.c_str(),
cert_chain_2_.c_str()))));
// Clean up.
CancelWatch(watcher_state_1);
}
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderWithGoodAtFirstThenDeletedBothCerts) {
// Create temporary files and copy cert data into it.
auto tmp_root_cert = absl::make_unique<TmpFile>(root_cert_);
auto tmp_identity_key = absl::make_unique<TmpFile>(private_key_);
auto tmp_identity_cert = absl::make_unique<TmpFile>(cert_chain_);
// Create FileWatcherCertificateProvider.
FileWatcherCertificateProvider provider(tmp_identity_key->name(),
tmp_identity_cert->name(),
tmp_root_cert->name(), 1);
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
// The initial data is all good, so we expect to have successful credential
// updates.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
// Delete TmpFile objects, which will remove the corresponding files.
tmp_root_cert.reset();
tmp_identity_key.reset();
tmp_identity_cert.reset();
// Wait 2 seconds for the provider's refresh thread to read the deleted files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// Expect to see errors sent to watchers, and no credential updates.
// We have no ideas on how many errors we will receive, so we only check once.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
::testing::Contains(ErrorInfo(kRootError, kIdentityError)));
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
// Clean up.
CancelWatch(watcher_state_1);
}
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderWithGoodAtFirstThenDeletedRootCerts) {
// Create temporary files and copy cert data into it.
auto tmp_root_cert = absl::make_unique<TmpFile>(root_cert_);
TmpFile tmp_identity_key(private_key_);
TmpFile tmp_identity_cert(cert_chain_);
// Create FileWatcherCertificateProvider.
FileWatcherCertificateProvider provider(tmp_identity_key.name(),
tmp_identity_cert.name(),
tmp_root_cert->name(), 1);
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
// The initial data is all good, so we expect to have successful credential
// updates.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
// Delete root TmpFile object, which will remove the corresponding file.
tmp_root_cert.reset();
// Wait 2 seconds for the provider's refresh thread to read the deleted files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// Expect to see errors sent to watchers, and no credential updates.
// We have no ideas on how many errors we will receive, so we only check once.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
::testing::Contains(ErrorInfo(kRootError, "")));
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
// Clean up.
CancelWatch(watcher_state_1);
}
TEST_F(GrpcTlsCertificateProviderTest,
FileWatcherCertificateProviderWithGoodAtFirstThenDeletedIdentityCerts) {
// Create temporary files and copy cert data into it.
TmpFile tmp_root_cert(root_cert_);
auto tmp_identity_key = absl::make_unique<TmpFile>(private_key_);
auto tmp_identity_cert = absl::make_unique<TmpFile>(cert_chain_);
// Create FileWatcherCertificateProvider.
FileWatcherCertificateProvider provider(tmp_identity_key->name(),
tmp_identity_cert->name(),
tmp_root_cert.name(), 1);
WatcherState* watcher_state_1 =
MakeWatcher(provider.distributor(), kCertName, kCertName);
// The initial data is all good, so we expect to have successful credential
// updates.
EXPECT_THAT(watcher_state_1->GetCredentialQueue(),
::testing::ElementsAre(CredentialInfo(
root_cert_, MakeCertKeyPairs(private_key_.c_str(),
cert_chain_.c_str()))));
// Delete identity TmpFile objects, which will remove the corresponding files.
tmp_identity_key.reset();
tmp_identity_cert.reset();
// Wait 2 seconds for the provider's refresh thread to read the deleted files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// Expect to see errors sent to watchers, and no credential updates.
// We have no ideas on how many errors we will receive, so we only check once.
EXPECT_THAT(watcher_state_1->GetErrorQueue(),
::testing::Contains(ErrorInfo("", kIdentityError)));
EXPECT_THAT(watcher_state_1->GetCredentialQueue(), ::testing::ElementsAre());
// Clean up.
CancelWatch(watcher_state_1);
}
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(argc, argv);
::testing::InitGoogleTest(&argc, argv);
grpc_init();
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}

@ -24,24 +24,440 @@
#include <grpc/support/string_util.h>
#include <gtest/gtest.h>
#include "src/core/lib/gpr/tmpfile.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/security/credentials/tls/tls_credentials.h"
#include "src/core/lib/security/security_connector/tls/tls_security_connector.h"
#include "test/core/security/tls_utils.h"
#include "test/core/util/test_config.h"
#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
#define CA_CERT_PATH_2 "src/core/tsi/test_creds/multi-domain.pem"
#define SERVER_CERT_PATH_2 "src/core/tsi/test_creds/server0.pem"
#define SERVER_KEY_PATH_2 "src/core/tsi/test_creds/server0.key"
#define INVALID_PATH "invalid/path"
namespace grpc_core {
namespace testing {
TEST(GrpcTlsCredentialsOptionsTest, ErrorDetails) {
class GrpcTlsCredentialsOptionsTest : public ::testing::Test {
protected:
void SetUp() override {
root_cert_ = GetFileContents(CA_CERT_PATH);
cert_chain_ = GetFileContents(SERVER_CERT_PATH);
private_key_ = GetFileContents(SERVER_KEY_PATH);
root_cert_2_ = GetFileContents(CA_CERT_PATH_2);
cert_chain_2_ = GetFileContents(SERVER_CERT_PATH_2);
private_key_2_ = GetFileContents(SERVER_KEY_PATH_2);
}
std::string root_cert_;
std::string private_key_;
std::string cert_chain_;
std::string root_cert_2_;
std::string private_key_2_;
std::string cert_chain_2_;
};
TEST_F(GrpcTlsCredentialsOptionsTest, ErrorDetails) {
grpc_tls_error_details error_details;
EXPECT_STREQ(error_details.error_details().c_str(), "");
error_details.set_error_details("test error details");
EXPECT_STREQ(error_details.error_details().c_str(), "test error details");
}
// Tests for StaticDataCertificateProvider.
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithStaticDataProviderOnBothCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<StaticDataCertificateProvider>(
root_cert_, MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_watch_identity_pair(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithStaticDataProviderOnRootCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<StaticDataCertificateProvider>(
root_cert_, PemKeyCertPairList());
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_FALSE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithStaticDataProviderOnNotProvidedCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<StaticDataCertificateProvider>(
"", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
EXPECT_EQ(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ServerOptionsWithStaticDataProviderOnBothCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<StaticDataCertificateProvider>(
root_cert_, MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_watch_identity_pair(true);
options->set_cert_request_type(
GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
auto credentials = MakeRefCounted<TlsServerCredentials>(options);
ASSERT_NE(credentials, nullptr);
auto connector = credentials->create_security_connector();
ASSERT_NE(connector, nullptr);
TlsServerSecurityConnector* tls_connector =
static_cast<TlsServerSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ServerOptionsWithStaticDataProviderOnIdentityCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<StaticDataCertificateProvider>(
"", MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
options->set_certificate_provider(std::move(provider));
options->set_watch_identity_pair(true);
options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
auto credentials = MakeRefCounted<TlsServerCredentials>(options);
ASSERT_NE(credentials, nullptr);
auto connector = credentials->create_security_connector();
ASSERT_NE(connector, nullptr);
TlsServerSecurityConnector* tls_connector =
static_cast<TlsServerSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
EXPECT_FALSE(tls_connector->RootCertsForTesting().has_value());
EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ServerOptionsWithStaticDataProviderOnNotProvidedCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<StaticDataCertificateProvider>(
root_cert_, PemKeyCertPairList());
options->set_certificate_provider(std::move(provider));
options->set_watch_identity_pair(true);
options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
auto credentials = MakeRefCounted<TlsServerCredentials>(options);
ASSERT_NE(credentials, nullptr);
auto connector = credentials->create_security_connector();
ASSERT_NE(connector, nullptr);
TlsServerSecurityConnector* tls_connector =
static_cast<TlsServerSecurityConnector*>(connector.get());
EXPECT_EQ(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
}
//// Tests for FileWatcherCertificateProvider.
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithCertWatcherProviderOnBothCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_watch_identity_pair(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithCertWatcherProviderOnRootCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider =
MakeRefCounted<FileWatcherCertificateProvider>("", "", CA_CERT_PATH, 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_FALSE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithCertWatcherProviderOnNotProvidedCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
SERVER_KEY_PATH, SERVER_CERT_PATH, "", 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
EXPECT_EQ(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithCertWatcherProviderOnBadTrustCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider =
MakeRefCounted<FileWatcherCertificateProvider>("", "", INVALID_PATH, 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
EXPECT_EQ(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ServerOptionsWithCertWatcherProviderOnBothCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_watch_identity_pair(true);
options->set_cert_request_type(
GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
auto credentials = MakeRefCounted<TlsServerCredentials>(options);
ASSERT_NE(credentials, nullptr);
auto connector = credentials->create_security_connector();
ASSERT_NE(connector, nullptr);
TlsServerSecurityConnector* tls_connector =
static_cast<TlsServerSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
EXPECT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ServerOptionsWithCertWatcherProviderOnIdentityCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
SERVER_KEY_PATH, SERVER_CERT_PATH, "", 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_identity_pair(true);
options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
auto credentials = MakeRefCounted<TlsServerCredentials>(options);
ASSERT_NE(credentials, nullptr);
auto connector = credentials->create_security_connector();
ASSERT_NE(connector, nullptr);
TlsServerSecurityConnector* tls_connector =
static_cast<TlsServerSecurityConnector*>(connector.get());
EXPECT_NE(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
EXPECT_FALSE(tls_connector->RootCertsForTesting().has_value());
EXPECT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ServerOptionsWithCertWatcherProviderOnNotProvidedCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider =
MakeRefCounted<FileWatcherCertificateProvider>("", "", CA_CERT_PATH, 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_identity_pair(true);
options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
auto credentials = MakeRefCounted<TlsServerCredentials>(options);
ASSERT_NE(credentials, nullptr);
auto connector = credentials->create_security_connector();
ASSERT_NE(connector, nullptr);
TlsServerSecurityConnector* tls_connector =
static_cast<TlsServerSecurityConnector*>(connector.get());
EXPECT_EQ(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ServerOptionsWithCertWatcherProviderOnBadIdentityCerts) {
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
INVALID_PATH, INVALID_PATH, "", 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_identity_pair(true);
options->set_cert_request_type(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE);
auto credentials = MakeRefCounted<TlsServerCredentials>(options);
ASSERT_NE(credentials, nullptr);
auto connector = credentials->create_security_connector();
ASSERT_NE(connector, nullptr);
TlsServerSecurityConnector* tls_connector =
static_cast<TlsServerSecurityConnector*>(connector.get());
EXPECT_EQ(tls_connector->ServerHandshakerFactoryForTesting(), nullptr);
}
// The following tests write credential data to temporary files to test the
// transition behavior of the provider.
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithCertWatcherProviderOnCertificateRefreshed) {
// Create temporary files and copy cert data into them.
TmpFile tmp_root_cert(root_cert_);
TmpFile tmp_identity_key(private_key_);
TmpFile tmp_identity_cert(cert_chain_);
// Create ClientOptions using FileWatcherCertificateProvider.
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
tmp_identity_key.name(), tmp_identity_cert.name(), tmp_root_cert.name(),
1);
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_watch_identity_pair(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
// Expect to see the credential data.
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_);
ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
// Copy new data to files.
// TODO(ZhenLian): right now it is not completely atomic. Use the real atomic
// update when the directory renaming is added in gpr.
tmp_root_cert.RewriteFile(root_cert_2_);
tmp_identity_key.RewriteFile(private_key_2_);
tmp_identity_cert.RewriteFile(cert_chain_2_);
// Wait 2 seconds for the provider's refresh thread to read the updated files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// Expect to see new credential data loaded by the security connector.
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_2_);
ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
MakeCertKeyPairs(private_key_2_.c_str(), cert_chain_2_.c_str()));
}
TEST_F(GrpcTlsCredentialsOptionsTest,
ClientOptionsWithCertWatcherProviderOnDeletedFiles) {
// Create temporary files and copy cert data into it.
auto tmp_root_cert = absl::make_unique<TmpFile>(root_cert_);
auto tmp_identity_key = absl::make_unique<TmpFile>(private_key_);
auto tmp_identity_cert = absl::make_unique<TmpFile>(cert_chain_);
// Create ClientOptions using FileWatcherCertificateProvider.
auto options = MakeRefCounted<grpc_tls_credentials_options>();
auto provider = MakeRefCounted<FileWatcherCertificateProvider>(
tmp_identity_key->name(), tmp_identity_cert->name(),
tmp_root_cert->name(), 1);
options->set_certificate_provider(std::move(provider));
options->set_watch_root_cert(true);
options->set_watch_identity_pair(true);
options->set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto credentials = MakeRefCounted<TlsCredentials>(options);
ASSERT_NE(credentials, nullptr);
grpc_channel_args* new_args = nullptr;
auto connector = credentials->create_security_connector(
nullptr, "random targets", nullptr, &new_args);
grpc_channel_args_destroy(new_args);
ASSERT_NE(connector, nullptr);
TlsChannelSecurityConnector* tls_connector =
static_cast<TlsChannelSecurityConnector*>(connector.get());
// The initial data is all good, so we expect to have successful credential
// updates.
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_);
ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
// Delete TmpFile objects, which will remove the corresponding files.
tmp_root_cert.reset();
tmp_identity_key.reset();
tmp_identity_cert.reset();
// Wait 2 seconds for the provider's refresh thread to read the deleted files.
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
gpr_time_from_seconds(2, GPR_TIMESPAN)));
// It's a bit hard to test if errors are sent to the security connector,
// because the security connector simply logs the error. We will see the err
// messages if we open the log.
// The old certs should still being used.
EXPECT_NE(tls_connector->ClientHandshakerFactoryForTesting(), nullptr);
ASSERT_TRUE(tls_connector->RootCertsForTesting().has_value());
EXPECT_EQ(tls_connector->RootCertsForTesting(), root_cert_);
ASSERT_TRUE(tls_connector->KeyCertPairListForTesting().has_value());
EXPECT_EQ(tls_connector->KeyCertPairListForTesting(),
MakeCertKeyPairs(private_key_.c_str(), cert_chain_.c_str()));
}
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(argc, argv);
::testing::InitGoogleTest(&argc, argv);

@ -0,0 +1,83 @@
//
// 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 "test/core/security/tls_utils.h"
#include "src/core/lib/gpr/tmpfile.h"
#include "src/core/lib/iomgr/load_file.h"
#include "src/core/lib/slice/slice_internal.h"
namespace grpc_core {
namespace testing {
TmpFile::TmpFile(absl::string_view credential_data) {
name_ = CreateTmpFileAndWriteData(credential_data);
GPR_ASSERT(!name_.empty());
}
TmpFile::~TmpFile() { GPR_ASSERT(remove(name_.c_str()) == 0); }
void TmpFile::RewriteFile(absl::string_view credential_data) {
// Create a new file containing new data.
std::string new_name = CreateTmpFileAndWriteData(credential_data);
GPR_ASSERT(!new_name.empty());
// Remove the old file.
GPR_ASSERT(remove(name_.c_str()) == 0);
// Rename the new file to the original name.
GPR_ASSERT(rename(new_name.c_str(), name_.c_str()) == 0);
}
std::string TmpFile::CreateTmpFileAndWriteData(
absl::string_view credential_data) {
char* name = nullptr;
FILE* file_descriptor = gpr_tmpfile("GrpcTlsCertificateProviderTest", &name);
GPR_ASSERT(fwrite(credential_data.data(), 1, credential_data.size(),
file_descriptor) == credential_data.size());
GPR_ASSERT(fclose(file_descriptor) == 0);
GPR_ASSERT(file_descriptor != nullptr);
GPR_ASSERT(name != nullptr);
std::string name_to_return = name;
gpr_free(name);
return name_to_return;
}
PemKeyCertPairList MakeCertKeyPairs(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);
PemKeyCertPairList pem_key_cert_pairs;
pem_key_cert_pairs.emplace_back(ssl_pair);
return pem_key_cert_pairs;
}
std::string GetFileContents(const char* path) {
grpc_slice slice = grpc_empty_slice();
GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", grpc_load_file(path, 0, &slice)));
std::string credential = std::string(StringViewFromSlice(slice));
grpc_slice_unref(slice);
return credential;
}
} // namespace testing
} // namespace grpc_core

@ -0,0 +1,47 @@
//
// 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 "src/core/lib/security/security_connector/ssl_utils.h"
namespace grpc_core {
namespace testing {
class TmpFile {
public:
// Create a temporary file with |credential_data| written in.
explicit TmpFile(absl::string_view credential_data);
~TmpFile();
const std::string& name() { return name_; }
// Rewrite |credential_data| to the temporary file, in an atomic way.
void RewriteFile(absl::string_view credential_data);
private:
std::string CreateTmpFileAndWriteData(absl::string_view credential_data);
std::string name_;
};
PemKeyCertPairList MakeCertKeyPairs(const char* private_key, const char* certs);
std::string GetFileContents(const char* path);
} // namespace testing
} // namespace grpc_core

@ -206,6 +206,7 @@ int main(int argc, char **argv) {
printf("%lx", (unsigned long) grpc_tls_identity_pairs_add_pair);
printf("%lx", (unsigned long) grpc_tls_identity_pairs_destroy);
printf("%lx", (unsigned long) grpc_tls_certificate_provider_static_data_create);
printf("%lx", (unsigned long) grpc_tls_certificate_provider_file_watcher_create);
printf("%lx", (unsigned long) grpc_tls_certificate_provider_release);
printf("%lx", (unsigned long) grpc_tls_credentials_options_create);
printf("%lx", (unsigned long) grpc_tls_credentials_options_set_cert_request_type);

@ -21,6 +21,11 @@ grpc_package(name = "test/cpp/client")
grpc_cc_test(
name = "credentials_test",
srcs = ["credentials_test.cc"],
data = [
"//src/core/tsi/test_creds:ca.pem",
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
external_deps = [
"gtest",
],

@ -32,6 +32,10 @@
#include "src/cpp/client/secure_credentials.h"
#include "src/cpp/common/tls_credentials_options_util.h"
#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
namespace {
constexpr const char* kRootCertName = "root_cert_name";
@ -40,6 +44,7 @@ constexpr const char* kIdentityCertName = "identity_cert_name";
constexpr const char* kIdentityCertPrivateKey = "identity_private_key";
constexpr const char* kIdentityCertContents = "identity_cert_contents";
using ::grpc::experimental::FileWatcherCertificateProvider;
using ::grpc::experimental::StaticDataCertificateProvider;
using ::grpc::experimental::TlsServerAuthorizationCheckArg;
using ::grpc::experimental::TlsServerAuthorizationCheckConfig;
@ -407,6 +412,52 @@ TEST(CredentialsTest,
GPR_ASSERT(channel_credentials.get() != nullptr);
}
TEST(
CredentialsTest,
TlsChannelCredentialsWithFileWatcherCertificateProviderLoadingRootAndIdentity) {
auto certificate_provider = std::make_shared<FileWatcherCertificateProvider>(
SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
grpc::experimental::TlsChannelCredentialsOptions options(
certificate_provider);
options.watch_root_certs();
options.set_root_cert_name(kRootCertName);
options.watch_identity_key_cert_pairs();
options.set_identity_cert_name(kIdentityCertName);
options.set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto test_server_authorization_check =
std::make_shared<TestTlsServerAuthorizationCheck>();
auto server_authorization_check_config =
std::make_shared<TlsServerAuthorizationCheckConfig>(
test_server_authorization_check);
options.set_server_authorization_check_config(
server_authorization_check_config);
auto channel_credentials = grpc::experimental::TlsCredentials(options);
GPR_ASSERT(channel_credentials.get() != nullptr);
}
// ChannelCredentials should always have root credential presented.
// Otherwise the system root certificates will be loaded, which will cause
// failure in some tests under MacOS/Windows.
TEST(CredentialsTest,
TlsChannelCredentialsWithFileWatcherCertificateProviderLoadingRootOnly) {
auto certificate_provider =
std::make_shared<FileWatcherCertificateProvider>(CA_CERT_PATH, 1);
grpc::experimental::TlsChannelCredentialsOptions options(
certificate_provider);
options.watch_root_certs();
options.set_root_cert_name(kRootCertName);
options.set_server_verification_option(GRPC_TLS_SERVER_VERIFICATION);
auto test_server_authorization_check =
std::make_shared<TestTlsServerAuthorizationCheck>();
auto server_authorization_check_config =
std::make_shared<TlsServerAuthorizationCheckConfig>(
test_server_authorization_check);
options.set_server_authorization_check_config(
server_authorization_check_config);
auto channel_credentials = grpc::experimental::TlsCredentials(options);
GPR_ASSERT(channel_credentials.get() != nullptr);
}
TEST(CredentialsTest, TlsServerAuthorizationCheckConfigErrorMessages) {
std::shared_ptr<TlsServerAuthorizationCheckConfig> config(
new TlsServerAuthorizationCheckConfig(nullptr));

@ -63,6 +63,11 @@ grpc_cc_test(
grpc_cc_test(
name = "credentials_test",
srcs = ["credentials_test.cc"],
data = [
"//src/core/tsi/test_creds:ca.pem",
"//src/core/tsi/test_creds:server1.key",
"//src/core/tsi/test_creds:server1.pem",
],
external_deps = [
"gtest",
],

@ -27,6 +27,10 @@
#include "test/core/util/port.h"
#include "test/core/util/test_config.h"
#define CA_CERT_PATH "src/core/tsi/test_creds/ca.pem"
#define SERVER_CERT_PATH "src/core/tsi/test_creds/server1.pem"
#define SERVER_KEY_PATH "src/core/tsi/test_creds/server1.key"
namespace {
constexpr const char* kRootCertName = "root_cert_name";
@ -35,6 +39,7 @@ constexpr const char* kIdentityCertName = "identity_cert_name";
constexpr const char* kIdentityCertPrivateKey = "identity_private_key";
constexpr const char* kIdentityCertContents = "identity_cert_contents";
using ::grpc::experimental::FileWatcherCertificateProvider;
using ::grpc::experimental::StaticDataCertificateProvider;
} // namespace
@ -86,6 +91,38 @@ TEST(CredentialsTest,
GPR_ASSERT(server_credentials.get() != nullptr);
}
TEST(
CredentialsTest,
TlsServerCredentialsWithFileWatcherCertificateProviderLoadingRootAndIdentity) {
auto certificate_provider = std::make_shared<FileWatcherCertificateProvider>(
SERVER_KEY_PATH, SERVER_CERT_PATH, CA_CERT_PATH, 1);
grpc::experimental::TlsServerCredentialsOptions options(certificate_provider);
options.watch_root_certs();
options.set_root_cert_name(kRootCertName);
options.watch_identity_key_cert_pairs();
options.set_identity_cert_name(kIdentityCertName);
options.set_cert_request_type(
GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
auto server_credentials = grpc::experimental::TlsServerCredentials(options);
GPR_ASSERT(server_credentials.get() != nullptr);
}
// ServerCredentials should always have identity credential presented.
// Otherwise gRPC stack will fail.
TEST(
CredentialsTest,
TlsServerCredentialsWithFileWatcherCertificateProviderLoadingIdentityOnly) {
auto certificate_provider = std::make_shared<FileWatcherCertificateProvider>(
SERVER_KEY_PATH, SERVER_CERT_PATH, 1);
grpc::experimental::TlsServerCredentialsOptions options(certificate_provider);
options.watch_identity_key_cert_pairs();
options.set_identity_cert_name(kIdentityCertName);
options.set_cert_request_type(
GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
auto server_credentials = grpc::experimental::TlsServerCredentials(options);
GPR_ASSERT(server_credentials.get() != nullptr);
}
} // namespace
} // namespace testing
} // namespace grpc

@ -4643,6 +4643,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": "grpc_tls_certificate_provider_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save