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 }