google.golang.org/grpc@v1.72.2/xds/internal/xdsclient/xdsresource/unmarshal_cds.go (about)

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package xdsresource
    19  
    20  import (
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"net"
    25  	"strconv"
    26  	"strings"
    27  	"time"
    28  
    29  	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    30  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    31  	v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3"
    32  	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    33  
    34  	"google.golang.org/grpc/internal/envconfig"
    35  	"google.golang.org/grpc/internal/pretty"
    36  	iserviceconfig "google.golang.org/grpc/internal/serviceconfig"
    37  	"google.golang.org/grpc/internal/xds/bootstrap"
    38  	"google.golang.org/grpc/internal/xds/matcher"
    39  	"google.golang.org/grpc/xds/internal/xdsclient/xdslbregistry"
    40  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
    41  	"google.golang.org/protobuf/proto"
    42  	"google.golang.org/protobuf/types/known/anypb"
    43  	"google.golang.org/protobuf/types/known/structpb"
    44  )
    45  
    46  // ValidateClusterAndConstructClusterUpdateForTesting exports the
    47  // validateClusterAndConstructClusterUpdate function for testing purposes.
    48  var ValidateClusterAndConstructClusterUpdateForTesting = validateClusterAndConstructClusterUpdate
    49  
    50  // TransportSocket proto message has a `name` field which is expected to be set
    51  // to this value by the management server.
    52  const transportSocketName = "envoy.transport_sockets.tls"
    53  
    54  func unmarshalClusterResource(r *anypb.Any, serverCfg *bootstrap.ServerConfig) (string, ClusterUpdate, error) {
    55  	r, err := UnwrapResource(r)
    56  	if err != nil {
    57  		return "", ClusterUpdate{}, fmt.Errorf("failed to unwrap resource: %v", err)
    58  	}
    59  
    60  	if !IsClusterResource(r.GetTypeUrl()) {
    61  		return "", ClusterUpdate{}, fmt.Errorf("unexpected resource type: %q ", r.GetTypeUrl())
    62  	}
    63  
    64  	cluster := &v3clusterpb.Cluster{}
    65  	if err := proto.Unmarshal(r.GetValue(), cluster); err != nil {
    66  		return "", ClusterUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err)
    67  	}
    68  	cu, err := validateClusterAndConstructClusterUpdate(cluster, serverCfg)
    69  	if err != nil {
    70  		return cluster.GetName(), ClusterUpdate{}, err
    71  	}
    72  	cu.Raw = r
    73  
    74  	return cluster.GetName(), cu, nil
    75  }
    76  
    77  const (
    78  	defaultRingHashMinSize = 1024
    79  	defaultRingHashMaxSize = 8 * 1024 * 1024 // 8M
    80  	ringHashSizeUpperBound = 8 * 1024 * 1024 // 8M
    81  
    82  	defaultLeastRequestChoiceCount = 2
    83  )
    84  
    85  func validateClusterAndConstructClusterUpdate(cluster *v3clusterpb.Cluster, serverCfg *bootstrap.ServerConfig) (ClusterUpdate, error) {
    86  	telemetryLabels := make(map[string]string)
    87  	if fmd := cluster.GetMetadata().GetFilterMetadata(); fmd != nil {
    88  		if val, ok := fmd["com.google.csm.telemetry_labels"]; ok {
    89  			if fields := val.GetFields(); fields != nil {
    90  				if val, ok := fields["service_name"]; ok {
    91  					if _, ok := val.GetKind().(*structpb.Value_StringValue); ok {
    92  						telemetryLabels["csm.service_name"] = val.GetStringValue()
    93  					}
    94  				}
    95  				if val, ok := fields["service_namespace"]; ok {
    96  					if _, ok := val.GetKind().(*structpb.Value_StringValue); ok {
    97  						telemetryLabels["csm.service_namespace_name"] = val.GetStringValue()
    98  					}
    99  				}
   100  			}
   101  		}
   102  	}
   103  	// "The values for the service labels csm.service_name and
   104  	// csm.service_namespace_name come from xDS, “unknown” if not present." -
   105  	// CSM Design.
   106  	if _, ok := telemetryLabels["csm.service_name"]; !ok {
   107  		telemetryLabels["csm.service_name"] = "unknown"
   108  	}
   109  	if _, ok := telemetryLabels["csm.service_namespace_name"]; !ok {
   110  		telemetryLabels["csm.service_namespace_name"] = "unknown"
   111  	}
   112  
   113  	var lbPolicy json.RawMessage
   114  	var err error
   115  	switch cluster.GetLbPolicy() {
   116  	case v3clusterpb.Cluster_ROUND_ROBIN:
   117  		lbPolicy = []byte(`[{"xds_wrr_locality_experimental": {"childPolicy": [{"round_robin": {}}]}}]`)
   118  	case v3clusterpb.Cluster_RING_HASH:
   119  		rhc := cluster.GetRingHashLbConfig()
   120  		if rhc.GetHashFunction() != v3clusterpb.Cluster_RingHashLbConfig_XX_HASH {
   121  			return ClusterUpdate{}, fmt.Errorf("unsupported ring_hash hash function %v in response: %+v", rhc.GetHashFunction(), cluster)
   122  		}
   123  		// Minimum defaults to 1024 entries, and limited to 8M entries Maximum
   124  		// defaults to 8M entries, and limited to 8M entries
   125  		var minSize, maxSize uint64 = defaultRingHashMinSize, defaultRingHashMaxSize
   126  		if min := rhc.GetMinimumRingSize(); min != nil {
   127  			minSize = min.GetValue()
   128  		}
   129  		if max := rhc.GetMaximumRingSize(); max != nil {
   130  			maxSize = max.GetValue()
   131  		}
   132  
   133  		rhLBCfg := []byte(fmt.Sprintf("{\"minRingSize\": %d, \"maxRingSize\": %d}", minSize, maxSize))
   134  		lbPolicy = []byte(fmt.Sprintf(`[{"ring_hash_experimental": %s}]`, rhLBCfg))
   135  	case v3clusterpb.Cluster_LEAST_REQUEST:
   136  		if !envconfig.LeastRequestLB {
   137  			return ClusterUpdate{}, fmt.Errorf("unexpected lbPolicy %v in response: %+v", cluster.GetLbPolicy(), cluster)
   138  		}
   139  
   140  		// "The configuration for the Least Request LB policy is the
   141  		// least_request_lb_config field. The field is optional; if not present,
   142  		// defaults will be assumed for all of its values." - A48
   143  		lr := cluster.GetLeastRequestLbConfig()
   144  		var choiceCount uint32 = defaultLeastRequestChoiceCount
   145  		if cc := lr.GetChoiceCount(); cc != nil {
   146  			choiceCount = cc.GetValue()
   147  		}
   148  		// "If choice_count < 2, the config will be rejected." - A48
   149  		if choiceCount < 2 {
   150  			return ClusterUpdate{}, fmt.Errorf("Cluster_LeastRequestLbConfig.ChoiceCount must be >= 2, got: %v", choiceCount)
   151  		}
   152  
   153  		lrLBCfg := []byte(fmt.Sprintf("{\"choiceCount\": %d}", choiceCount))
   154  		lbPolicy = []byte(fmt.Sprintf(`[{"least_request_experimental": %s}]`, lrLBCfg))
   155  	default:
   156  		return ClusterUpdate{}, fmt.Errorf("unexpected lbPolicy %v in response: %+v", cluster.GetLbPolicy(), cluster)
   157  	}
   158  	// Process security configuration received from the control plane iff the
   159  	// corresponding environment variable is set.
   160  	var sc *SecurityConfig
   161  	if sc, err = securityConfigFromCluster(cluster); err != nil {
   162  		return ClusterUpdate{}, err
   163  	}
   164  
   165  	// Process outlier detection received from the control plane iff the
   166  	// corresponding environment variable is set.
   167  	var od json.RawMessage
   168  	if od, err = outlierConfigFromCluster(cluster); err != nil {
   169  		return ClusterUpdate{}, err
   170  	}
   171  
   172  	if cluster.GetLoadBalancingPolicy() != nil {
   173  		lbPolicy, err = xdslbregistry.ConvertToServiceConfig(cluster.GetLoadBalancingPolicy(), 0)
   174  		if err != nil {
   175  			return ClusterUpdate{}, fmt.Errorf("error converting LoadBalancingPolicy %v in response: %+v: %v", cluster.GetLoadBalancingPolicy(), cluster, err)
   176  		}
   177  		// "It will be the responsibility of the XdsClient to validate the
   178  		// converted configuration. It will do this by having the gRPC LB policy
   179  		// registry parse the configuration." - A52
   180  		bc := &iserviceconfig.BalancerConfig{}
   181  		if err := json.Unmarshal(lbPolicy, bc); err != nil {
   182  			return ClusterUpdate{}, fmt.Errorf("JSON generated from xDS LB policy registry: %s is invalid: %v", pretty.FormatJSON(lbPolicy), err)
   183  		}
   184  	}
   185  
   186  	ret := ClusterUpdate{
   187  		ClusterName:      cluster.GetName(),
   188  		SecurityCfg:      sc,
   189  		MaxRequests:      circuitBreakersFromCluster(cluster),
   190  		LBPolicy:         lbPolicy,
   191  		OutlierDetection: od,
   192  		TelemetryLabels:  telemetryLabels,
   193  	}
   194  
   195  	if lrs := cluster.GetLrsServer(); lrs != nil {
   196  		if lrs.GetSelf() == nil {
   197  			return ClusterUpdate{}, fmt.Errorf("unsupported config_source_specifier %T in lrs_server field", lrs.ConfigSourceSpecifier)
   198  		}
   199  		ret.LRSServerConfig = serverCfg
   200  	}
   201  
   202  	// Validate and set cluster type from the response.
   203  	switch {
   204  	case cluster.GetType() == v3clusterpb.Cluster_EDS:
   205  		if configsource := cluster.GetEdsClusterConfig().GetEdsConfig(); configsource.GetAds() == nil && configsource.GetSelf() == nil {
   206  			return ClusterUpdate{}, fmt.Errorf("CDS's EDS config source is not ADS or Self: %+v", cluster)
   207  		}
   208  		ret.ClusterType = ClusterTypeEDS
   209  		ret.EDSServiceName = cluster.GetEdsClusterConfig().GetServiceName()
   210  		if strings.HasPrefix(ret.ClusterName, "xdstp:") && ret.EDSServiceName == "" {
   211  			return ClusterUpdate{}, fmt.Errorf("CDS's EDS service name is not set with a new-style cluster name: %+v", cluster)
   212  		}
   213  		return ret, nil
   214  	case cluster.GetType() == v3clusterpb.Cluster_LOGICAL_DNS:
   215  		ret.ClusterType = ClusterTypeLogicalDNS
   216  		dnsHN, err := dnsHostNameFromCluster(cluster)
   217  		if err != nil {
   218  			return ClusterUpdate{}, err
   219  		}
   220  		ret.DNSHostName = dnsHN
   221  		return ret, nil
   222  	case cluster.GetClusterType() != nil && cluster.GetClusterType().Name == "envoy.clusters.aggregate":
   223  		clusters := &v3aggregateclusterpb.ClusterConfig{}
   224  		if err := proto.Unmarshal(cluster.GetClusterType().GetTypedConfig().GetValue(), clusters); err != nil {
   225  			return ClusterUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err)
   226  		}
   227  		if len(clusters.Clusters) == 0 {
   228  			return ClusterUpdate{}, fmt.Errorf("xds: aggregate cluster has empty clusters field in response: %+v", cluster)
   229  		}
   230  		ret.ClusterType = ClusterTypeAggregate
   231  		ret.PrioritizedClusterNames = clusters.Clusters
   232  		return ret, nil
   233  	default:
   234  		return ClusterUpdate{}, fmt.Errorf("unsupported cluster type (%v, %v) in response: %+v", cluster.GetType(), cluster.GetClusterType(), cluster)
   235  	}
   236  }
   237  
   238  // dnsHostNameFromCluster extracts the DNS host name from the cluster's load
   239  // assignment.
   240  //
   241  // There should be exactly one locality, with one endpoint, whose address
   242  // contains the address and port.
   243  func dnsHostNameFromCluster(cluster *v3clusterpb.Cluster) (string, error) {
   244  	loadAssignment := cluster.GetLoadAssignment()
   245  	if loadAssignment == nil {
   246  		return "", fmt.Errorf("load_assignment not present for LOGICAL_DNS cluster")
   247  	}
   248  	if len(loadAssignment.GetEndpoints()) != 1 {
   249  		return "", fmt.Errorf("load_assignment for LOGICAL_DNS cluster must have exactly one locality, got: %+v", loadAssignment)
   250  	}
   251  	endpoints := loadAssignment.GetEndpoints()[0].GetLbEndpoints()
   252  	if len(endpoints) != 1 {
   253  		return "", fmt.Errorf("locality for LOGICAL_DNS cluster must have exactly one endpoint, got: %+v", endpoints)
   254  	}
   255  	endpoint := endpoints[0].GetEndpoint()
   256  	if endpoint == nil {
   257  		return "", fmt.Errorf("endpoint for LOGICAL_DNS cluster not set")
   258  	}
   259  	socketAddr := endpoint.GetAddress().GetSocketAddress()
   260  	if socketAddr == nil {
   261  		return "", fmt.Errorf("socket address for endpoint for LOGICAL_DNS cluster not set")
   262  	}
   263  	if socketAddr.GetResolverName() != "" {
   264  		return "", fmt.Errorf("socket address for endpoint for LOGICAL_DNS cluster not set has unexpected custom resolver name: %v", socketAddr.GetResolverName())
   265  	}
   266  	host := socketAddr.GetAddress()
   267  	if host == "" {
   268  		return "", fmt.Errorf("host for endpoint for LOGICAL_DNS cluster not set")
   269  	}
   270  	port := socketAddr.GetPortValue()
   271  	if port == 0 {
   272  		return "", fmt.Errorf("port for endpoint for LOGICAL_DNS cluster not set")
   273  	}
   274  	return net.JoinHostPort(host, strconv.Itoa(int(port))), nil
   275  }
   276  
   277  // securityConfigFromCluster extracts the relevant security configuration from
   278  // the received Cluster resource.
   279  func securityConfigFromCluster(cluster *v3clusterpb.Cluster) (*SecurityConfig, error) {
   280  	if tsm := cluster.GetTransportSocketMatches(); len(tsm) != 0 {
   281  		return nil, fmt.Errorf("unsupported transport_socket_matches field is non-empty: %+v", tsm)
   282  	}
   283  	// The Cluster resource contains a `transport_socket` field, which contains
   284  	// a oneof `typed_config` field of type `protobuf.Any`. The any proto
   285  	// contains a marshaled representation of an `UpstreamTlsContext` message.
   286  	ts := cluster.GetTransportSocket()
   287  	if ts == nil {
   288  		return nil, nil
   289  	}
   290  	if name := ts.GetName(); name != transportSocketName {
   291  		return nil, fmt.Errorf("transport_socket field has unexpected name: %s", name)
   292  	}
   293  	tc := ts.GetTypedConfig()
   294  	if tc == nil || tc.TypeUrl != version.V3UpstreamTLSContextURL {
   295  		return nil, fmt.Errorf("transport_socket field has unexpected typeURL: %s", tc.TypeUrl)
   296  	}
   297  	upstreamCtx := &v3tlspb.UpstreamTlsContext{}
   298  	if err := proto.Unmarshal(tc.GetValue(), upstreamCtx); err != nil {
   299  		return nil, fmt.Errorf("failed to unmarshal UpstreamTlsContext in CDS response: %v", err)
   300  	}
   301  	// The following fields from `UpstreamTlsContext` are ignored:
   302  	// - sni
   303  	// - allow_renegotiation
   304  	// - max_session_keys
   305  	if upstreamCtx.GetCommonTlsContext() == nil {
   306  		return nil, errors.New("UpstreamTlsContext in CDS response does not contain a CommonTlsContext")
   307  	}
   308  
   309  	return securityConfigFromCommonTLSContext(upstreamCtx.GetCommonTlsContext(), false)
   310  }
   311  
   312  // common is expected to be not nil.
   313  // The `alpn_protocols` field is ignored.
   314  func securityConfigFromCommonTLSContext(common *v3tlspb.CommonTlsContext, server bool) (*SecurityConfig, error) {
   315  	if common.GetTlsParams() != nil {
   316  		return nil, fmt.Errorf("unsupported tls_params field in CommonTlsContext message: %+v", common)
   317  	}
   318  	if common.GetCustomHandshaker() != nil {
   319  		return nil, fmt.Errorf("unsupported custom_handshaker field in CommonTlsContext message: %+v", common)
   320  	}
   321  
   322  	// For now, if we can't get a valid security config from the new fields, we
   323  	// fallback to the old deprecated fields.
   324  	// TODO: Drop support for deprecated fields. NACK if err != nil here.
   325  	sc, err1 := securityConfigFromCommonTLSContextUsingNewFields(common, server)
   326  	if sc == nil || sc.Equal(&SecurityConfig{}) {
   327  		var err error
   328  		sc, err = securityConfigFromCommonTLSContextWithDeprecatedFields(common, server)
   329  		if err != nil {
   330  			// Retain the validation error from using the new fields.
   331  			return nil, errors.Join(err1, fmt.Errorf("failed to parse config using deprecated fields: %v", err))
   332  		}
   333  	}
   334  	if sc != nil {
   335  		// sc == nil is a valid case where the control plane has not sent us any
   336  		// security configuration. xDS creds will use fallback creds.
   337  		if server {
   338  			if sc.IdentityInstanceName == "" {
   339  				return nil, errors.New("security configuration on the server-side does not contain identity certificate provider instance name")
   340  			}
   341  		} else {
   342  			if !sc.UseSystemRootCerts && sc.RootInstanceName == "" {
   343  				return nil, errors.New("security configuration on the client-side does not contain root certificate provider instance name")
   344  			}
   345  		}
   346  	}
   347  	return sc, nil
   348  }
   349  
   350  func securityConfigFromCommonTLSContextWithDeprecatedFields(common *v3tlspb.CommonTlsContext, server bool) (*SecurityConfig, error) {
   351  	// The `CommonTlsContext` contains a
   352  	// `tls_certificate_certificate_provider_instance` field of type
   353  	// `CertificateProviderInstance`, which contains the provider instance name
   354  	// and the certificate name to fetch identity certs.
   355  	sc := &SecurityConfig{}
   356  	if identity := common.GetTlsCertificateCertificateProviderInstance(); identity != nil {
   357  		sc.IdentityInstanceName = identity.GetInstanceName()
   358  		sc.IdentityCertName = identity.GetCertificateName()
   359  	}
   360  
   361  	// The `CommonTlsContext` contains a `validation_context_type` field which
   362  	// is a oneof. We can get the values that we are interested in from two of
   363  	// those possible values:
   364  	//  - combined validation context:
   365  	//    - contains a default validation context which holds the list of
   366  	//      matchers for accepted SANs.
   367  	//    - contains certificate provider instance configuration
   368  	//  - certificate provider instance configuration
   369  	//    - in this case, we do not get a list of accepted SANs.
   370  	switch t := common.GetValidationContextType().(type) {
   371  	case *v3tlspb.CommonTlsContext_CombinedValidationContext:
   372  		combined := common.GetCombinedValidationContext()
   373  		var matchers []matcher.StringMatcher
   374  		if def := combined.GetDefaultValidationContext(); def != nil {
   375  			for _, m := range def.GetMatchSubjectAltNames() {
   376  				matcher, err := matcher.StringMatcherFromProto(m)
   377  				if err != nil {
   378  					return nil, err
   379  				}
   380  				matchers = append(matchers, matcher)
   381  			}
   382  		}
   383  		if server && len(matchers) != 0 {
   384  			return nil, fmt.Errorf("match_subject_alt_names field in validation context is not supported on the server: %v", common)
   385  		}
   386  		sc.SubjectAltNameMatchers = matchers
   387  		if pi := combined.GetValidationContextCertificateProviderInstance(); pi != nil {
   388  			sc.RootInstanceName = pi.GetInstanceName()
   389  			sc.RootCertName = pi.GetCertificateName()
   390  		}
   391  	case *v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance:
   392  		pi := common.GetValidationContextCertificateProviderInstance()
   393  		sc.RootInstanceName = pi.GetInstanceName()
   394  		sc.RootCertName = pi.GetCertificateName()
   395  	case nil:
   396  		// It is valid for the validation context to be nil on the server side.
   397  	default:
   398  		return nil, fmt.Errorf("validation context contains unexpected type: %T", t)
   399  	}
   400  	return sc, nil
   401  }
   402  
   403  // gRFC A29 https://github.com/grpc/proposal/blob/master/A29-xds-tls-security.md
   404  // specifies the new way to fetch security configuration and says the following:
   405  //
   406  // Although there are various ways to obtain certificates as per this proto
   407  // (which are supported by Envoy), gRPC supports only one of them and that is
   408  // the `CertificateProviderPluginInstance` proto.
   409  //
   410  // This helper function attempts to fetch security configuration from the
   411  // `CertificateProviderPluginInstance` message, given a CommonTlsContext.
   412  func securityConfigFromCommonTLSContextUsingNewFields(common *v3tlspb.CommonTlsContext, server bool) (*SecurityConfig, error) {
   413  	// The `tls_certificate_provider_instance` field of type
   414  	// `CertificateProviderPluginInstance` is used to fetch the identity
   415  	// certificate provider.
   416  	sc := &SecurityConfig{}
   417  	identity := common.GetTlsCertificateProviderInstance()
   418  	if identity == nil && len(common.GetTlsCertificates()) != 0 {
   419  		return nil, fmt.Errorf("expected field tls_certificate_provider_instance is not set, while unsupported field tls_certificates is set in CommonTlsContext message: %+v", common)
   420  	}
   421  	if identity == nil && common.GetTlsCertificateSdsSecretConfigs() != nil {
   422  		return nil, fmt.Errorf("expected field tls_certificate_provider_instance is not set, while unsupported field tls_certificate_sds_secret_configs is set in CommonTlsContext message: %+v", common)
   423  	}
   424  	sc.IdentityInstanceName = identity.GetInstanceName()
   425  	sc.IdentityCertName = identity.GetCertificateName()
   426  
   427  	// The `CommonTlsContext` contains a oneof field `validation_context_type`,
   428  	// which contains the `CertificateValidationContext` message in one of the
   429  	// following ways:
   430  	//  - `validation_context` field
   431  	//    - this is directly of type `CertificateValidationContext`
   432  	//  - `combined_validation_context` field
   433  	//    - this is of type `CombinedCertificateValidationContext` and contains
   434  	//      a `default validation context` field of type
   435  	//      `CertificateValidationContext`
   436  	//
   437  	// The `CertificateValidationContext` message has the following fields that
   438  	// we are interested in:
   439  	//  - `ca_certificate_provider_instance`
   440  	//    - this is of type `CertificateProviderPluginInstance`
   441  	//  - `system_root_certs`:
   442  	//    - This indicates the usage of system root certs for validation.
   443  	//  - `match_subject_alt_names`
   444  	//    - this is a list of string matchers
   445  	//
   446  	// The `CertificateProviderPluginInstance` message contains two fields
   447  	//  - instance_name
   448  	//    - this is the certificate provider instance name to be looked up in
   449  	//      the bootstrap configuration
   450  	//  - certificate_name
   451  	//    -  this is an opaque name passed to the certificate provider
   452  	var validationCtx *v3tlspb.CertificateValidationContext
   453  	switch typ := common.GetValidationContextType().(type) {
   454  	case *v3tlspb.CommonTlsContext_ValidationContext:
   455  		validationCtx = common.GetValidationContext()
   456  	case *v3tlspb.CommonTlsContext_CombinedValidationContext:
   457  		validationCtx = common.GetCombinedValidationContext().GetDefaultValidationContext()
   458  	case nil:
   459  		// It is valid for the validation context to be nil on the server side.
   460  		return sc, nil
   461  	default:
   462  		return nil, fmt.Errorf("validation context contains unexpected type: %T", typ)
   463  	}
   464  	// If we get here, it means that the `CertificateValidationContext` message
   465  	// was found through one of the supported ways. It is an error if the
   466  	// validation context is specified, but it does not specify a way to
   467  	// validate TLS certificates. Peer TLS certs can be verified in the
   468  	// following ways:
   469  	// 1. If the ca_certificate_provider_instance field is set, it contains
   470  	//    information about the certificate provider to be used for the root
   471  	//    certificates, else
   472  	// 2. If the system_root_certs field is set, and the config is for a client,
   473  	//    use the system default root certs.
   474  	useSystemRootCerts := false
   475  	if validationCtx.GetCaCertificateProviderInstance() == nil && envconfig.XDSSystemRootCertsEnabled {
   476  		if server {
   477  			if validationCtx.GetSystemRootCerts() != nil {
   478  				// The `system_root_certs` field will not be supported on the
   479  				// gRPC server side. If `ca_certificate_provider_instance` is
   480  				// unset and `system_root_certs` is set, the LDS resource will
   481  				// be NACKed.
   482  				// - A82
   483  				return nil, fmt.Errorf("expected field ca_certificate_provider_instance is missing and unexpected field system_root_certs is set for server in CommonTlsContext message: %+v", common)
   484  			}
   485  		} else {
   486  			if validationCtx.GetSystemRootCerts() != nil {
   487  				useSystemRootCerts = true
   488  			}
   489  		}
   490  	}
   491  
   492  	// The following fields are ignored:
   493  	// - trusted_ca
   494  	// - watched_directory
   495  	// - allow_expired_certificate
   496  	// - trust_chain_verification
   497  	switch {
   498  	case len(validationCtx.GetVerifyCertificateSpki()) != 0:
   499  		return nil, fmt.Errorf("unsupported verify_certificate_spki field in CommonTlsContext message: %+v", common)
   500  	case len(validationCtx.GetVerifyCertificateHash()) != 0:
   501  		return nil, fmt.Errorf("unsupported verify_certificate_hash field in CommonTlsContext message: %+v", common)
   502  	case validationCtx.GetRequireSignedCertificateTimestamp().GetValue():
   503  		return nil, fmt.Errorf("unsupported require_signed_certificate_timestamp field in CommonTlsContext message: %+v", common)
   504  	case validationCtx.GetCrl() != nil:
   505  		return nil, fmt.Errorf("unsupported crl field in CommonTlsContext message: %+v", common)
   506  	case validationCtx.GetCustomValidatorConfig() != nil:
   507  		return nil, fmt.Errorf("unsupported custom_validator_config field in CommonTlsContext message: %+v", common)
   508  	}
   509  
   510  	if rootProvider := validationCtx.GetCaCertificateProviderInstance(); rootProvider != nil {
   511  		sc.RootInstanceName = rootProvider.GetInstanceName()
   512  		sc.RootCertName = rootProvider.GetCertificateName()
   513  	} else if useSystemRootCerts {
   514  		sc.UseSystemRootCerts = true
   515  	} else if !server && envconfig.XDSSystemRootCertsEnabled {
   516  		return nil, fmt.Errorf("expected fields ca_certificate_provider_instance and system_root_certs are missing in CommonTlsContext message: %+v", common)
   517  	} else {
   518  		// Don't mention the system_root_certs field if it was not checked.
   519  		return nil, fmt.Errorf("expected field ca_certificate_provider_instance is missing in CommonTlsContext message: %+v", common)
   520  	}
   521  
   522  	var matchers []matcher.StringMatcher
   523  	for _, m := range validationCtx.GetMatchSubjectAltNames() {
   524  		matcher, err := matcher.StringMatcherFromProto(m)
   525  		if err != nil {
   526  			return nil, err
   527  		}
   528  		matchers = append(matchers, matcher)
   529  	}
   530  	if server && len(matchers) != 0 {
   531  		return nil, fmt.Errorf("match_subject_alt_names field in validation context is not supported on the server: %v", common)
   532  	}
   533  	sc.SubjectAltNameMatchers = matchers
   534  	return sc, nil
   535  }
   536  
   537  // circuitBreakersFromCluster extracts the circuit breakers configuration from
   538  // the received cluster resource. Returns nil if no CircuitBreakers or no
   539  // Thresholds in CircuitBreakers.
   540  func circuitBreakersFromCluster(cluster *v3clusterpb.Cluster) *uint32 {
   541  	for _, threshold := range cluster.GetCircuitBreakers().GetThresholds() {
   542  		if threshold.GetPriority() != v3corepb.RoutingPriority_DEFAULT {
   543  			continue
   544  		}
   545  		maxRequestsPb := threshold.GetMaxRequests()
   546  		if maxRequestsPb == nil {
   547  			return nil
   548  		}
   549  		maxRequests := maxRequestsPb.GetValue()
   550  		return &maxRequests
   551  	}
   552  	return nil
   553  }
   554  
   555  // idurationp takes a time.Duration and converts it to an internal duration, and
   556  // returns a pointer to that internal duration.
   557  func idurationp(d time.Duration) *iserviceconfig.Duration {
   558  	id := iserviceconfig.Duration(d)
   559  	return &id
   560  }
   561  
   562  func uint32p(i uint32) *uint32 {
   563  	return &i
   564  }
   565  
   566  // Helper types to prepare Outlier Detection JSON. Pointer types to distinguish
   567  // between unset and a zero value.
   568  type successRateEjection struct {
   569  	StdevFactor           *uint32 `json:"stdevFactor,omitempty"`
   570  	EnforcementPercentage *uint32 `json:"enforcementPercentage,omitempty"`
   571  	MinimumHosts          *uint32 `json:"minimumHosts,omitempty"`
   572  	RequestVolume         *uint32 `json:"requestVolume,omitempty"`
   573  }
   574  
   575  type failurePercentageEjection struct {
   576  	Threshold             *uint32 `json:"threshold,omitempty"`
   577  	EnforcementPercentage *uint32 `json:"enforcementPercentage,omitempty"`
   578  	MinimumHosts          *uint32 `json:"minimumHosts,omitempty"`
   579  	RequestVolume         *uint32 `json:"requestVolume,omitempty"`
   580  }
   581  
   582  type odLBConfig struct {
   583  	Interval                  *iserviceconfig.Duration   `json:"interval,omitempty"`
   584  	BaseEjectionTime          *iserviceconfig.Duration   `json:"baseEjectionTime,omitempty"`
   585  	MaxEjectionTime           *iserviceconfig.Duration   `json:"maxEjectionTime,omitempty"`
   586  	MaxEjectionPercent        *uint32                    `json:"maxEjectionPercent,omitempty"`
   587  	SuccessRateEjection       *successRateEjection       `json:"successRateEjection,omitempty"`
   588  	FailurePercentageEjection *failurePercentageEjection `json:"failurePercentageEjection,omitempty"`
   589  }
   590  
   591  // outlierConfigFromCluster converts the received Outlier Detection
   592  // configuration into JSON configuration for Outlier Detection, taking into
   593  // account xDS Defaults. Returns nil if no OutlierDetection field set in the
   594  // cluster resource.
   595  func outlierConfigFromCluster(cluster *v3clusterpb.Cluster) (json.RawMessage, error) {
   596  	od := cluster.GetOutlierDetection()
   597  	if od == nil {
   598  		return nil, nil
   599  	}
   600  
   601  	// "The outlier_detection field of the Cluster resource should have its fields
   602  	//	validated according to the rules for the corresponding LB policy config
   603  	//	fields in the above "Validation" section. If any of these requirements is
   604  	//	violated, the Cluster resource should be NACKed." - A50
   605  	// "The google.protobuf.Duration fields interval, base_ejection_time, and
   606  	// max_ejection_time must obey the restrictions in the
   607  	// google.protobuf.Duration documentation and they must have non-negative
   608  	// values." - A50
   609  	var interval *iserviceconfig.Duration
   610  	if i := od.GetInterval(); i != nil {
   611  		if err := i.CheckValid(); err != nil {
   612  			return nil, fmt.Errorf("outlier_detection.interval is invalid with error: %v", err)
   613  		}
   614  		if interval = idurationp(i.AsDuration()); *interval < 0 {
   615  			return nil, fmt.Errorf("outlier_detection.interval = %v; must be a valid duration and >= 0", *interval)
   616  		}
   617  	}
   618  
   619  	var baseEjectionTime *iserviceconfig.Duration
   620  	if bet := od.GetBaseEjectionTime(); bet != nil {
   621  		if err := bet.CheckValid(); err != nil {
   622  			return nil, fmt.Errorf("outlier_detection.base_ejection_time is invalid with error: %v", err)
   623  		}
   624  		if baseEjectionTime = idurationp(bet.AsDuration()); *baseEjectionTime < 0 {
   625  			return nil, fmt.Errorf("outlier_detection.base_ejection_time = %v; must be >= 0", *baseEjectionTime)
   626  		}
   627  	}
   628  
   629  	var maxEjectionTime *iserviceconfig.Duration
   630  	if met := od.GetMaxEjectionTime(); met != nil {
   631  		if err := met.CheckValid(); err != nil {
   632  			return nil, fmt.Errorf("outlier_detection.max_ejection_time is invalid: %v", err)
   633  		}
   634  		if maxEjectionTime = idurationp(met.AsDuration()); *maxEjectionTime < 0 {
   635  			return nil, fmt.Errorf("outlier_detection.max_ejection_time = %v; must be >= 0", *maxEjectionTime)
   636  		}
   637  	}
   638  
   639  	// "The fields max_ejection_percent, enforcing_success_rate,
   640  	// failure_percentage_threshold, and enforcing_failure_percentage must have
   641  	// values less than or equal to 100. If any of these requirements is
   642  	// violated, the Cluster resource should be NACKed." - A50
   643  	var maxEjectionPercent *uint32
   644  	if mep := od.GetMaxEjectionPercent(); mep != nil {
   645  		if maxEjectionPercent = uint32p(mep.GetValue()); *maxEjectionPercent > 100 {
   646  			return nil, fmt.Errorf("outlier_detection.max_ejection_percent = %v; must be <= 100", *maxEjectionPercent)
   647  		}
   648  	}
   649  	// "if the enforcing_success_rate field is set to 0, the config
   650  	// success_rate_ejection field will be null and all success_rate_* fields
   651  	// will be ignored." - A50
   652  	var enforcingSuccessRate *uint32
   653  	if esr := od.GetEnforcingSuccessRate(); esr != nil {
   654  		if enforcingSuccessRate = uint32p(esr.GetValue()); *enforcingSuccessRate > 100 {
   655  			return nil, fmt.Errorf("outlier_detection.enforcing_success_rate = %v; must be <= 100", *enforcingSuccessRate)
   656  		}
   657  	}
   658  	var failurePercentageThreshold *uint32
   659  	if fpt := od.GetFailurePercentageThreshold(); fpt != nil {
   660  		if failurePercentageThreshold = uint32p(fpt.GetValue()); *failurePercentageThreshold > 100 {
   661  			return nil, fmt.Errorf("outlier_detection.failure_percentage_threshold = %v; must be <= 100", *failurePercentageThreshold)
   662  		}
   663  	}
   664  	// "If the enforcing_failure_percent field is set to 0 or null, the config
   665  	// failure_percent_ejection field will be null and all failure_percent_*
   666  	// fields will be ignored." - A50
   667  	var enforcingFailurePercentage *uint32
   668  	if efp := od.GetEnforcingFailurePercentage(); efp != nil {
   669  		if enforcingFailurePercentage = uint32p(efp.GetValue()); *enforcingFailurePercentage > 100 {
   670  			return nil, fmt.Errorf("outlier_detection.enforcing_failure_percentage = %v; must be <= 100", *enforcingFailurePercentage)
   671  		}
   672  	}
   673  
   674  	var successRateStdevFactor *uint32
   675  	if srsf := od.GetSuccessRateStdevFactor(); srsf != nil {
   676  		successRateStdevFactor = uint32p(srsf.GetValue())
   677  	}
   678  	var successRateMinimumHosts *uint32
   679  	if srmh := od.GetSuccessRateMinimumHosts(); srmh != nil {
   680  		successRateMinimumHosts = uint32p(srmh.GetValue())
   681  	}
   682  	var successRateRequestVolume *uint32
   683  	if srrv := od.GetSuccessRateRequestVolume(); srrv != nil {
   684  		successRateRequestVolume = uint32p(srrv.GetValue())
   685  	}
   686  	var failurePercentageMinimumHosts *uint32
   687  	if fpmh := od.GetFailurePercentageMinimumHosts(); fpmh != nil {
   688  		failurePercentageMinimumHosts = uint32p(fpmh.GetValue())
   689  	}
   690  	var failurePercentageRequestVolume *uint32
   691  	if fprv := od.GetFailurePercentageRequestVolume(); fprv != nil {
   692  		failurePercentageRequestVolume = uint32p(fprv.GetValue())
   693  	}
   694  
   695  	// "if the enforcing_success_rate field is set to 0, the config
   696  	// success_rate_ejection field will be null and all success_rate_* fields
   697  	// will be ignored." - A50
   698  	var sre *successRateEjection
   699  	if enforcingSuccessRate == nil || *enforcingSuccessRate != 0 {
   700  		sre = &successRateEjection{
   701  			StdevFactor:           successRateStdevFactor,
   702  			EnforcementPercentage: enforcingSuccessRate,
   703  			MinimumHosts:          successRateMinimumHosts,
   704  			RequestVolume:         successRateRequestVolume,
   705  		}
   706  	}
   707  
   708  	// "If the enforcing_failure_percent field is set to 0 or null, the config
   709  	// failure_percent_ejection field will be null and all failure_percent_*
   710  	// fields will be ignored." - A50
   711  	var fpe *failurePercentageEjection
   712  	if enforcingFailurePercentage != nil && *enforcingFailurePercentage != 0 {
   713  		fpe = &failurePercentageEjection{
   714  			Threshold:             failurePercentageThreshold,
   715  			EnforcementPercentage: enforcingFailurePercentage,
   716  			MinimumHosts:          failurePercentageMinimumHosts,
   717  			RequestVolume:         failurePercentageRequestVolume,
   718  		}
   719  	}
   720  
   721  	odLBCfg := &odLBConfig{
   722  		Interval:                  interval,
   723  		BaseEjectionTime:          baseEjectionTime,
   724  		MaxEjectionTime:           maxEjectionTime,
   725  		MaxEjectionPercent:        maxEjectionPercent,
   726  		SuccessRateEjection:       sre,
   727  		FailurePercentageEjection: fpe,
   728  	}
   729  	return json.Marshal(odLBCfg)
   730  }