xDS resource validation: identify extensions by type_url instead of name (#31024)

* xDS resource validation: identify extensions by type_url instead of name

* fix build
pull/31049/head^2
Mark D. Roth 3 years ago committed by GitHub
parent 22df3d9089
commit 6d792b6bce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 128
      src/core/ext/xds/xds_cluster.cc
  2. 30
      src/core/ext/xds/xds_listener.cc
  3. 47
      test/cpp/end2end/xds/xds_end2end_test.cc

@ -26,6 +26,7 @@
#include "absl/status/status.h" #include "absl/status/status.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include "absl/strings/strip.h"
#include "envoy/config/cluster/v3/circuit_breaker.upb.h" #include "envoy/config/cluster/v3/circuit_breaker.upb.h"
#include "envoy/config/cluster/v3/cluster.upb.h" #include "envoy/config/cluster/v3/cluster.upb.h"
#include "envoy/config/cluster/v3/cluster.upbdefs.h" #include "envoy/config/cluster/v3/cluster.upbdefs.h"
@ -104,39 +105,41 @@ namespace {
absl::StatusOr<CommonTlsContext> UpstreamTlsContextParse( absl::StatusOr<CommonTlsContext> UpstreamTlsContextParse(
const XdsResourceType::DecodeContext& context, const XdsResourceType::DecodeContext& context,
const envoy_config_core_v3_TransportSocket* transport_socket) { const envoy_config_core_v3_TransportSocket* transport_socket) {
CommonTlsContext common_tls_context;
// Record Upstream tls context
absl::string_view name = UpbStringToAbsl(
envoy_config_core_v3_TransportSocket_name(transport_socket));
if (name != "envoy.transport_sockets.tls") {
return absl::InvalidArgumentError(
absl::StrCat("Unrecognized transport socket: ", name));
}
auto* typed_config = auto* typed_config =
envoy_config_core_v3_TransportSocket_typed_config(transport_socket); envoy_config_core_v3_TransportSocket_typed_config(transport_socket);
if (typed_config != nullptr) { if (typed_config == nullptr) {
const upb_StringView encoded_upstream_tls_context = return absl::InvalidArgumentError("transport_socket.typed_config not set");
google_protobuf_Any_value(typed_config); }
auto* upstream_tls_context = absl::string_view type_url = absl::StripPrefix(
envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse( UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
encoded_upstream_tls_context.data, "type.googleapis.com/");
encoded_upstream_tls_context.size, context.arena); if (type_url !=
if (upstream_tls_context == nullptr) { "envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext") {
return absl::InvalidArgumentError("Can't decode upstream tls context."); return absl::InvalidArgumentError(
} absl::StrCat("Unrecognized transport socket type: ", type_url));
auto* common_tls_context_proto = }
envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context( const upb_StringView encoded_upstream_tls_context =
upstream_tls_context); google_protobuf_Any_value(typed_config);
if (common_tls_context_proto != nullptr) { auto* upstream_tls_context =
auto common_context = envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse(
CommonTlsContext::Parse(context, common_tls_context_proto); encoded_upstream_tls_context.data, encoded_upstream_tls_context.size,
if (!common_context.ok()) { context.arena);
return absl::InvalidArgumentError( if (upstream_tls_context == nullptr) {
absl::StrCat("Error parsing UpstreamTlsContext: ", return absl::InvalidArgumentError("Can't decode upstream tls context.");
common_context.status().message())); }
} auto* common_tls_context_proto =
common_tls_context = std::move(*common_context); envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context(
upstream_tls_context);
CommonTlsContext common_tls_context;
if (common_tls_context_proto != nullptr) {
auto common_context =
CommonTlsContext::Parse(context, common_tls_context_proto);
if (!common_context.ok()) {
return absl::InvalidArgumentError(
absl::StrCat("Error parsing UpstreamTlsContext: ",
common_context.status().message()));
} }
common_tls_context = std::move(*common_context);
} }
if (common_tls_context.certificate_validation_context if (common_tls_context.certificate_validation_context
.ca_certificate_provider_instance.instance_name.empty()) { .ca_certificate_provider_instance.instance_name.empty()) {
@ -243,42 +246,45 @@ absl::StatusOr<XdsClusterResource> CdsResourceParse(
absl::Status status = CdsLogicalDnsParse(cluster, &cds_update); absl::Status status = CdsLogicalDnsParse(cluster, &cds_update);
if (!status.ok()) errors.emplace_back(status.message()); if (!status.ok()) errors.emplace_back(status.message());
} else { } else {
if (!envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) { const auto* custom_cluster_type =
envoy_config_cluster_v3_Cluster_cluster_type(cluster);
if (custom_cluster_type == nullptr) {
errors.push_back("DiscoveryType is not valid."); errors.push_back("DiscoveryType is not valid.");
} else { } else {
const envoy_config_cluster_v3_Cluster_CustomClusterType* const auto* typed_config =
custom_cluster_type = envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config(
envoy_config_cluster_v3_Cluster_cluster_type(cluster);
upb_StringView type_name =
envoy_config_cluster_v3_Cluster_CustomClusterType_name(
custom_cluster_type); custom_cluster_type);
if (UpbStringToAbsl(type_name) != "envoy.clusters.aggregate") { if (typed_config == nullptr) {
errors.emplace_back("DiscoveryType is not valid."); errors.push_back("cluster_type.typed_config not set");
} else { } else {
cds_update.cluster_type = XdsClusterResource::ClusterType::AGGREGATE; absl::string_view type_url = absl::StripPrefix(
// Retrieve aggregate clusters. UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
const google_protobuf_Any* typed_config = "type.googleapis.com/");
envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config( if (type_url !=
custom_cluster_type); "envoy.extensions.clusters.aggregate.v3.ClusterConfig") {
const upb_StringView aggregate_cluster_config_upb_stringview = errors.push_back(
google_protobuf_Any_value(typed_config); absl::StrCat("unknown cluster_type extension: ", type_url));
const envoy_extensions_clusters_aggregate_v3_ClusterConfig*
aggregate_cluster_config =
envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse(
aggregate_cluster_config_upb_stringview.data,
aggregate_cluster_config_upb_stringview.size,
context.arena);
if (aggregate_cluster_config == nullptr) {
errors.emplace_back("Can't parse aggregate cluster.");
} else { } else {
size_t size; cds_update.cluster_type = XdsClusterResource::ClusterType::AGGREGATE;
const upb_StringView* clusters = // Retrieve aggregate clusters.
envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters( const upb_StringView aggregate_cluster_config_upb_stringview =
aggregate_cluster_config, &size); google_protobuf_Any_value(typed_config);
for (size_t i = 0; i < size; ++i) { const auto* aggregate_cluster_config =
const upb_StringView cluster = clusters[i]; envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse(
cds_update.prioritized_cluster_names.emplace_back( aggregate_cluster_config_upb_stringview.data,
UpbStringToStdString(cluster)); aggregate_cluster_config_upb_stringview.size, context.arena);
if (aggregate_cluster_config == nullptr) {
errors.emplace_back("Can't parse aggregate cluster.");
} else {
size_t size;
const upb_StringView* clusters =
envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters(
aggregate_cluster_config, &size);
for (size_t i = 0; i < size; ++i) {
const upb_StringView cluster = clusters[i];
cds_update.prioritized_cluster_names.emplace_back(
UpbStringToStdString(cluster));
}
} }
} }
} }

@ -28,6 +28,7 @@
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h" #include "absl/strings/str_format.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include "absl/strings/strip.h"
#include "envoy/config/core/v3/address.upb.h" #include "envoy/config/core/v3/address.upb.h"
#include "envoy/config/core/v3/base.upb.h" #include "envoy/config/core/v3/base.upb.h"
#include "envoy/config/core/v3/config_source.upb.h" #include "envoy/config/core/v3/config_source.upb.h"
@ -505,28 +506,30 @@ absl::StatusOr<XdsListenerResource::DownstreamTlsContext>
DownstreamTlsContextParse( DownstreamTlsContextParse(
const XdsResourceType::DecodeContext& context, const XdsResourceType::DecodeContext& context,
const envoy_config_core_v3_TransportSocket* transport_socket) { const envoy_config_core_v3_TransportSocket* transport_socket) {
absl::string_view name = UpbStringToAbsl( const auto* typed_config =
envoy_config_core_v3_TransportSocket_name(transport_socket));
if (name != "envoy.transport_sockets.tls") {
return absl::InvalidArgumentError(
absl::StrCat("Unrecognized transport socket: ", name));
}
std::vector<std::string> errors;
XdsListenerResource::DownstreamTlsContext downstream_tls_context;
auto* typed_config =
envoy_config_core_v3_TransportSocket_typed_config(transport_socket); envoy_config_core_v3_TransportSocket_typed_config(transport_socket);
if (typed_config == nullptr) { if (typed_config == nullptr) {
return absl::InvalidArgumentError("transport socket typed config unset"); return absl::InvalidArgumentError("transport socket typed config unset");
} }
absl::string_view type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
"type.googleapis.com/");
if (type_url !=
"envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext") {
return absl::InvalidArgumentError(
absl::StrCat("Unrecognized transport socket type: ", type_url));
}
const upb_StringView encoded_downstream_tls_context = const upb_StringView encoded_downstream_tls_context =
google_protobuf_Any_value(typed_config); google_protobuf_Any_value(typed_config);
auto* downstream_tls_context_proto = const auto* downstream_tls_context_proto =
envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse( envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse(
encoded_downstream_tls_context.data, encoded_downstream_tls_context.data,
encoded_downstream_tls_context.size, context.arena); encoded_downstream_tls_context.size, context.arena);
if (downstream_tls_context_proto == nullptr) { if (downstream_tls_context_proto == nullptr) {
return absl::InvalidArgumentError("Can't decode downstream tls context."); return absl::InvalidArgumentError("Can't decode downstream tls context.");
} }
std::vector<std::string> errors;
XdsListenerResource::DownstreamTlsContext downstream_tls_context;
auto* common_tls_context = auto* common_tls_context =
envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context( envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context(
downstream_tls_context_proto); downstream_tls_context_proto);
@ -556,7 +559,6 @@ DownstreamTlsContextParse(
envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_LENIENT_STAPLING) { envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_LENIENT_STAPLING) {
errors.emplace_back("ocsp_staple_policy: Only LENIENT_STAPLING supported"); errors.emplace_back("ocsp_staple_policy: Only LENIENT_STAPLING supported");
} }
if (downstream_tls_context.common_tls_context if (downstream_tls_context.common_tls_context
.tls_certificate_provider_instance.instance_name.empty()) { .tls_certificate_provider_instance.instance_name.empty()) {
errors.emplace_back( errors.emplace_back(
@ -712,10 +714,10 @@ absl::StatusOr<FilterChain> FilterChainParse(
if (typed_config == nullptr) { if (typed_config == nullptr) {
errors.emplace_back("No typed_config found in filter."); errors.emplace_back("No typed_config found in filter.");
} else { } else {
absl::string_view type_url = absl::string_view type_url = absl::StripPrefix(
UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)); UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)),
"type.googleapis.com/");
if (type_url != if (type_url !=
"type.googleapis.com/"
"envoy.extensions.filters.network.http_connection_manager.v3." "envoy.extensions.filters.network.http_connection_manager.v3."
"HttpConnectionManager") { "HttpConnectionManager") {
errors.emplace_back(absl::StrCat("Unsupported filter type ", type_url)); errors.emplace_back(absl::StrCat("Unsupported filter type ", type_url));

@ -431,24 +431,41 @@ class XdsSecurityTest : public XdsEnd2endTest {
int backend_index_ = 0; int backend_index_ = 0;
}; };
TEST_P(XdsSecurityTest, TransportSocketMissingTypedConfig) {
auto cluster = default_cluster_;
cluster.mutable_transport_socket();
balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_EQ(response_state->error_message,
"xDS response validation errors: [resource index 0: cluster_name: "
"INVALID_ARGUMENT: errors parsing CDS resource: ["
"Error parsing security configuration: "
"transport_socket.typed_config not set]]");
}
TEST_P(XdsSecurityTest, UnknownTransportSocket) { TEST_P(XdsSecurityTest, UnknownTransportSocket) {
auto cluster = default_cluster_; auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket(); auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("unknown_transport_socket"); transport_socket->mutable_typed_config()->PackFrom(Listener());
balancer_->ads_service()->SetCdsResource(cluster); balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000)); WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message, EXPECT_EQ(response_state->error_message,
::testing::HasSubstr( "xDS response validation errors: [resource index 0: cluster_name: "
"Unrecognized transport socket: unknown_transport_socket")); "INVALID_ARGUMENT: errors parsing CDS resource: ["
"Error parsing security configuration: "
"Unrecognized transport socket type: "
"envoy.config.listener.v3.Listener]]");
} }
TEST_P(XdsSecurityTest, TEST_P(XdsSecurityTest,
TLSConfigurationWithoutValidationContextCertificateProviderInstance) { TLSConfigurationWithoutValidationContextCertificateProviderInstance) {
auto cluster = default_cluster_; auto cluster = default_cluster_;
auto* transport_socket = cluster.mutable_transport_socket(); auto* transport_socket = cluster.mutable_transport_socket();
transport_socket->set_name("envoy.transport_sockets.tls"); transport_socket->mutable_typed_config()->PackFrom(UpstreamTlsContext());
balancer_->ads_service()->SetCdsResource(cluster); balancer_->ads_service()->SetCdsResource(cluster);
const auto response_state = const auto response_state =
WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000)); WaitForCdsNack(DEBUG_LOCATION, RpcOptions().set_timeout_ms(5000));
@ -1571,11 +1588,25 @@ class XdsServerSecurityTest : public XdsEnd2endTest {
std::vector<std::string> client_authenticated_identity_; std::vector<std::string> client_authenticated_identity_;
}; };
TEST_P(XdsServerSecurityTest, TransportSocketTypedConfigUnset) {
Listener listener = default_server_listener_;
auto* filter_chain = listener.mutable_default_filter_chain();
filter_chain->mutable_transport_socket();
SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener,
backends_[0]->port(),
default_server_route_config_);
backends_[0]->Start();
const auto response_state = WaitForLdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message,
::testing::HasSubstr("transport socket typed config unset"));
}
TEST_P(XdsServerSecurityTest, UnknownTransportSocket) { TEST_P(XdsServerSecurityTest, UnknownTransportSocket) {
Listener listener = default_server_listener_; Listener listener = default_server_listener_;
auto* filter_chain = listener.mutable_default_filter_chain(); auto* filter_chain = listener.mutable_default_filter_chain();
auto* transport_socket = filter_chain->mutable_transport_socket(); auto* transport_socket = filter_chain->mutable_transport_socket();
transport_socket->set_name("unknown_transport_socket"); transport_socket->mutable_typed_config()->PackFrom(Listener());
SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener, SetServerListenerNameAndRouteConfiguration(balancer_.get(), listener,
backends_[0]->port(), backends_[0]->port(),
default_server_route_config_); default_server_route_config_);
@ -1583,8 +1614,8 @@ TEST_P(XdsServerSecurityTest, UnknownTransportSocket) {
const auto response_state = WaitForLdsNack(DEBUG_LOCATION); const auto response_state = WaitForLdsNack(DEBUG_LOCATION);
ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK"; ASSERT_TRUE(response_state.has_value()) << "timed out waiting for NACK";
EXPECT_THAT(response_state->error_message, EXPECT_THAT(response_state->error_message,
::testing::HasSubstr( ::testing::HasSubstr("Unrecognized transport socket type: "
"Unrecognized transport socket: unknown_transport_socket")); "envoy.config.listener.v3.Listener"));
} }
TEST_P(XdsServerSecurityTest, NacksRequireSNI) { TEST_P(XdsServerSecurityTest, NacksRequireSNI) {

Loading…
Cancel
Save