// Copyright 2017 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. // // TODO(roth): Split this file up into a common test framework and a set // of test files that use that framework. Need to figure out the best // way to split up the tests. One option would be to split it up by xDS // resource type; another approach would be to have all of the "core" // xDS functionality in one file and then move specific features to // their own files (e.g., mTLS security, fault injection, circuit // breaking, etc). #include #include #include #include #include #include #include #include #include #include #include #include "absl/functional/bind_front.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" #include "absl/types/optional.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "src/core/ext/filters/client_channel/backup_poller.h" #include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_args.h" #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" #include "src/core/ext/filters/http/client/http_client_filter.h" #include "src/core/ext/xds/certificate_provider_registry.h" #include "src/core/ext/xds/xds_api.h" #include "src/core/ext/xds/xds_channel_args.h" #include "src/core/ext/xds/xds_client.h" #include "src/core/ext/xds/xds_listener.h" #include "src/core/lib/address_utils/parse_address.h" #include "src/core/lib/address_utils/sockaddr_utils.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/gpr/time_precise.h" #include "src/core/lib/gpr/tmpfile.h" #include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/gprpp/sync.h" #include "src/core/lib/gprpp/time.h" #include "src/core/lib/gprpp/time_util.h" #include "src/core/lib/iomgr/load_file.h" #include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/resolver/server_address.h" #include "src/core/lib/security/credentials/fake/fake_credentials.h" #include "src/cpp/client/secure_credentials.h" #include "src/cpp/server/secure_server_credentials.h" #include "src/proto/grpc/testing/echo.grpc.pb.h" #include "src/proto/grpc/testing/xds/ads_for_test.grpc.pb.h" #include "src/proto/grpc/testing/xds/cds_for_test.grpc.pb.h" #include "src/proto/grpc/testing/xds/eds_for_test.grpc.pb.h" #include "src/proto/grpc/testing/xds/lds_rds_for_test.grpc.pb.h" #include "src/proto/grpc/testing/xds/lrs_for_test.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/ads.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/aggregate_cluster.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/cluster.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/discovery.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/endpoint.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/fault.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/http_connection_manager.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/http_filter_rbac.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/listener.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/lrs.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/route.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/router.grpc.pb.h" #include "src/proto/grpc/testing/xds/v3/tls.grpc.pb.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" #include "test/cpp/end2end/xds/no_op_http_filter.h" #include "test/cpp/end2end/xds/xds_end2end_test_lib.h" #include "test/cpp/util/test_config.h" #include "test/cpp/util/tls_test_utils.h" namespace grpc { namespace testing { namespace { using ::envoy::config::listener::v3::FilterChainMatch; using ::envoy::config::rbac::v3::Policy; using ::envoy::config::rbac::v3::RBAC_Action; using ::envoy::config::rbac::v3::RBAC_Action_ALLOW; using ::envoy::config::rbac::v3::RBAC_Action_DENY; using ::envoy::config::rbac::v3::RBAC_Action_LOG; using ::envoy::extensions::filters::http::rbac::v3::RBAC; using ::envoy::extensions::filters::http::rbac::v3::RBACPerRoute; using ::envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext; using ::envoy::extensions::transport_sockets::tls::v3::UpstreamTlsContext; using ::envoy::type::matcher::v3::StringMatcher; using ::grpc::experimental::ExternalCertificateVerifier; using ::grpc::experimental::IdentityKeyCertPair; using ::grpc::experimental::StaticDataCertificateProvider; constexpr char kClientCertPath[] = "src/core/tsi/test_creds/client.pem"; constexpr char kClientKeyPath[] = "src/core/tsi/test_creds/client.key"; constexpr char kBadClientCertPath[] = "src/core/tsi/test_creds/badclient.pem"; constexpr char kBadClientKeyPath[] = "src/core/tsi/test_creds/badclient.key"; // Based on StaticDataCertificateProvider, but provides alternate certificates // if the certificate name is not empty. class FakeCertificateProvider final : public grpc_tls_certificate_provider { public: struct CertData { std::string root_certificate; grpc_core::PemKeyCertPairList identity_key_cert_pairs; }; using CertDataMap = std::map; class CertDataMapWrapper { public: CertDataMap Get() { grpc_core::MutexLock lock(&mu_); return cert_data_map_; } void Set(CertDataMap data) { grpc_core::MutexLock lock(&mu_); cert_data_map_ = std::move(data); } private: grpc_core::Mutex mu_; CertDataMap cert_data_map_ ABSL_GUARDED_BY(mu_); }; explicit FakeCertificateProvider(CertDataMap cert_data_map) : distributor_( grpc_core::MakeRefCounted()), cert_data_map_(std::move(cert_data_map)) { distributor_->SetWatchStatusCallback([this](std::string cert_name, bool root_being_watched, bool identity_being_watched) { if (!root_being_watched && !identity_being_watched) return; auto it = cert_data_map_.find(cert_name); if (it == cert_data_map_.end()) { grpc_error_handle error = GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( "No certificates available for cert_name \"", cert_name, "\"")); distributor_->SetErrorForCert(cert_name, GRPC_ERROR_REF(error), GRPC_ERROR_REF(error)); GRPC_ERROR_UNREF(error); } else { absl::optional root_certificate; absl::optional pem_key_cert_pairs; if (root_being_watched) { root_certificate = it->second.root_certificate; } if (identity_being_watched) { pem_key_cert_pairs = it->second.identity_key_cert_pairs; } distributor_->SetKeyMaterials(cert_name, std::move(root_certificate), std::move(pem_key_cert_pairs)); } }); } ~FakeCertificateProvider() override { distributor_->SetWatchStatusCallback(nullptr); } grpc_core::RefCountedPtr distributor() const override { return distributor_; } grpc_core::UniqueTypeName type() const override { static grpc_core::UniqueTypeName::Factory kFactory("fake"); return kFactory.Create(); } private: int CompareImpl(const grpc_tls_certificate_provider* other) const override { // TODO(yashykt): Maybe do something better here. return grpc_core::QsortCompare( static_cast(this), other); } grpc_core::RefCountedPtr distributor_; CertDataMap cert_data_map_; }; class FakeCertificateProviderFactory : public grpc_core::CertificateProviderFactory { public: class Config : public grpc_core::CertificateProviderFactory::Config { public: explicit Config(const char* name) : name_(name) {} const char* name() const override { return name_; } std::string ToString() const override { return "{}"; } private: const char* name_; }; FakeCertificateProviderFactory( const char* name, FakeCertificateProvider::CertDataMapWrapper* cert_data_map) : name_(name), cert_data_map_(cert_data_map) { GPR_ASSERT(cert_data_map != nullptr); } const char* name() const override { return name_; } grpc_core::RefCountedPtr CreateCertificateProviderConfig(const grpc_core::Json& /*config_json*/, grpc_error_handle* /*error*/) override { return grpc_core::MakeRefCounted(name_); } grpc_core::RefCountedPtr CreateCertificateProvider( grpc_core::RefCountedPtr /*config*/) override { GPR_ASSERT(cert_data_map_ != nullptr); return grpc_core::MakeRefCounted( cert_data_map_->Get()); } private: const char* name_; FakeCertificateProvider::CertDataMapWrapper* cert_data_map_; }; // Global variables for each provider. FakeCertificateProvider::CertDataMapWrapper* g_fake1_cert_data_map = nullptr; FakeCertificateProvider::CertDataMapWrapper* g_fake2_cert_data_map = nullptr; class XdsSecurityTest : public XdsEnd2endTest { protected: void SetUp() override { BootstrapBuilder builder = BootstrapBuilder(); builder.AddCertificateProviderPlugin("fake_plugin1", "fake1"); builder.AddCertificateProviderPlugin("fake_plugin2", "fake2"); std::vector fields; fields.push_back(absl::StrFormat(" \"certificate_file\": \"%s\"", kClientCertPath)); fields.push_back(absl::StrFormat(" \"private_key_file\": \"%s\"", kClientKeyPath)); fields.push_back(absl::StrFormat(" \"ca_certificate_file\": \"%s\"", kCaCertPath)); builder.AddCertificateProviderPlugin("file_plugin", "file_watcher", absl::StrJoin(fields, ",\n")); InitClient(builder); CreateAndStartBackends(1); root_cert_ = ReadFile(kCaCertPath); bad_root_cert_ = ReadFile(kBadClientCertPath); identity_pair_ = ReadTlsIdentityPair(kClientKeyPath, kClientCertPath); // TODO(yashykt): Use different client certs here instead of reusing // server certs after https://github.com/grpc/grpc/pull/24876 is merged fallback_identity_pair_ = ReadTlsIdentityPair(kServerKeyPath, kServerCertPath); bad_identity_pair_ = ReadTlsIdentityPair(kBadClientKeyPath, kBadClientCertPath); server_san_exact_.set_exact("*.test.google.fr"); server_san_prefix_.set_prefix("waterzooi.test.google"); server_san_suffix_.set_suffix("google.fr"); server_san_contains_.set_contains("google"); server_san_regex_.mutable_safe_regex()->mutable_google_re2(); server_san_regex_.mutable_safe_regex()->set_regex( "(foo|waterzooi).test.google.(fr|be)"); bad_san_1_.set_exact("192.168.1.4"); bad_san_2_.set_exact("foo.test.google.in"); authenticated_identity_ = {"testclient"}; fallback_authenticated_identity_ = {"*.test.google.fr", "waterzooi.test.google.be", "*.test.youtube.com", "192.168.1.3"}; EdsResourceArgs args({ {"locality0", CreateEndpointsForBackends(0, 1)}, }); balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); } // Sends CDS updates with the new security configuration and verifies that // after propagation, this new configuration is used for connections. If \a // identity_instance_name and \a root_instance_name are both empty, // connections are expected to use fallback credentials. void UpdateAndVerifyXdsSecurityConfiguration( absl::string_view root_instance_name, absl::string_view root_certificate_name, absl::string_view identity_instance_name, absl::string_view identity_certificate_name, const std::vector& san_matchers, const std::vector& expected_authenticated_identity, bool test_expects_failure = false) { auto cluster = default_cluster_; if (!identity_instance_name.empty() || !root_instance_name.empty()) { auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; if (!identity_instance_name.empty()) { upstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name(std::string(identity_instance_name)); upstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_certificate_name(std::string(identity_certificate_name)); } if (!root_instance_name.empty()) { upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name(std::string(root_instance_name)); upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_certificate_name(std::string(root_certificate_name)); } if (!san_matchers.empty()) { auto* validation_context = upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context(); for (const auto& san_matcher : san_matchers) { *validation_context->add_match_subject_alt_names() = san_matcher; } } transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); } balancer_->ads_service()->SetCdsResource(cluster); // The updates might take time to have an effect, so use a retry loop. constexpr int kRetryCount = 100; int num_tries = 0; for (; num_tries < kRetryCount; num_tries++) { // Restart the servers to force a reconnection so that previously // connected subchannels are not used for the RPC. ShutdownBackend(0); StartBackend(0); if (test_expects_failure) { if (SendRpc().ok()) { gpr_log(GPR_ERROR, "RPC succeeded. Failure expected. Trying again."); continue; } } else { WaitForBackend(DEBUG_LOCATION, 0, [](const RpcResult& result) { if (!result.status.ok()) { EXPECT_EQ(result.status.error_code(), StatusCode::UNAVAILABLE); EXPECT_EQ(result.status.error_message(), // TODO(roth): Improve this message as part of // https://github.com/grpc/grpc/issues/22883. "weighted_target: all children report state " "TRANSIENT_FAILURE"); } }); Status status = SendRpc(); if (!status.ok()) { gpr_log(GPR_ERROR, "RPC failed. code=%d message=%s Trying again.", status.error_code(), status.error_message().c_str()); continue; } if (backends_[0]->backend_service()->last_peer_identity() != expected_authenticated_identity) { gpr_log( GPR_ERROR, "Expected client identity does not match. (actual) %s vs " "(expected) %s Trying again.", absl::StrJoin( backends_[0]->backend_service()->last_peer_identity(), ",") .c_str(), absl::StrJoin(expected_authenticated_identity, ",").c_str()); continue; } } break; } EXPECT_LT(num_tries, kRetryCount); } std::string root_cert_; std::string bad_root_cert_; grpc_core::PemKeyCertPairList identity_pair_; grpc_core::PemKeyCertPairList fallback_identity_pair_; grpc_core::PemKeyCertPairList bad_identity_pair_; StringMatcher server_san_exact_; StringMatcher server_san_prefix_; StringMatcher server_san_suffix_; StringMatcher server_san_contains_; StringMatcher server_san_regex_; StringMatcher bad_san_1_; StringMatcher bad_san_2_; std::vector authenticated_identity_; std::vector fallback_authenticated_identity_; }; TEST_P(XdsSecurityTest, UnknownTransportSocket) { auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("unknown_transport_socket"); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "Unrecognized transport socket: unknown_transport_socket")); } TEST_P(XdsSecurityTest, TLSConfigurationWithoutValidationContextCertificateProviderInstance) { auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("TLS configuration provided but no " "ca_certificate_provider_instance found.")); } TEST_P( XdsSecurityTest, MatchSubjectAltNamesProvidedWithoutValidationContextCertificateProviderInstance) { auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; auto* validation_context = upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context(); *validation_context->add_match_subject_alt_names() = server_san_exact_; transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("TLS configuration provided but no " "ca_certificate_provider_instance found.")); } TEST_P( XdsSecurityTest, TlsCertificateProviderInstanceWithoutValidationContextCertificateProviderInstance) { auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name(std::string("fake_plugin1")); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("TLS configuration provided but no " "ca_certificate_provider_instance found.")); } TEST_P(XdsSecurityTest, RegexSanMatcherDoesNotAllowIgnoreCase) { auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name(std::string("fake_plugin1")); auto* validation_context = upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context(); StringMatcher matcher; matcher.mutable_safe_regex()->mutable_google_re2(); matcher.mutable_safe_regex()->set_regex( "(foo|waterzooi).test.google.(fr|be)"); matcher.set_ignore_case(true); *validation_context->add_match_subject_alt_names() = matcher; transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "StringMatcher: ignore_case has no effect for SAFE_REGEX.")); } TEST_P(XdsSecurityTest, UnknownRootCertificateProvider) { auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("unknown"); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "Unrecognized certificate provider instance name: unknown")); } TEST_P(XdsSecurityTest, UnknownIdentityCertificateProvider) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name("unknown"); upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "Unrecognized certificate provider instance name: unknown")); } TEST_P(XdsSecurityTest, NacksCertificateValidationContextWithVerifyCertificateSpki) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->add_verify_certificate_spki("spki"); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "CertificateValidationContext: verify_certificate_spki unsupported")); } TEST_P(XdsSecurityTest, NacksCertificateValidationContextWithVerifyCertificateHash) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->add_verify_certificate_hash("hash"); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "CertificateValidationContext: verify_certificate_hash unsupported")); } TEST_P(XdsSecurityTest, NacksCertificateValidationContextWithRequireSignedCertificateTimes) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_require_signed_certificate_timestamp() ->set_value(true); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("CertificateValidationContext: " "require_signed_certificate_timestamp unsupported")); } TEST_P(XdsSecurityTest, NacksCertificateValidationContextWithCrl) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_crl(); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("CertificateValidationContext: crl unsupported")); } TEST_P(XdsSecurityTest, NacksCertificateValidationContextWithCustomValidatorConfig) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_custom_validator_config(); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "CertificateValidationContext: custom_validator_config unsupported")); } TEST_P(XdsSecurityTest, NacksValidationContextSdsSecretConfig) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context_sds_secret_config(); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("validation_context_sds_secret_config unsupported")); } TEST_P(XdsSecurityTest, NacksTlsParams) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context()->mutable_tls_params(); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("tls_params unsupported")); } TEST_P(XdsSecurityTest, NacksCustomHandshaker) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context() ->mutable_custom_handshaker(); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("custom_handshaker unsupported")); } TEST_P(XdsSecurityTest, NacksTlsCertificates) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context()->add_tls_certificates(); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("tls_certificates unsupported")); } TEST_P(XdsSecurityTest, NacksTlsCertificateSdsSecretConfigs) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); upstream_tls_context.mutable_common_tls_context() ->add_tls_certificate_sds_secret_configs(); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); const auto response_state = WaitForCdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("tls_certificate_sds_secret_configs unsupported")); } TEST_P(XdsSecurityTest, TestTlsConfigurationInCombinedValidationContext) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_combined_validation_context() ->mutable_default_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); WaitForBackend(DEBUG_LOCATION, 0, [](const RpcResult& result) { if (!result.status.ok()) { EXPECT_EQ(result.status.error_code(), StatusCode::UNAVAILABLE); EXPECT_EQ(result.status.error_message(), // TODO(roth): Improve this message as part of // https://github.com/grpc/grpc/issues/22883. "weighted_target: all children report state TRANSIENT_FAILURE"); } }); } // TODO(yashykt): Remove this test once we stop supporting old fields TEST_P(XdsSecurityTest, TestTlsConfigurationInValidationContextCertificateProviderInstance) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); auto cluster = default_cluster_; auto* transport_socket = cluster.mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); UpstreamTlsContext upstream_tls_context; upstream_tls_context.mutable_common_tls_context() ->mutable_combined_validation_context() ->mutable_validation_context_certificate_provider_instance() ->set_instance_name("fake_plugin1"); transport_socket->mutable_typed_config()->PackFrom(upstream_tls_context); balancer_->ads_service()->SetCdsResource(cluster); WaitForBackend(DEBUG_LOCATION, 0, [](const RpcResult& result) { if (!result.status.ok()) { EXPECT_EQ(result.status.error_code(), StatusCode::UNAVAILABLE); EXPECT_EQ(result.status.error_message(), // TODO(roth): Improve this message as part of // https://github.com/grpc/grpc/issues/22883. "weighted_target: all children report state TRANSIENT_FAILURE"); } }); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithNoSanMatchers) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithExactSanMatcher) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithPrefixSanMatcher) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_prefix_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithSuffixSanMatcher) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_suffix_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithContainsSanMatcher) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_contains_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithRegexSanMatcher) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_regex_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithSanMatchersUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin1", "", "fake_plugin1", "", {server_san_exact_, server_san_prefix_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {bad_san_1_, bad_san_2_}, {}, true /* failure */); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin1", "", "fake_plugin1", "", {server_san_prefix_, server_san_regex_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithRootPluginUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"", {bad_root_cert_, bad_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin2" /* bad root */, "", "fake_plugin1", "", {}, {}, true /* failure */); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithIdentityPluginUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"", {root_cert_, fallback_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin2", "", {server_san_exact_}, fallback_authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithBothPluginsUpdated) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"", {bad_root_cert_, bad_identity_pair_}}, {"good", {root_cert_, fallback_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin2", "", "fake_plugin2", "", {}, {}, true /* failure */); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_prefix_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin2", "good", "fake_plugin2", "good", {server_san_prefix_}, fallback_authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithRootCertificateNameUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"bad", {bad_root_cert_, bad_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_regex_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "bad", "fake_plugin1", "", {server_san_regex_}, {}, true /* failure */); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithIdentityCertificateNameUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"bad", {bad_root_cert_, bad_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "bad", {server_san_exact_}, {}, true /* failure */); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithIdentityCertificateNameUpdateGoodCerts) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"good", {root_cert_, fallback_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "good", {server_san_exact_}, fallback_authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsConfigurationWithBothCertificateNamesUpdated) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"bad", {bad_root_cert_, bad_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "bad", "fake_plugin1", "bad", {server_san_prefix_}, {}, true /* failure */); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_prefix_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestTlsConfigurationWithNoSanMatchers) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", {}, {} /* unauthenticated */); } TEST_P(XdsSecurityTest, TestTlsConfigurationWithSanMatchers) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin1", "", "", "", {server_san_exact_, server_san_prefix_, server_san_regex_}, {} /* unauthenticated */); } TEST_P(XdsSecurityTest, TestTlsConfigurationWithSanMatchersUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin1", "", "", "", {server_san_exact_, server_san_prefix_}, {} /* unauthenticated */); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin1", "", "", "", {bad_san_1_, bad_san_2_}, {} /* unauthenticated */, true /* failure */); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin1", "", "", "", {server_san_prefix_, server_san_regex_}, {} /* unauthenticated */); } TEST_P(XdsSecurityTest, TestTlsConfigurationWithRootCertificateNameUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"bad", {bad_root_cert_, bad_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", {server_san_exact_}, {} /* unauthenticated */); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "bad", "", "", {server_san_exact_}, {}, true /* failure */); } TEST_P(XdsSecurityTest, TestTlsConfigurationWithRootPluginUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"", {bad_root_cert_, bad_identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", {server_san_exact_}, {} /* unauthenticated */); UpdateAndVerifyXdsSecurityConfiguration( "fake_plugin2", "", "", "", {server_san_exact_}, {}, true /* failure */); } TEST_P(XdsSecurityTest, TestFallbackConfiguration) { UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", {}, fallback_authenticated_identity_); } TEST_P(XdsSecurityTest, TestMtlsToTls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", {server_san_exact_}, {} /* unauthenticated */); } TEST_P(XdsSecurityTest, TestMtlsToFallback) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", {}, fallback_authenticated_identity_); } TEST_P(XdsSecurityTest, TestTlsToMtls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", {server_san_exact_}, {} /* unauthenticated */); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestTlsToFallback) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", {server_san_exact_}, {} /* unauthenticated */); UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", {}, fallback_authenticated_identity_); } TEST_P(XdsSecurityTest, TestFallbackToMtls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", {}, fallback_authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "fake_plugin1", "", {server_san_exact_}, authenticated_identity_); } TEST_P(XdsSecurityTest, TestFallbackToTls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); UpdateAndVerifyXdsSecurityConfiguration("", "", "", "", {}, fallback_authenticated_identity_); UpdateAndVerifyXdsSecurityConfiguration("fake_plugin1", "", "", "", {server_san_exact_}, {} /* unauthenticated */); } TEST_P(XdsSecurityTest, TestFileWatcherCertificateProvider) { UpdateAndVerifyXdsSecurityConfiguration("file_plugin", "", "file_plugin", "", {server_san_exact_}, authenticated_identity_); } class XdsEnabledServerTest : public XdsEnd2endTest { protected: void SetUp() override {} // No-op -- individual tests do this themselves. void DoSetUp(BootstrapBuilder builder = BootstrapBuilder()) { InitClient(builder); CreateBackends(1, /*xds_enabled=*/true); EdsResourceArgs args({{"locality0", CreateEndpointsForBackends(0, 1)}}); balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); } }; TEST_P(XdsEnabledServerTest, Basic) { DoSetUp(); backends_[0]->Start(); WaitForBackend(DEBUG_LOCATION, 0); } TEST_P(XdsEnabledServerTest, ListenerDeletionIgnored) { DoSetUp(BootstrapBuilder().SetIgnoreResourceDeletion()); backends_[0]->Start(); WaitForBackend(DEBUG_LOCATION, 0); // Check that we ACKed. // TODO(roth): There may be multiple entries in the resource state response // queue, because the client doesn't necessarily subscribe to all resources // in a single message, and the server currently (I suspect incorrectly?) // thinks that each subscription message is an ACK. So for now, we // drain the entire LDS resource state response queue, ensuring that // all responses are ACKs. Need to look more closely at the protocol // semantics here and make sure the server is doing the right thing, // in which case we may be able to avoid this. while (true) { auto response_state = balancer_->ads_service()->lds_response_state(); if (!response_state.has_value()) break; ASSERT_TRUE(response_state.has_value()); EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); } // Now unset the resource. balancer_->ads_service()->UnsetResource( kLdsTypeUrl, GetServerListenerName(backends_[0]->port())); // Wait for update to be ACKed. absl::Time deadline = absl::Now() + (absl::Seconds(10) * grpc_test_slowdown_factor()); while (true) { auto response_state = balancer_->ads_service()->lds_response_state(); if (!response_state.has_value()) { gpr_sleep_until(grpc_timeout_seconds_to_deadline(1)); continue; } EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); ASSERT_LT(absl::Now(), deadline); break; } // Make sure server is still serving. CheckRpcSendOk(DEBUG_LOCATION); } TEST_P(XdsEnabledServerTest, BadLdsUpdateNoApiListenerNorAddress) { DoSetUp(); Listener listener = default_server_listener_; listener.clear_address(); listener.set_name( absl::StrCat("grpc/server?xds.resource.listening_address=", ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); balancer_->ads_service()->SetLdsResource(listener); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Listener has neither address nor ApiListener")); } // TODO(roth): Re-enable the following test once // github.com/istio/istio/issues/38914 is resolved. TEST_P(XdsEnabledServerTest, DISABLED_BadLdsUpdateBothApiListenerAndAddress) { DoSetUp(); Listener listener = default_server_listener_; listener.mutable_api_listener(); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Listener has both address and ApiListener")); } TEST_P(XdsEnabledServerTest, NacksNonZeroXffNumTrusterHops) { DoSetUp(); Listener listener = default_server_listener_; HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); http_connection_manager.set_xff_num_trusted_hops(1); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("'xff_num_trusted_hops' must be zero")); } TEST_P(XdsEnabledServerTest, NacksNonEmptyOriginalIpDetectionExtensions) { DoSetUp(); Listener listener = default_server_listener_; HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); http_connection_manager.add_original_ip_detection_extensions(); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("'original_ip_detection_extensions' must be empty")); } TEST_P(XdsEnabledServerTest, UnsupportedL4Filter) { DoSetUp(); Listener listener = default_server_listener_; listener.mutable_default_filter_chain()->clear_filters(); listener.mutable_default_filter_chain()->add_filters()->mutable_typed_config()->PackFrom(default_listener_ /* any proto object other than HttpConnectionManager */); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("Unsupported filter type")); } TEST_P(XdsEnabledServerTest, NacksEmptyHttpFilterList) { DoSetUp(); Listener listener = default_server_listener_; HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); http_connection_manager.clear_http_filters(); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("Expected at least one HTTP filter")); } TEST_P(XdsEnabledServerTest, UnsupportedHttpFilter) { DoSetUp(); Listener listener = default_server_listener_; HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); http_connection_manager.clear_http_filters(); auto* http_filter = http_connection_manager.add_http_filters(); http_filter->set_name("grpc.testing.unsupported_http_filter"); http_filter->mutable_typed_config()->set_type_url( "custom/grpc.testing.unsupported_http_filter"); http_filter = http_connection_manager.add_http_filters(); http_filter->set_name("router"); http_filter->mutable_typed_config()->PackFrom( envoy::extensions::filters::http::router::v3::Router()); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("no filter registered for config type " "grpc.testing.unsupported_http_filter")); } TEST_P(XdsEnabledServerTest, HttpFilterNotSupportedOnServer) { DoSetUp(); Listener listener = default_server_listener_; HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); http_connection_manager.clear_http_filters(); auto* http_filter = http_connection_manager.add_http_filters(); http_filter->set_name("grpc.testing.client_only_http_filter"); http_filter->mutable_typed_config()->set_type_url( "custom/grpc.testing.client_only_http_filter"); http_filter = http_connection_manager.add_http_filters(); http_filter->set_name("router"); http_filter->mutable_typed_config()->PackFrom( envoy::extensions::filters::http::router::v3::Router()); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Filter grpc.testing.client_only_http_filter is not " "supported on servers")); } TEST_P(XdsEnabledServerTest, HttpFilterNotSupportedOnServerIgnoredWhenOptional) { DoSetUp(); Listener listener = default_server_listener_; HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); http_connection_manager.clear_http_filters(); auto* http_filter = http_connection_manager.add_http_filters(); http_filter->set_name("grpc.testing.client_only_http_filter"); http_filter->mutable_typed_config()->set_type_url( "custom/grpc.testing.client_only_http_filter"); http_filter->set_is_optional(true); http_filter = http_connection_manager.add_http_filters(); http_filter->set_name("router"); http_filter->mutable_typed_config()->PackFrom( envoy::extensions::filters::http::router::v3::Router()); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); WaitForBackend(DEBUG_LOCATION, 0); auto response_state = balancer_->ads_service()->lds_response_state(); ASSERT_TRUE(response_state.has_value()); EXPECT_EQ(response_state->state, AdsServiceImpl::ResponseState::ACKED); } // Verify that a mismatch of listening address results in "not serving" // status. TEST_P(XdsEnabledServerTest, ListenerAddressMismatch) { DoSetUp(); Listener listener = default_server_listener_; // Set a different listening address in the LDS update listener.mutable_address()->mutable_socket_address()->set_address( "192.168.1.1"); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::FAILED_PRECONDITION); } TEST_P(XdsEnabledServerTest, UseOriginalDstNotSupported) { DoSetUp(); Listener listener = default_server_listener_; listener.mutable_use_original_dst()->set_value(true); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Field \'use_original_dst\' is not supported.")); } class XdsServerSecurityTest : public XdsEnd2endTest { protected: void SetUp() override { BootstrapBuilder builder = BootstrapBuilder(); builder.AddCertificateProviderPlugin("fake_plugin1", "fake1"); builder.AddCertificateProviderPlugin("fake_plugin2", "fake2"); std::vector fields; fields.push_back(absl::StrFormat(" \"certificate_file\": \"%s\"", kClientCertPath)); fields.push_back(absl::StrFormat(" \"private_key_file\": \"%s\"", kClientKeyPath)); fields.push_back(absl::StrFormat(" \"ca_certificate_file\": \"%s\"", kCaCertPath)); builder.AddCertificateProviderPlugin("file_plugin", "file_watcher", absl::StrJoin(fields, ",\n")); InitClient(builder); CreateBackends(1, /*xds_enabled=*/true); root_cert_ = ReadFile(kCaCertPath); bad_root_cert_ = ReadFile(kBadClientCertPath); identity_pair_ = ReadTlsIdentityPair(kServerKeyPath, kServerCertPath); bad_identity_pair_ = ReadTlsIdentityPair(kBadClientKeyPath, kBadClientCertPath); identity_pair_2_ = ReadTlsIdentityPair(kClientKeyPath, kClientCertPath); server_authenticated_identity_ = {"*.test.google.fr", "waterzooi.test.google.be", "*.test.youtube.com", "192.168.1.3"}; server_authenticated_identity_2_ = {"testclient"}; client_authenticated_identity_ = {"*.test.google.fr", "waterzooi.test.google.be", "*.test.youtube.com", "192.168.1.3"}; EdsResourceArgs args({ {"locality0", CreateEndpointsForBackends(0, 1)}, }); balancer_->ads_service()->SetEdsResource(BuildEdsResource(args)); } void SetLdsUpdate(absl::string_view root_instance_name, absl::string_view root_certificate_name, absl::string_view identity_instance_name, absl::string_view identity_certificate_name, bool require_client_certificates) { Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); if (!identity_instance_name.empty()) { auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name(std::string(identity_instance_name)); downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_certificate_name(std::string(identity_certificate_name)); if (!root_instance_name.empty()) { downstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name(std::string(root_instance_name)); downstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_certificate_name(std::string(root_certificate_name)); downstream_tls_context.mutable_require_client_certificate()->set_value( require_client_certificates); } transport_socket->mutable_typed_config()->PackFrom( downstream_tls_context); } SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); } std::shared_ptr CreateMtlsChannel() { ChannelArguments args; // Override target name for host name check args.SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, ipv6_only_ ? "::1" : "127.0.0.1"); args.SetInt(GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL, 1); std::string uri = absl::StrCat( ipv6_only_ ? "ipv6:[::1]:" : "ipv4:127.0.0.1:", backends_[0]->port()); IdentityKeyCertPair key_cert_pair; key_cert_pair.private_key = ReadFile(kServerKeyPath); key_cert_pair.certificate_chain = ReadFile(kServerCertPath); std::vector identity_key_cert_pairs; identity_key_cert_pairs.emplace_back(key_cert_pair); auto certificate_provider = std::make_shared( ReadFile(kCaCertPath), identity_key_cert_pairs); grpc::experimental::TlsChannelCredentialsOptions options; options.set_certificate_provider(std::move(certificate_provider)); options.watch_root_certs(); options.watch_identity_key_cert_pairs(); auto verifier = ExternalCertificateVerifier::Create(true); options.set_verify_server_certs(true); options.set_certificate_verifier(std::move(verifier)); auto channel_creds = grpc::experimental::TlsCredentials(options); GPR_ASSERT(channel_creds.get() != nullptr); return CreateCustomChannel(uri, channel_creds, args); } std::shared_ptr CreateTlsChannel() { ChannelArguments args; // Override target name for host name check args.SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, ipv6_only_ ? "::1" : "127.0.0.1"); args.SetInt(GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL, 1); std::string uri = absl::StrCat( ipv6_only_ ? "ipv6:[::1]:" : "ipv4:127.0.0.1:", backends_[0]->port()); auto certificate_provider = std::make_shared(ReadFile(kCaCertPath)); grpc::experimental::TlsChannelCredentialsOptions options; options.set_certificate_provider(std::move(certificate_provider)); options.watch_root_certs(); auto verifier = ExternalCertificateVerifier::Create(true); options.set_verify_server_certs(true); options.set_certificate_verifier(std::move(verifier)); auto channel_creds = grpc::experimental::TlsCredentials(options); GPR_ASSERT(channel_creds.get() != nullptr); return CreateCustomChannel(uri, channel_creds, args); } std::shared_ptr CreateInsecureChannel( bool use_put_requests = false) { ChannelArguments args; // Override target name for host name check args.SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, ipv6_only_ ? "::1" : "127.0.0.1"); args.SetInt(GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL, 1); if (use_put_requests) { args.SetInt(GRPC_ARG_TEST_ONLY_USE_PUT_REQUESTS, 1); } std::string uri = absl::StrCat( ipv6_only_ ? "ipv6:[::1]:" : "ipv4:127.0.0.1:", backends_[0]->port()); return CreateCustomChannel(uri, InsecureChannelCredentials(), args); } void SendRpc( std::function()> channel_creator, std::vector expected_server_identity, std::vector expected_client_identity, bool test_expects_failure = false, absl::optional expected_status = absl::nullopt) { gpr_log(GPR_INFO, "Sending RPC"); int num_tries = 0; constexpr int kRetryCount = 100; auto overall_deadline = absl::Now() + absl::Seconds(5); for (; num_tries < kRetryCount || absl::Now() < overall_deadline; num_tries++) { auto channel = channel_creator(); auto stub = grpc::testing::EchoTestService::NewStub(channel); ClientContext context; context.set_wait_for_ready(true); context.set_deadline(grpc_timeout_milliseconds_to_deadline(2000)); EchoRequest request; // TODO(yashykt): Skipping the cancelled check on the server since the // server's graceful shutdown isn't as per spec and the check isn't // necessary for what we want to test here anyway. // https://github.com/grpc/grpc/issues/24237 request.mutable_param()->set_skip_cancelled_check(true); request.set_message(kRequestMessage); EchoResponse response; Status status = stub->Echo(&context, request, &response); if (test_expects_failure) { if (status.ok()) { gpr_log(GPR_ERROR, "RPC succeeded. Failure expected. Trying again."); continue; } if (expected_status.has_value() && *expected_status != status.error_code()) { gpr_log(GPR_ERROR, "Expected status does not match Actual(%d) vs Expected(%d)", status.error_code(), *expected_status); continue; } } else { if (!status.ok()) { gpr_log(GPR_ERROR, "RPC failed. code=%d message=%s Trying again.", status.error_code(), status.error_message().c_str()); continue; } EXPECT_EQ(response.message(), kRequestMessage); std::vector peer_identity; for (const auto& entry : context.auth_context()->GetPeerIdentity()) { peer_identity.emplace_back( std::string(entry.data(), entry.size()).c_str()); } if (peer_identity != expected_server_identity) { gpr_log(GPR_ERROR, "Expected server identity does not match. (actual) %s vs " "(expected) %s Trying again.", absl::StrJoin(peer_identity, ",").c_str(), absl::StrJoin(expected_server_identity, ",").c_str()); continue; } if (backends_[0]->backend_service()->last_peer_identity() != expected_client_identity) { gpr_log( GPR_ERROR, "Expected client identity does not match. (actual) %s vs " "(expected) %s Trying again.", absl::StrJoin( backends_[0]->backend_service()->last_peer_identity(), ",") .c_str(), absl::StrJoin(expected_client_identity, ",").c_str()); continue; } } break; } EXPECT_LT(num_tries, kRetryCount); } std::string root_cert_; std::string bad_root_cert_; grpc_core::PemKeyCertPairList identity_pair_; grpc_core::PemKeyCertPairList bad_identity_pair_; grpc_core::PemKeyCertPairList identity_pair_2_; std::vector server_authenticated_identity_; std::vector server_authenticated_identity_2_; std::vector client_authenticated_identity_; }; TEST_P(XdsServerSecurityTest, UnknownTransportSocket) { Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("unknown_transport_socket"); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "Unrecognized transport socket: unknown_transport_socket")); } TEST_P(XdsServerSecurityTest, NacksRequireSNI) { Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name("fake_plugin1"); downstream_tls_context.mutable_require_sni()->set_value(true); transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("require_sni: unsupported")); } TEST_P(XdsServerSecurityTest, NacksOcspStaplePolicyOtherThanLenientStapling) { Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name("fake_plugin1"); downstream_tls_context.set_ocsp_staple_policy( envoy::extensions::transport_sockets::tls::v3:: DownstreamTlsContext_OcspStaplePolicy_STRICT_STAPLING); transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "ocsp_staple_policy: Only LENIENT_STAPLING supported")); } TEST_P( XdsServerSecurityTest, NacksRequiringClientCertificateWithoutValidationCertificateProviderInstance) { Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name("fake_plugin1"); downstream_tls_context.mutable_require_client_certificate()->set_value(true); transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "TLS configuration requires client certificates but no " "certificate provider instance specified for validation.")); } TEST_P(XdsServerSecurityTest, NacksTlsConfigurationWithoutIdentityProviderInstance) { Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("TLS configuration provided but no " "tls_certificate_provider_instance found.")); } TEST_P(XdsServerSecurityTest, NacksMatchSubjectAltNames) { Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name("fake_plugin1"); downstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->add_match_subject_alt_names() ->set_exact("*.test.google.fr"); transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("match_subject_alt_names not supported on servers")); } TEST_P(XdsServerSecurityTest, UnknownIdentityCertificateProvider) { SetLdsUpdate("", "", "unknown", "", false); SendRpc([this]() { return CreateTlsChannel(); }, {}, {}, true /* test_expects_failure */); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "Unrecognized certificate provider instance name: unknown")); } TEST_P(XdsServerSecurityTest, UnknownRootCertificateProvider) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("unknown", "", "fake_plugin1", "", false); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr( "Unrecognized certificate provider instance name: unknown")); } TEST_P(XdsServerSecurityTest, TestDeprecateTlsCertificateCertificateProviderInstanceField) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); filter_chain->mutable_filters()->at(0).mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_certificate_provider_instance() ->set_instance_name("fake_plugin1"); transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); } TEST_P(XdsServerSecurityTest, CertificatesNotAvailable) { g_fake1_cert_data_map->Set({}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); SendRpc([this]() { return CreateMtlsChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerSecurityTest, TestMtls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); } TEST_P(XdsServerSecurityTest, TestMtlsWithRootPluginUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"", {bad_root_cert_, bad_identity_pair_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); SetLdsUpdate("fake_plugin2", "", "fake_plugin1", "", true); SendRpc([this]() { return CreateMtlsChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerSecurityTest, TestMtlsWithIdentityPluginUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"", {root_cert_, identity_pair_2_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); SetLdsUpdate("fake_plugin1", "", "fake_plugin2", "", true); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_2_, client_authenticated_identity_); } TEST_P(XdsServerSecurityTest, TestMtlsWithBothPluginsUpdated) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"good", {root_cert_, identity_pair_2_}}, {"", {bad_root_cert_, bad_identity_pair_}}}); SetLdsUpdate("fake_plugin2", "", "fake_plugin2", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, {}, {}, true /* test_expects_failure */); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); SetLdsUpdate("fake_plugin2", "good", "fake_plugin2", "good", true); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_2_, client_authenticated_identity_); } TEST_P(XdsServerSecurityTest, TestMtlsWithRootCertificateNameUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"bad", {bad_root_cert_, bad_identity_pair_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); SetLdsUpdate("fake_plugin1", "bad", "fake_plugin1", "", true); SendRpc([this]() { return CreateMtlsChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerSecurityTest, TestMtlsWithIdentityCertificateNameUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"good", {root_cert_, identity_pair_2_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "good", true); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_2_, client_authenticated_identity_); } TEST_P(XdsServerSecurityTest, TestMtlsWithBothCertificateNamesUpdated) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"good", {root_cert_, identity_pair_2_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); SetLdsUpdate("fake_plugin1", "good", "fake_plugin1", "good", true); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_2_, client_authenticated_identity_); } TEST_P(XdsServerSecurityTest, TestMtlsNotRequiringButProvidingClientCerts) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); } TEST_P(XdsServerSecurityTest, TestMtlsNotRequiringAndNotProvidingClientCerts) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); } TEST_P(XdsServerSecurityTest, TestTls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); } TEST_P(XdsServerSecurityTest, TestTlsWithIdentityPluginUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); g_fake2_cert_data_map->Set({{"", {root_cert_, identity_pair_2_}}}); SetLdsUpdate("", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); SetLdsUpdate("", "", "fake_plugin2", "", false); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_2_, {}); } TEST_P(XdsServerSecurityTest, TestTlsWithIdentityCertificateNameUpdate) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}, {"good", {root_cert_, identity_pair_2_}}}); SetLdsUpdate("", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); SetLdsUpdate("", "", "fake_plugin1", "good", false); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_2_, {}); } TEST_P(XdsServerSecurityTest, TestFallback) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("", "", "", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerSecurityTest, TestMtlsToTls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, {}, {}, true /* test_expects_failure */); SetLdsUpdate("", "", "fake_plugin1", "", false); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); } TEST_P(XdsServerSecurityTest, TestTlsToMtls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); SendRpc([this]() { return CreateTlsChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerSecurityTest, TestMtlsToFallback) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); SetLdsUpdate("", "", "", "", false); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerSecurityTest, TestFallbackToMtls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("", "", "", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); SetLdsUpdate("fake_plugin1", "", "fake_plugin1", "", true); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_); } TEST_P(XdsServerSecurityTest, TestTlsToFallback) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("", "", "fake_plugin1", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); SetLdsUpdate("", "", "", "", false); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerSecurityTest, TestFallbackToTls) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); SetLdsUpdate("", "", "", "", false); backends_[0]->Start(); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); SetLdsUpdate("", "", "fake_plugin1", "", false); SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); } class XdsEnabledServerStatusNotificationTest : public XdsServerSecurityTest { protected: void SetValidLdsUpdate() { SetLdsUpdate("", "", "", "", false); } void SetInvalidLdsUpdate() { Listener listener = default_server_listener_; listener.clear_address(); listener.set_name(absl::StrCat( "grpc/server?xds.resource.listening_address=", ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); balancer_->ads_service()->SetLdsResource(listener); } void UnsetLdsUpdate() { balancer_->ads_service()->UnsetResource( kLdsTypeUrl, absl::StrCat("grpc/server?xds.resource.listening_address=", ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port())); } }; TEST_P(XdsEnabledServerStatusNotificationTest, ServingStatus) { SetValidLdsUpdate(); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsEnabledServerStatusNotificationTest, NotServingStatus) { SetInvalidLdsUpdate(); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::UNAVAILABLE); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsEnabledServerStatusNotificationTest, ErrorUpdateWhenAlreadyServing) { SetValidLdsUpdate(); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); // Invalid update does not lead to a change in the serving status. SetInvalidLdsUpdate(); do { SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } while (!balancer_->ads_service()->lds_response_state().has_value()); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsEnabledServerStatusNotificationTest, NotServingStatusToServingStatusTransition) { SetInvalidLdsUpdate(); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::UNAVAILABLE); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); // Send a valid LDS update to change to serving status SetValidLdsUpdate(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } // This test verifies that the resource getting deleted when already serving // results in future connections being dropped. TEST_P(XdsEnabledServerStatusNotificationTest, ServingStatusToNonServingStatusTransition) { SetValidLdsUpdate(); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); // Deleting the resource should result in a non-serving status. UnsetLdsUpdate(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::NOT_FOUND); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsEnabledServerStatusNotificationTest, RepeatedServingStatusChanges) { backends_[0]->Start(); for (int i = 0; i < 5; i++) { // Send a valid LDS update to get the server to start listening SetValidLdsUpdate(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); // Deleting the resource will make the server start rejecting connections UnsetLdsUpdate(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::NOT_FOUND); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } } TEST_P(XdsEnabledServerStatusNotificationTest, ExistingRpcsOnResourceDeletion) { // Send a valid LDS update to get the server to start listening SetValidLdsUpdate(); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); constexpr int kNumChannels = 10; struct StreamingRpc { std::shared_ptr channel; std::unique_ptr stub; ClientContext context; std::unique_ptr> stream; } streaming_rpcs[kNumChannels]; EchoRequest request; EchoResponse response; request.set_message("Hello"); for (int i = 0; i < kNumChannels; i++) { streaming_rpcs[i].channel = CreateInsecureChannel(); streaming_rpcs[i].stub = grpc::testing::EchoTestService::NewStub(streaming_rpcs[i].channel); streaming_rpcs[i].context.set_wait_for_ready(true); streaming_rpcs[i].stream = streaming_rpcs[i].stub->BidiStream(&streaming_rpcs[i].context); EXPECT_TRUE(streaming_rpcs[i].stream->Write(request)); streaming_rpcs[i].stream->Read(&response); EXPECT_EQ(request.message(), response.message()); } // Deleting the resource will make the server start rejecting connections UnsetLdsUpdate(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::NOT_FOUND); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); for (int i = 0; i < kNumChannels; i++) { EXPECT_TRUE(streaming_rpcs[i].stream->Write(request)); streaming_rpcs[i].stream->Read(&response); EXPECT_EQ(request.message(), response.message()); EXPECT_TRUE(streaming_rpcs[i].stream->WritesDone()); auto status = streaming_rpcs[i].stream->Finish(); EXPECT_TRUE(status.ok()) << status.error_message() << ", " << status.error_details() << ", " << streaming_rpcs[i].context.debug_error_string(); // New RPCs on the existing channels should fail. ClientContext new_context; new_context.set_deadline(grpc_timeout_milliseconds_to_deadline(1000)); EXPECT_FALSE( streaming_rpcs[i].stub->Echo(&new_context, request, &response).ok()); } } TEST_P(XdsEnabledServerStatusNotificationTest, ExistingRpcsFailOnResourceUpdateAfterDrainGraceTimeExpires) { constexpr int kDrainGraceTimeMs = 100; xds_drain_grace_time_ms_ = kDrainGraceTimeMs; g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); // Send a valid LDS update to get the server to start listening SetValidLdsUpdate(); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); constexpr int kNumChannels = 10; struct StreamingRpc { std::shared_ptr channel; std::unique_ptr stub; ClientContext context; std::unique_ptr> stream; } streaming_rpcs[kNumChannels]; EchoRequest request; EchoResponse response; request.set_message("Hello"); for (int i = 0; i < kNumChannels; i++) { streaming_rpcs[i].channel = CreateInsecureChannel(); streaming_rpcs[i].stub = grpc::testing::EchoTestService::NewStub(streaming_rpcs[i].channel); streaming_rpcs[i].context.set_wait_for_ready(true); streaming_rpcs[i].stream = streaming_rpcs[i].stub->BidiStream(&streaming_rpcs[i].context); EXPECT_TRUE(streaming_rpcs[i].stream->Write(request)); streaming_rpcs[i].stream->Read(&response); EXPECT_EQ(request.message(), response.message()); } grpc_core::Timestamp update_time = NowFromCycleCounter(); // Update the resource. SetLdsUpdate("", "", "fake_plugin1", "", false); // Wait for the updated resource to take effect. SendRpc([this]() { return CreateTlsChannel(); }, server_authenticated_identity_, {}); // After the drain grace time expires, the existing RPCs should all fail. for (int i = 0; i < kNumChannels; i++) { // Wait for the drain grace time to expire EXPECT_FALSE(streaming_rpcs[i].stream->Read(&response)); // Make sure that the drain grace interval is honored. EXPECT_GE(NowFromCycleCounter() - update_time, grpc_core::Duration::Milliseconds(kDrainGraceTimeMs)); auto status = streaming_rpcs[i].stream->Finish(); EXPECT_EQ(status.error_code(), grpc::StatusCode::UNAVAILABLE) << status.error_code() << ", " << status.error_message() << ", " << status.error_details() << ", " << streaming_rpcs[i].context.debug_error_string(); } } using XdsServerFilterChainMatchTest = XdsServerSecurityTest; TEST_P(XdsServerFilterChainMatchTest, DefaultFilterChainUsedWhenNoFilterChainMentioned) { backends_[0]->Start(); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerFilterChainMatchTest, DefaultFilterChainUsedWhenOtherFilterChainsDontMatch) { Listener listener = default_server_listener_; // Add a filter chain that will never get matched auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match() ->mutable_destination_port() ->set_value(8080); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithDestinationPortDontMatch) { Listener listener = default_server_listener_; // Add filter chain with destination port that should never get matched auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match() ->mutable_destination_port() ->set_value(8080); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // RPC should fail since no matching filter chain was found and no default // filter chain is configured. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithServerNamesDontMatch) { Listener listener = default_server_listener_; // Add filter chain with server name that should never get matched auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // RPC should fail since no matching filter chain was found and no default // filter chain is configured. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithTransportProtocolsOtherThanRawBufferDontMatch) { Listener listener = default_server_listener_; // Add filter chain with transport protocol "tls" that should never match auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_transport_protocol("tls"); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // RPC should fail since no matching filter chain was found and no default // filter chain is configured. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithApplicationProtocolsDontMatch) { Listener listener = default_server_listener_; // Add filter chain with application protocol that should never get matched auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_application_protocols("h2"); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // RPC should fail since no matching filter chain was found and no default // filter chain is configured. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithTransportProtocolRawBufferIsPreferred) { Listener listener = default_server_listener_; // Add filter chain with "raw_buffer" transport protocol auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_transport_protocol( "raw_buffer"); // Add another filter chain with no transport protocol set but application // protocol set (fails match) filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_application_protocols("h2"); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // A successful RPC proves that filter chains that mention "raw_buffer" as // the transport protocol are chosen as the best match in the round. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithMoreSpecificDestinationPrefixRangesArePreferred) { Listener listener = default_server_listener_; // Add filter chain with prefix range (length 4 and 16) but with server name // mentioned. (Prefix range is matched first.) auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); auto* prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(4); prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(16); filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); // Add filter chain with two prefix ranges (length 8 and 24). Since 24 is // the highest match, it should be chosen. filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(8); prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(24); // Add another filter chain with a non-matching prefix range (with length // 30) filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix("192.168.1.1"); prefix_range->mutable_prefix_len()->set_value(30); filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); // Add another filter chain with no prefix range mentioned filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_server_names("server_name"); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // A successful RPC proves that the filter chain with the longest matching // prefix range was the best match. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsThatMentionSourceTypeArePreferred) { Listener listener = default_server_listener_; // Add filter chain with the local source type (best match) auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::SAME_IP_OR_LOOPBACK); // Add filter chain with the external source type but bad source port. // Note that backends_[0]->port() will never be a match for the source port // because it is already being used by a backend. filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::EXTERNAL); filter_chain->mutable_filter_chain_match()->add_source_ports( backends_[0]->port()); // Add filter chain with the default source type (ANY) but bad source port. filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_source_ports( backends_[0]->port()); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // A successful RPC proves that the filter chain with the longest matching // prefix range was the best match. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithMoreSpecificSourcePrefixRangesArePreferred) { Listener listener = default_server_listener_; // Add filter chain with source prefix range (length 16) but with a bad // source port mentioned. (Prefix range is matched first.) Note that // backends_[0]->port() will never be a match for the source port because it // is already being used by a backend. auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); auto* source_prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); source_prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); source_prefix_range->mutable_prefix_len()->set_value(4); source_prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); source_prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); source_prefix_range->mutable_prefix_len()->set_value(16); filter_chain->mutable_filter_chain_match()->add_source_ports( backends_[0]->port()); // Add filter chain with two source prefix ranges (length 8 and 24). Since // 24 is the highest match, it should be chosen. filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); source_prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); source_prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); source_prefix_range->mutable_prefix_len()->set_value(8); source_prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); source_prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); source_prefix_range->mutable_prefix_len()->set_value(24); // Add another filter chain with a non-matching source prefix range (with // length 30) and bad source port filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); source_prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); source_prefix_range->set_address_prefix("192.168.1.1"); source_prefix_range->mutable_prefix_len()->set_value(30); filter_chain->mutable_filter_chain_match()->add_source_ports( backends_[0]->port()); // Add another filter chain with no source prefix range mentioned and bad // source port filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_source_ports( backends_[0]->port()); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // A successful RPC proves that the filter chain with the longest matching // source prefix range was the best match. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerFilterChainMatchTest, FilterChainsWithMoreSpecificSourcePortArePreferred) { Listener listener = default_server_listener_; auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); // Since we don't know which port will be used by the channel, just add all // ports except for 0. for (int i = 1; i < 65536; i++) { filter_chain->mutable_filter_chain_match()->add_source_ports(i); } // Add another filter chain with no source port mentioned with a bad // DownstreamTlsContext configuration. filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name("fake_plugin1"); transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); listener.clear_default_filter_chain(); balancer_->ads_service()->SetLdsResource( PopulateServerListenerNameAndPort(listener, backends_[0]->port())); backends_[0]->Start(); // A successful RPC proves that the filter chain with matching source port // was chosen. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchNacked) { Listener listener = default_server_listener_; // Add filter chain auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); // Add a duplicate filter chain filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "Duplicate matching rules detected when adding filter chain: {}")); } TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnPrefixRangesNacked) { Listener listener = default_server_listener_; // Add filter chain with prefix range auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); auto* prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(16); prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(24); // Add a filter chain with a duplicate prefix range entry filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(16); prefix_range = filter_chain->mutable_filter_chain_match()->add_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(32); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; if (ipv6_only_) { EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "Duplicate matching rules detected when adding filter chain: " "{prefix_ranges={{address_prefix=[::]:0, prefix_len=16}, " "{address_prefix=[::]:0, prefix_len=32}}}")); } else { EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "Duplicate matching rules detected when adding filter chain: " "{prefix_ranges={{address_prefix=127.0.0.0:0, prefix_len=16}, " "{address_prefix=127.0.0.1:0, prefix_len=32}}}")); } } TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnTransportProtocolNacked) { Listener listener = default_server_listener_; // Add filter chain with "raw_buffer" transport protocol auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_transport_protocol( "raw_buffer"); // Add a duplicate filter chain with the same "raw_buffer" transport // protocol entry filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_transport_protocol( "raw_buffer"); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Duplicate matching rules detected when adding " "filter chain: {transport_protocol=raw_buffer}")); } TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnLocalSourceTypeNacked) { Listener listener = default_server_listener_; // Add filter chain with the local source type auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::SAME_IP_OR_LOOPBACK); // Add a duplicate filter chain with the same local source type entry filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::SAME_IP_OR_LOOPBACK); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Duplicate matching rules detected when adding " "filter chain: {source_type=SAME_IP_OR_LOOPBACK}")); } TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnExternalSourceTypeNacked) { Listener listener = default_server_listener_; // Add filter chain with the external source type auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::EXTERNAL); // Add a duplicate filter chain with the same external source type entry filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::EXTERNAL); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Duplicate matching rules detected when adding " "filter chain: {source_type=EXTERNAL}")); } TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnSourcePrefixRangesNacked) { Listener listener = default_server_listener_; // Add filter chain with source prefix range auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); auto* prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(16); prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(24); // Add a filter chain with a duplicate source prefix range entry filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(16); prefix_range = filter_chain->mutable_filter_chain_match()->add_source_prefix_ranges(); prefix_range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); prefix_range->mutable_prefix_len()->set_value(32); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; if (ipv6_only_) { EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "Duplicate matching rules detected when adding filter chain: " "{source_prefix_ranges={{address_prefix=[::]:0, prefix_len=16}, " "{address_prefix=[::]:0, prefix_len=32}}}")); } else { EXPECT_THAT( response_state->error_message, ::testing::HasSubstr( "Duplicate matching rules detected when adding filter chain: " "{source_prefix_ranges={{address_prefix=127.0.0.0:0, " "prefix_len=16}, " "{address_prefix=127.0.0.1:0, prefix_len=32}}}")); } } TEST_P(XdsServerFilterChainMatchTest, DuplicateMatchOnSourcePortNacked) { Listener listener = default_server_listener_; // Add filter chain with the external source type auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_source_ports(8080); // Add a duplicate filter chain with the same source port entry filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( ServerHcmAccessor().Unpack(listener)); filter_chain->mutable_filter_chain_match()->add_source_ports(8080); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT( response_state->error_message, ::testing::HasSubstr("Duplicate matching rules detected when adding " "filter chain: {source_ports={8080}}")); } class XdsServerRdsTest : public XdsEnabledServerStatusNotificationTest { protected: XdsServerRdsTest() : env_var_("GRPC_XDS_EXPERIMENTAL_RBAC") {} ScopedExperimentalEnvVar env_var_; }; TEST_P(XdsServerRdsTest, Basic) { backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerRdsTest, NacksInvalidDomainPattern) { RouteConfiguration route_config = default_server_route_config_; route_config.mutable_virtual_hosts()->at(0).add_domains(""); SetServerListenerNameAndRouteConfiguration( balancer_.get(), default_server_listener_, backends_[0]->port(), route_config); backends_[0]->Start(); const auto response_state = WaitForRouteConfigNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("Invalid domain pattern \"\"")); } TEST_P(XdsServerRdsTest, NacksEmptyDomainsList) { RouteConfiguration route_config = default_server_route_config_; route_config.mutable_virtual_hosts()->at(0).clear_domains(); SetServerListenerNameAndRouteConfiguration( balancer_.get(), default_server_listener_, backends_[0]->port(), route_config); backends_[0]->Start(); const auto response_state = WaitForRouteConfigNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("VirtualHost has no domains")); } TEST_P(XdsServerRdsTest, NacksEmptyRoutesList) { RouteConfiguration route_config = default_server_route_config_; route_config.mutable_virtual_hosts()->at(0).clear_routes(); SetServerListenerNameAndRouteConfiguration( balancer_.get(), default_server_listener_, backends_[0]->port(), route_config); backends_[0]->Start(); const auto response_state = WaitForRouteConfigNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("No route found in the virtual host")); } TEST_P(XdsServerRdsTest, NacksEmptyMatch) { RouteConfiguration route_config = default_server_route_config_; route_config.mutable_virtual_hosts() ->at(0) .mutable_routes() ->at(0) .clear_match(); SetServerListenerNameAndRouteConfiguration( balancer_.get(), default_server_listener_, backends_[0]->port(), route_config); backends_[0]->Start(); const auto response_state = WaitForRouteConfigNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("Match can't be null")); } TEST_P(XdsServerRdsTest, FailsRouteMatchesOtherThanNonForwardingAction) { SetServerListenerNameAndRouteConfiguration( balancer_.get(), default_server_listener_, backends_[0]->port(), default_route_config_ /* inappropriate route config for servers */); backends_[0]->Start(); // The server should be ready to serve but RPCs should fail. backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } // Test that non-inline route configuration also works for non-default filter // chains TEST_P(XdsServerRdsTest, NonInlineRouteConfigurationNonDefaultFilterChain) { if (!GetParam().enable_rds_testing()) { return; } Listener listener = default_server_listener_; auto* filter_chain = listener.add_filter_chains(); HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); auto* rds = http_connection_manager.mutable_rds(); rds->set_route_config_name(kDefaultServerRouteConfigurationName); rds->mutable_config_source()->mutable_self(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( http_connection_manager); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsServerRdsTest, NonInlineRouteConfigurationNotAvailable) { if (!GetParam().enable_rds_testing()) { return; } Listener listener = default_server_listener_; PopulateServerListenerNameAndPort(listener, backends_[0]->port()); HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); auto* rds = http_connection_manager.mutable_rds(); rds->set_route_config_name("unknown_server_route_config"); rds->mutable_config_source()->mutable_self(); listener.add_filter_chains()->add_filters()->mutable_typed_config()->PackFrom( http_connection_manager); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, true /* test_expects_failure */); } // TODO(yashykt): Once https://github.com/grpc/grpc/issues/24035 is fixed, we // should add tests that make sure that different route configs are used for // incoming connections with a different match. TEST_P(XdsServerRdsTest, MultipleRouteConfigurations) { Listener listener = default_server_listener_; // Set a filter chain with a new route config name auto new_route_config = default_server_route_config_; new_route_config.set_name("new_server_route_config"); HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); auto* rds = http_connection_manager.mutable_rds(); rds->set_route_config_name(new_route_config.name()); rds->mutable_config_source()->mutable_self(); listener.add_filter_chains()->add_filters()->mutable_typed_config()->PackFrom( http_connection_manager); // Set another filter chain with another route config name auto another_route_config = default_server_route_config_; another_route_config.set_name("another_server_route_config"); http_connection_manager.mutable_rds()->set_route_config_name( another_route_config.name()); auto* filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( http_connection_manager); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::SAME_IP_OR_LOOPBACK); // Add another filter chain with the same route config name filter_chain = listener.add_filter_chains(); filter_chain->add_filters()->mutable_typed_config()->PackFrom( http_connection_manager); filter_chain->mutable_filter_chain_match()->set_source_type( FilterChainMatch::EXTERNAL); // Add another filter chain with an inline route config filter_chain = listener.add_filter_chains(); filter_chain->mutable_filter_chain_match()->add_source_ports(1234); http_connection_manager = ServerHcmAccessor().Unpack(listener); *http_connection_manager.mutable_route_config() = default_server_route_config_; filter_chain->add_filters()->mutable_typed_config()->PackFrom( http_connection_manager); // Set resources on the ADS service balancer_->ads_service()->SetRdsResource(new_route_config); balancer_->ads_service()->SetRdsResource(another_route_config); SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, backends_[0]->port(), default_server_route_config_); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } // Tests RBAC configurations on the server with RDS testing and route config // override permutations. class XdsRbacTest : public XdsServerRdsTest { protected: void SetServerRbacPolicies(Listener listener, const std::vector& rbac_policies) { HttpConnectionManager http_connection_manager = ServerHcmAccessor().Unpack(listener); http_connection_manager.clear_http_filters(); RouteConfiguration route_config = default_server_route_config_; int count = 0; for (auto& rbac : rbac_policies) { auto* filter = http_connection_manager.add_http_filters(); std::string filter_name = absl::StrFormat("rbac%d", ++count); filter->set_name(filter_name); switch (GetParam().filter_config_setup()) { case XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInListener: filter->mutable_typed_config()->PackFrom(rbac); break; case XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute: filter->mutable_typed_config()->PackFrom(RBAC()); google::protobuf::Any filter_config; RBACPerRoute rbac_per_route; *rbac_per_route.mutable_rbac() = rbac; filter_config.PackFrom(rbac_per_route); auto* config_map = route_config.mutable_virtual_hosts(0) ->mutable_routes(0) ->mutable_typed_per_filter_config(); (*config_map)[filter_name] = std::move(filter_config); } } auto* filter = http_connection_manager.add_http_filters(); filter->set_name("router"); filter->mutable_typed_config()->PackFrom( envoy::extensions::filters::http::router::v3::Router()); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration( balancer_.get(), listener, backends_[0]->port(), route_config); } void SetServerRbacPolicy(Listener listener, const RBAC& rbac) { SetServerRbacPolicies(std::move(listener), {rbac}); } void SetServerRbacPolicy(const RBAC& rbac) { SetServerRbacPolicy(default_server_listener_, rbac); } }; TEST_P(XdsRbacTest, AbsentRbacPolicy) { SetServerRbacPolicy(RBAC()); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // An absent RBAC policy leads to all RPCs being accepted. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } TEST_P(XdsRbacTest, LogAction) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(envoy::config::rbac::v3::RBAC_Action_LOG); SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // A Log action is identical to no rbac policy being configured. SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } using XdsRbacNackTest = XdsRbacTest; TEST_P(XdsRbacNackTest, NacksSchemePrincipalHeader) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); Policy policy; auto* header = policy.add_principals()->mutable_header(); header->set_name(":scheme"); header->set_exact_match("http"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); if (GetParam().enable_rds_testing() && GetParam().filter_config_setup() == XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { const auto response_state = WaitForRdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("':scheme' not allowed in header")); } else { const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("':scheme' not allowed in header")); } } TEST_P(XdsRbacNackTest, NacksGrpcPrefixedPrincipalHeaders) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); Policy policy; auto* header = policy.add_principals()->mutable_header(); header->set_name("grpc-status"); header->set_exact_match("0"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); if (GetParam().enable_rds_testing() && GetParam().filter_config_setup() == XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { const auto response_state = WaitForRdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); } else { const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); } } TEST_P(XdsRbacNackTest, NacksSchemePermissionHeader) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); Policy policy; auto* header = policy.add_permissions()->mutable_header(); header->set_name(":scheme"); header->set_exact_match("http"); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); if (GetParam().enable_rds_testing() && GetParam().filter_config_setup() == XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { const auto response_state = WaitForRdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("':scheme' not allowed in header")); } else { const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("':scheme' not allowed in header")); } } TEST_P(XdsRbacNackTest, NacksGrpcPrefixedPermissionHeaders) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(envoy::config::rbac::v3::RBAC_Action_ALLOW); Policy policy; auto* header = policy.add_permissions()->mutable_header(); header->set_name("grpc-status"); header->set_exact_match("0"); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); if (GetParam().enable_rds_testing() && GetParam().filter_config_setup() == XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) { const auto response_state = WaitForRdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); } else { const auto response_state = WaitForLdsNack(DEBUG_LOCATION); ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; EXPECT_THAT(response_state->error_message, ::testing::HasSubstr("'grpc-' prefixes not allowed in header")); } } // Tests RBAC policies where a route override is always present. Action // permutations are not added. using XdsRbacTestWithRouteOverrideAlwaysPresent = XdsRbacTest; TEST_P(XdsRbacTestWithRouteOverrideAlwaysPresent, EmptyRBACPerRouteOverride) { HttpConnectionManager http_connection_manager; Listener listener = default_server_listener_; RouteConfiguration route_config = default_server_route_config_; auto* filter = http_connection_manager.add_http_filters(); filter->set_name("rbac"); // Create a top-level RBAC policy with a DENY action for all RPCs RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(RBAC_Action_DENY); Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; filter->mutable_typed_config()->PackFrom(rbac); // Override with an Empty RBACPerRoute policy which should result in RBAC // being disabled and RPCs being allowed. google::protobuf::Any filter_config; filter_config.PackFrom(RBACPerRoute()); auto* config_map = route_config.mutable_virtual_hosts(0) ->mutable_routes(0) ->mutable_typed_per_filter_config(); (*config_map)["rbac"] = std::move(filter_config); filter = http_connection_manager.add_http_filters(); filter->set_name("router"); filter->mutable_typed_config()->PackFrom( envoy::extensions::filters::http::router::v3::Router()); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration( balancer_.get(), listener, backends_[0]->port(), route_config); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } // Test a non-empty top level RBAC with a non-empty RBACPerRouteOverride TEST_P(XdsRbacTestWithRouteOverrideAlwaysPresent, NonEmptyTopLevelRBACNonEmptyPerRouteOverride) { HttpConnectionManager http_connection_manager; Listener listener = default_server_listener_; RouteConfiguration route_config = default_server_route_config_; auto* filter = http_connection_manager.add_http_filters(); filter->set_name("rbac"); // Create a top-level RBAC policy with a DENY action for all RPCs RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(RBAC_Action_DENY); Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; filter->mutable_typed_config()->PackFrom(rbac); // Override with a non-empty RBACPerRoute policy which allows all RPCs. google::protobuf::Any filter_config; RBACPerRoute rbac_per_route; rules = rbac_per_route.mutable_rbac()->mutable_rules(); rules->set_action(RBAC_Action_ALLOW); (*rules->mutable_policies())["policy"] = policy; filter_config.PackFrom(RBACPerRoute()); auto* config_map = route_config.mutable_virtual_hosts(0) ->mutable_routes(0) ->mutable_typed_per_filter_config(); (*config_map)["rbac"] = std::move(filter_config); filter = http_connection_manager.add_http_filters(); filter->set_name("router"); filter->mutable_typed_config()->PackFrom( envoy::extensions::filters::http::router::v3::Router()); ServerHcmAccessor().Pack(http_connection_manager, &listener); SetServerListenerNameAndRouteConfiguration( balancer_.get(), listener, backends_[0]->port(), route_config); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}); } // Adds Action Permutations to XdsRbacTest using XdsRbacTestWithActionPermutations = XdsRbacTest; TEST_P(XdsRbacTestWithActionPermutations, EmptyRbacPolicy) { RBAC rbac; rbac.mutable_rules()->set_action(GetParam().rbac_action()); SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // An empty RBAC policy leads to all RPCs being rejected. SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, MultipleRbacPolicies) { RBAC always_allow; auto* rules = always_allow.mutable_rules(); rules->set_action(RBAC_Action_ALLOW); Policy policy; policy.add_permissions()->set_any(true); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; RBAC rbac; rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicies(default_server_listener_, {always_allow, rbac, always_allow}); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, MethodPostPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* header = policy.add_permissions()->mutable_header(); header->set_name(":method"); header->set_exact_match("POST"); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->set_allow_put_requests(true); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // All RPCs use POST method by default SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Test that an RPC with PUT method is handled properly. SendRpc([this]() { return CreateInsecureChannel(/*use_put_requests=*/true); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() != RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, MethodGetPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* header = policy.add_permissions()->mutable_header(); header->set_name(":method"); header->set_exact_match("GET"); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // Test that an RPC with a POST method gets rejected SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); // TODO(yashykt): When we start supporting GET requests in the future, this // should be modified to test that they are accepted with this rule. } TEST_P(XdsRbacTestWithActionPermutations, MethodPutPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* header = policy.add_permissions()->mutable_header(); header->set_name(":method"); header->set_exact_match("PUT"); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->set_allow_put_requests(true); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // Test that an RPC with a POST method gets rejected SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); // Test that an RPC with a PUT method gets accepted SendRpc( [this]() { return CreateInsecureChannel(/*use_put_requests=*/true); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() != RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, UrlPathPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_permissions()->mutable_url_path()->mutable_path()->set_exact( "/grpc.testing.EchoTestService/Echo"); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Test an RPC with a different URL path auto stub = grpc::testing::EchoTestService::NewStub(CreateInsecureChannel()); ClientContext context; context.set_wait_for_ready(true); context.set_deadline(grpc_timeout_milliseconds_to_deadline(2000)); EchoRequest request; request.set_message(kRequestMessage); EchoResponse response; Status status = stub->Echo1(&context, request, &response); EXPECT_TRUE(GetParam().rbac_action() == RBAC_Action_DENY ? status.ok() : !status.ok()) << status.error_code() << ", " << status.error_message() << ", " << status.error_details() << ", " << context.debug_error_string(); } TEST_P(XdsRbacTestWithActionPermutations, DestinationIpPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* range = policy.add_permissions()->mutable_destination_ip(); range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); range->mutable_prefix_len()->set_value(ipv6_only_ ? 128 : 32); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. policy.clear_permissions(); range = policy.add_permissions()->mutable_destination_ip(); range->set_address_prefix(ipv6_only_ ? "::2" : "127.0.0.2"); range->mutable_prefix_len()->set_value(ipv6_only_ ? 128 : 32); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, DestinationPortPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_permissions()->set_destination_port(backends_[0]->port()); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. policy.clear_permissions(); policy.add_permissions()->set_destination_port(1); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, MetadataPermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_permissions()->mutable_metadata(); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); // Test metadata with inverted match policy.clear_permissions(); policy.add_permissions()->mutable_metadata()->set_invert(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, ReqServerNamePermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_principals()->set_any(true); policy.add_permissions()->mutable_requested_server_name()->set_exact( "server_name"); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); policy.clear_permissions(); policy.add_permissions()->mutable_requested_server_name()->set_exact(""); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, NotRulePermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_permissions() ->mutable_not_rule() ->mutable_requested_server_name() ->set_exact("server_name"); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. policy.clear_permissions(); policy.add_permissions()->mutable_not_rule()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AndRulePermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* and_rules = policy.add_permissions()->mutable_and_rules(); and_rules->add_rules()->set_any(true); and_rules->add_rules()->set_destination_port(backends_[0]->port()); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. and_rules = (*policy.mutable_permissions())[0].mutable_and_rules(); (*and_rules->mutable_rules())[1].set_destination_port(1); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, OrRulePermissionAnyPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* or_rules = policy.add_permissions()->mutable_or_rules(); or_rules->add_rules()->mutable_not_rule()->set_any(true); or_rules->add_rules()->set_destination_port(backends_[0]->port()); policy.add_principals()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. or_rules = (*policy.mutable_permissions())[0].mutable_or_rules(); (*or_rules->mutable_rules())[1].set_destination_port(1); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionMethodPostPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* header = policy.add_principals()->mutable_header(); header->set_name(":method"); header->set_exact_match("POST"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->set_allow_put_requests(true); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // All RPCs use POST method by default SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Test that an RPC with PUT method is handled properly. SendRpc([this]() { return CreateInsecureChannel(/*use_put_requests=*/true); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() != RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionMethodGetPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* header = policy.add_principals()->mutable_header(); header->set_name(":method"); header->set_exact_match("GET"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // Test that an RPC with a POST method gets rejected SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); // TODO(yashykt): When we start supporting GET requests in the future, this // should be modified to test that they are accepted with this rule. } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionMethodPutPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* header = policy.add_principals()->mutable_header(); header->set_name(":method"); header->set_exact_match("PUT"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->set_allow_put_requests(true); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); // Test that an RPC with a PUT method gets accepted SendRpc( [this]() { return CreateInsecureChannel(/*use_put_requests=*/true); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() != RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); // Test that an RPC with a POST method gets rejected SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionUrlPathPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_principals()->mutable_url_path()->mutable_path()->set_exact( "/grpc.testing.EchoTestService/Echo"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Test an RPC with a different URL path auto stub = grpc::testing::EchoTestService::NewStub(CreateInsecureChannel()); ClientContext context; context.set_wait_for_ready(true); context.set_deadline(grpc_timeout_milliseconds_to_deadline(2000)); EchoRequest request; request.set_message(kRequestMessage); EchoResponse response; Status status = stub->Echo1(&context, request, &response); EXPECT_TRUE(GetParam().rbac_action() == RBAC_Action_DENY ? status.ok() : !status.ok()) << status.error_code() << ", " << status.error_message() << ", " << status.error_details() << ", " << context.debug_error_string(); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionDirectRemoteIpPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* range = policy.add_principals()->mutable_direct_remote_ip(); range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); range->mutable_prefix_len()->set_value(ipv6_only_ ? 128 : 32); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. policy.clear_principals(); range = policy.add_principals()->mutable_direct_remote_ip(); range->set_address_prefix(ipv6_only_ ? "::2" : "127.0.0.2"); range->mutable_prefix_len()->set_value(ipv6_only_ ? 128 : 32); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionRemoteIpPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* range = policy.add_principals()->mutable_remote_ip(); range->set_address_prefix(ipv6_only_ ? "::1" : "127.0.0.1"); range->mutable_prefix_len()->set_value(ipv6_only_ ? 128 : 32); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. policy.clear_principals(); range = policy.add_principals()->mutable_remote_ip(); range->set_address_prefix(ipv6_only_ ? "::2" : "127.0.0.2"); range->mutable_prefix_len()->set_value(ipv6_only_ ? 128 : 32); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionAuthenticatedPrincipal) { g_fake1_cert_data_map->Set({{"", {root_cert_, identity_pair_}}}); Listener listener = default_server_listener_; auto* filter_chain = listener.mutable_default_filter_chain(); auto* transport_socket = filter_chain->mutable_transport_socket(); transport_socket->set_name("envoy.transport_sockets.tls"); DownstreamTlsContext downstream_tls_context; downstream_tls_context.mutable_common_tls_context() ->mutable_tls_certificate_provider_instance() ->set_instance_name("fake_plugin1"); downstream_tls_context.mutable_common_tls_context() ->mutable_validation_context() ->mutable_ca_certificate_provider_instance() ->set_instance_name("fake_plugin1"); downstream_tls_context.mutable_require_client_certificate()->set_value(true); transport_socket->mutable_typed_config()->PackFrom(downstream_tls_context); RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_principals() ->mutable_authenticated() ->mutable_principal_name() ->set_exact("*.test.google.fr"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(listener, rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateMtlsChannel(); }, server_authenticated_identity_, client_authenticated_identity_, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionMetadataPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_principals()->mutable_metadata(); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); // Test metadata with inverted match policy.clear_principals(); policy.add_principals()->mutable_metadata()->set_invert(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionNotIdPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; policy.add_principals() ->mutable_not_id() ->mutable_url_path() ->mutable_path() ->set_exact("/grpc.testing.EchoTestService/Echo1"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. policy.clear_principals(); policy.add_principals()->mutable_not_id()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionAndIdPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* and_ids = policy.add_principals()->mutable_and_ids(); and_ids->add_ids()->set_any(true); and_ids->add_ids()->mutable_url_path()->mutable_path()->set_exact( "/grpc.testing.EchoTestService/Echo"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. and_ids = (*policy.mutable_principals())[0].mutable_and_ids(); (*and_ids->mutable_ids())[1].mutable_url_path()->mutable_path()->set_exact( "/grpc.testing.EchoTestService/Echo1"); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } TEST_P(XdsRbacTestWithActionPermutations, AnyPermissionOrIdPrincipal) { RBAC rbac; auto* rules = rbac.mutable_rules(); rules->set_action(GetParam().rbac_action()); Policy policy; auto* or_ids = policy.add_principals()->mutable_or_ids(); or_ids->add_ids()->mutable_not_id()->set_any(true); or_ids->add_ids()->mutable_url_path()->mutable_path()->set_exact( "/grpc.testing.EchoTestService/Echo"); policy.add_permissions()->set_any(true); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); backends_[0]->Start(); backends_[0]->notifier()->WaitOnServingStatusChange( absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()), grpc::StatusCode::OK); SendRpc([this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_DENY, grpc::StatusCode::PERMISSION_DENIED); // Change the policy itself for a negative test where there is no match. or_ids = (*policy.mutable_principals())[0].mutable_or_ids(); (*or_ids->mutable_ids())[1].mutable_url_path()->mutable_path()->set_exact( "/grpc.testing.EchoTestService/Echo1"); (*rules->mutable_policies())["policy"] = policy; SetServerRbacPolicy(rbac); SendRpc( [this]() { return CreateInsecureChannel(); }, {}, {}, /*test_expects_failure=*/GetParam().rbac_action() == RBAC_Action_ALLOW, grpc::StatusCode::PERMISSION_DENIED); } // CDS depends on XdsResolver. // Security depends on v3. // Not enabling load reporting or RDS, since those are irrelevant to these // tests. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsSecurityTest, ::testing::Values(XdsTestType().set_use_xds_credentials()), &XdsTestType::Name); // We are only testing the server here. // Run with bootstrap from env var, so that we use a global XdsClient // instance. Otherwise, we would need to use a separate fake resolver // result generator on the client and server sides. INSTANTIATE_TEST_SUITE_P(XdsTest, XdsEnabledServerTest, ::testing::Values(XdsTestType().set_bootstrap_source( XdsTestType::kBootstrapFromEnvVar)), &XdsTestType::Name); // We are only testing the server here. // Run with bootstrap from env var so that we use one XdsClient. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsServerSecurityTest, ::testing::Values( XdsTestType() .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar) .set_use_xds_credentials()), &XdsTestType::Name); INSTANTIATE_TEST_SUITE_P( XdsTest, XdsEnabledServerStatusNotificationTest, ::testing::Values(XdsTestType().set_use_xds_credentials()), &XdsTestType::Name); // Run with bootstrap from env var so that we use one XdsClient. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsServerFilterChainMatchTest, ::testing::Values( XdsTestType() .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar) .set_use_xds_credentials()), &XdsTestType::Name); // Test xDS-enabled server with and without RDS. // Run with bootstrap from env var so that we use one XdsClient. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsServerRdsTest, ::testing::Values( XdsTestType() .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar) .set_use_xds_credentials(), XdsTestType() .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar) .set_use_xds_credentials() .set_enable_rds_testing()), &XdsTestType::Name); // We are only testing the server here. // Run with bootstrap from env var, so that we use a global XdsClient // instance. Otherwise, we would need to use a separate fake resolver // result generator on the client and server sides. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsRbacTest, ::testing::Values( XdsTestType().set_use_xds_credentials().set_bootstrap_source( XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_enable_rds_testing() .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_enable_rds_testing() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar)), &XdsTestType::Name); // We are only testing the server here. // Run with bootstrap from env var, so that we use a global XdsClient // instance. Otherwise, we would need to use a separate fake resolver // result generator on the client and server sides. // Note that we are simply using the default fake credentials instead of xds // credentials for NACK tests to avoid a mismatch between the client and the // server's security settings when using the WaitForNack() infrastructure. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsRbacNackTest, ::testing::Values( XdsTestType().set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType().set_enable_rds_testing().set_bootstrap_source( XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_enable_rds_testing() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar)), &XdsTestType::Name); // We are only testing the server here. // Run with bootstrap from env var, so that we use a global XdsClient // instance. Otherwise, we would need to use a separate fake resolver // result generator on the client and server sides. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsRbacTestWithRouteOverrideAlwaysPresent, ::testing::Values( XdsTestType() .set_use_xds_credentials() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_enable_rds_testing() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar)), &XdsTestType::Name); // We are only testing the server here. // Run with bootstrap from env var, so that we use a global XdsClient // instance. Otherwise, we would need to use a separate fake resolver // result generator on the client and server sides. INSTANTIATE_TEST_SUITE_P( XdsTest, XdsRbacTestWithActionPermutations, ::testing::Values( XdsTestType() .set_use_xds_credentials() .set_rbac_action(RBAC_Action_ALLOW) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_rbac_action(RBAC_Action_DENY) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_enable_rds_testing() .set_rbac_action(RBAC_Action_ALLOW) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_enable_rds_testing() .set_rbac_action(RBAC_Action_DENY) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_rbac_action(RBAC_Action_ALLOW) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_rbac_action(RBAC_Action_DENY) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_enable_rds_testing() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_rbac_action(RBAC_Action_ALLOW) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar), XdsTestType() .set_use_xds_credentials() .set_enable_rds_testing() .set_filter_config_setup( XdsTestType::HttpFilterConfigLocation::kHttpFilterConfigInRoute) .set_rbac_action(RBAC_Action_DENY) .set_bootstrap_source(XdsTestType::kBootstrapFromEnvVar)), &XdsTestType::Name); } // namespace } // namespace testing } // namespace grpc int main(int argc, char** argv) { grpc::testing::TestEnvironment env(&argc, argv); ::testing::InitGoogleTest(&argc, argv); // Make the backup poller poll very frequently in order to pick up // updates from all the subchannels's FDs. GPR_GLOBAL_CONFIG_SET(grpc_client_channel_backup_poll_interval_ms, 1); #if TARGET_OS_IPHONE // Workaround Apple CFStream bug gpr_setenv("grpc_cfstream", "0"); #endif grpc::testing::FakeCertificateProvider::CertDataMapWrapper cert_data_map_1; grpc::testing::g_fake1_cert_data_map = &cert_data_map_1; grpc_core::CertificateProviderRegistry::RegisterCertificateProviderFactory( absl::make_unique( "fake1", grpc::testing::g_fake1_cert_data_map)); grpc::testing::FakeCertificateProvider::CertDataMapWrapper cert_data_map_2; grpc::testing::g_fake2_cert_data_map = &cert_data_map_2; grpc_core::CertificateProviderRegistry::RegisterCertificateProviderFactory( absl::make_unique( "fake2", grpc::testing::g_fake2_cert_data_map)); grpc_init(); grpc_core::XdsHttpFilterRegistry::RegisterFilter( absl::make_unique( "grpc.testing.client_only_http_filter", /* supported_on_clients = */ true, /* supported_on_servers = */ false, /* is_terminal_filter */ false), {"grpc.testing.client_only_http_filter"}); grpc_core::XdsHttpFilterRegistry::RegisterFilter( absl::make_unique( "grpc.testing.server_only_http_filter", /* supported_on_clients = */ false, /* supported_on_servers = */ true, /* is_terminal_filter */ false), {"grpc.testing.server_only_http_filter"}); grpc_core::XdsHttpFilterRegistry::RegisterFilter( absl::make_unique( "grpc.testing.terminal_http_filter", /* supported_on_clients = */ true, /* supported_on_servers = */ true, /* is_terminal_filter */ true), {"grpc.testing.terminal_http_filter"}); const auto result = RUN_ALL_TESTS(); grpc_shutdown(); return result; }