blob: a1843007939688c5d136691add5585af28f1ff2d [file] [log] [blame]
// 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 <deque>
#include <memory>
#include <mutex>
#include <numeric>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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/time/time.h"
#include "absl/types/optional.h"
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpcpp/channel.h>
#include <grpcpp/client_context.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/tls_certificate_provider.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/xds_server_builder.h>
#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/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/config/config_vars.h"
#include "src/core/lib/config/core_configuration.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/crash.h"
#include "src/core/lib/gprpp/env.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/certificate_provider/certificate_provider_registry.h"
#include "src/core/lib/security/credentials/fake/fake_credentials.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.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/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/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_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<std::string /*cert_name */, CertData>;
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<grpc_tls_certificate_distributor>()),
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(absl::StrCat(
"No certificates available for cert_name \"", cert_name, "\""));
distributor_->SetErrorForCert(cert_name, error, error);
} else {
absl::optional<std::string> root_certificate;
absl::optional<grpc_core::PemKeyCertPairList> 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<grpc_tls_certificate_distributor> 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<const grpc_tls_certificate_provider*>(this), other);
}
grpc_core::RefCountedPtr<grpc_tls_certificate_distributor> distributor_;
CertDataMap cert_data_map_;
};
class FakeCertificateProviderFactory
: public grpc_core::CertificateProviderFactory {
public:
class Config : public grpc_core::CertificateProviderFactory::Config {
public:
explicit Config(absl::string_view name) : name_(name) {}
absl::string_view name() const override { return name_; }
std::string ToString() const override { return "{}"; }
private:
absl::string_view name_;
};
FakeCertificateProviderFactory(
absl::string_view name,
FakeCertificateProvider::CertDataMapWrapper* cert_data_map)
: name_(name), cert_data_map_(cert_data_map) {
GPR_ASSERT(cert_data_map != nullptr);
}
absl::string_view name() const override { return name_; }
grpc_core::RefCountedPtr<grpc_core::CertificateProviderFactory::Config>
CreateCertificateProviderConfig(
const grpc_core::Json& /*config_json*/,
const grpc_core::JsonArgs& /*args*/,
grpc_core::ValidationErrors* /*errors*/) override {
return grpc_core::MakeRefCounted<Config>(name_);
}
grpc_core::RefCountedPtr<grpc_tls_certificate_provider>
CreateCertificateProvider(
grpc_core::RefCountedPtr<grpc_core::CertificateProviderFactory::Config>
/*config*/) override {
GPR_ASSERT(cert_data_map_ != nullptr);
return grpc_core::MakeRefCounted<FakeCertificateProvider>(
cert_data_map_->Get());
}
private:
absl::string_view 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<std::string> 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(2);
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<StringMatcher>& san_matchers,
const std::vector<std::string>& expected_authenticated_identity,
bool test_expects_failure = false) {
// Change the backend and use a unique service name to use so that we know
// that the CDS update was applied.
std::string service_name = absl::StrCat(
"eds_service_name",
absl::FormatTime("%H%M%E3S", absl::Now(), absl::LocalTimeZone()));
backend_index_ = (backend_index_ + 1) % 2;
EdsResourceArgs args({
{"locality0",
CreateEndpointsForBackends(backend_index_, backend_index_ + 1)},
});
balancer_->ads_service()->SetEdsResource(
BuildEdsResource(args, service_name.c_str()));
auto cluster = default_cluster_;
cluster.mutable_eds_cluster_config()->set_service_name(service_name);
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.
if (test_expects_failure) {
SendRpcsUntil(
DEBUG_LOCATION,
[&](const RpcResult& result) {
if (result.status.ok()) {
gpr_log(GPR_ERROR,
"RPC succeeded. Failure expected. Trying again.");
return true;
}
EXPECT_EQ(result.status.error_code(), StatusCode::UNAVAILABLE);
// TODO(yashkt): Change individual test cases to expect the exact
// error message here.
return false;
},
/* timeout_ms= */ 20 * 1000, RpcOptions().set_timeout_ms(5000));
} else {
backends_[backend_index_]->backend_service()->ResetCounters();
SendRpcsUntil(
DEBUG_LOCATION,
[&](const RpcResult& result) {
// Make sure that we are hitting the correct backend.
// TODO(yashykt): Even if we haven't moved to the correct backend
// and are still using the previous update, we should still check
// for the status and make sure that it fits our expectations.
if (backends_[backend_index_]->backend_service()->request_count() ==
0) {
return true;
}
EXPECT_TRUE(result.status.ok())
<< "code=" << result.status.error_code()
<< " message=" << result.status.error_message();
// Check that the identity is as expected.
EXPECT_EQ(backends_[backend_index_]
->backend_service()
->last_peer_identity(),
expected_authenticated_identity);
return false;
},
/* timeout_ms= */ 20 * 1000, RpcOptions().set_timeout_ms(5000));
}
}
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<std::string> authenticated_identity_;
std::vector<std::string> fallback_authenticated_identity_;
int backend_index_ = 0;
};
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);
CheckRpcSendOk(DEBUG_LOCATION, 1, RpcOptions().set_timeout_ms(5000));
}
// 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);
CheckRpcSendOk(DEBUG_LOCATION, 1, RpcOptions().set_timeout_ms(5000));
}
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);
}
// Testing just one example of an invalid resource here.
// Unit tests for XdsListenerResourceType have exhaustive tests for all
// of the invalid cases.
TEST_P(XdsEnabledServerTest, BadLdsUpdateNoApiListenerNorAddress) {
DoSetUp();
Listener listener = default_server_listener_;
listener.clear_address();
listener.set_name(GetServerListenerName(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::EndsWith(absl::StrCat(
GetServerListenerName(backends_[0]->port()),
": INVALID_ARGUMENT: Listener has neither address nor "
"ApiListener]")));
}
// Verify that a non-TCP listener results in "not serving" status.
TEST_P(XdsEnabledServerTest, NonTcpListener) {
DoSetUp();
Listener listener = default_listener_; // Client-side listener.
listener = PopulateServerListenerNameAndPort(listener, backends_[0]->port());
auto hcm = ClientHcmAccessor().Unpack(listener);
auto* rds = hcm.mutable_rds();
rds->set_route_config_name(kDefaultRouteConfigurationName);
rds->mutable_config_source()->mutable_self();
ClientHcmAccessor().Pack(hcm, &listener);
balancer_->ads_service()->SetLdsResource(listener);
backends_[0]->Start();
backends_[0]->notifier()->WaitOnServingStatusChange(
absl::StrCat(ipv6_only_ ? "[::1]:" : "127.0.0.1:", backends_[0]->port()),
grpc::StatusCode::FAILED_PRECONDITION);
}
// 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);
}
class XdsServerSecurityTest : public XdsEnd2endTest {
protected:
void SetUp() override {
BootstrapBuilder builder = BootstrapBuilder();
builder.AddCertificateProviderPlugin("fake_plugin1", "fake1");
builder.AddCertificateProviderPlugin("fake_plugin2", "fake2");
std::vector<std::string> 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<grpc::Channel> 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<IdentityKeyCertPair> identity_key_cert_pairs;
identity_key_cert_pairs.emplace_back(key_cert_pair);
auto certificate_provider = std::make_shared<StaticDataCertificateProvider>(
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<SyncCertificateVerifier>(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<grpc::Channel> 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<StaticDataCertificateProvider>(ReadFile(kCaCertPath));
grpc::experimental::TlsChannelCredentialsOptions options;
options.set_certificate_provider(std::move(certificate_provider));
options.watch_root_certs();
auto verifier =
ExternalCertificateVerifier::Create<SyncCertificateVerifier>(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<grpc::Channel> 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<std::shared_ptr<grpc::Channel>()> channel_creator,
std::vector<std::string> expected_server_identity,
std::vector<std::string> expected_client_identity,
bool test_expects_failure = false,
absl::optional<grpc::StatusCode> 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<std::string> 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<std::string> server_authenticated_identity_;
std::vector<std::string> server_authenticated_identity_2_;
std::vector<std::string> client_authenticated_identity_;
};
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> channel;
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub;
ClientContext context;
std::unique_ptr<ClientReaderWriter<EchoRequest, EchoResponse>> 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> channel;
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub;
ClientContext context;
std::unique_ptr<ClientReaderWriter<EchoRequest, EchoResponse>> 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();
}
}
class XdsServerFilterChainMatchTest : public XdsServerSecurityTest {
public:
HttpConnectionManager GetHttpConnectionManager(const Listener& listener) {
HttpConnectionManager http_connection_manager =
ServerHcmAccessor().Unpack(listener);
*http_connection_manager.mutable_route_config() =
default_server_route_config_;
return http_connection_manager;
}
};
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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(
GetHttpConnectionManager(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(); }, {}, {});
}
using XdsServerRdsTest = XdsEnabledServerStatusNotificationTest;
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, 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>& 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(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(); }, {}, {});
}
// 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.
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.
grpc_core::ConfigVars::Overrides overrides;
overrides.client_channel_backup_poll_interval_ms = 1;
grpc_core::ConfigVars::SetOverrides(overrides);
#if TARGET_OS_IPHONE
// Workaround Apple CFStream bug
grpc_core::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::testing::FakeCertificateProvider::CertDataMapWrapper cert_data_map_2;
grpc::testing::g_fake2_cert_data_map = &cert_data_map_2;
grpc_core::CoreConfiguration::RegisterBuilder(
[](grpc_core::CoreConfiguration::Builder* builder) {
builder->certificate_provider_registry()
->RegisterCertificateProviderFactory(
std::make_unique<grpc::testing::FakeCertificateProviderFactory>(
"fake1", grpc::testing::g_fake1_cert_data_map));
builder->certificate_provider_registry()
->RegisterCertificateProviderFactory(
std::make_unique<grpc::testing::FakeCertificateProviderFactory>(
"fake2", grpc::testing::g_fake2_cert_data_map));
});
grpc_init();
const auto result = RUN_ALL_TESTS();
grpc_shutdown();
return result;
}