istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/cluster_tls.go (about)

     1  // Copyright Istio Authors. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package core
    16  
    17  import (
    18  	"fmt"
    19  
    20  	cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
    21  	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    22  	internalupstream "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/internal_upstream/v3"
    23  	tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    24  	http "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
    25  	metadata "github.com/envoyproxy/go-control-plane/envoy/type/metadata/v3"
    26  	"google.golang.org/protobuf/types/known/structpb"
    27  
    28  	"istio.io/api/mesh/v1alpha1"
    29  	networking "istio.io/api/networking/v1alpha3"
    30  	"istio.io/istio/pilot/pkg/features"
    31  	"istio.io/istio/pilot/pkg/model"
    32  	"istio.io/istio/pilot/pkg/networking/util"
    33  	sec_model "istio.io/istio/pilot/pkg/security/model"
    34  	"istio.io/istio/pilot/pkg/serviceregistry/provider"
    35  	"istio.io/istio/pilot/pkg/util/protoconv"
    36  	xdsfilters "istio.io/istio/pilot/pkg/xds/filters"
    37  	"istio.io/istio/pkg/log"
    38  	"istio.io/istio/pkg/security"
    39  	"istio.io/istio/pkg/wellknown"
    40  )
    41  
    42  var istioMtlsTransportSocketMatch = &structpb.Struct{
    43  	Fields: map[string]*structpb.Value{
    44  		model.TLSModeLabelShortname: {Kind: &structpb.Value_StringValue{StringValue: model.IstioMutualTLSModeLabel}},
    45  	},
    46  }
    47  
    48  var internalUpstreamSocket = &core.TransportSocket{
    49  	Name: "envoy.transport_sockets.internal_upstream",
    50  	ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: protoconv.MessageToAny(&internalupstream.InternalUpstreamTransport{
    51  		PassthroughMetadata: []*internalupstream.InternalUpstreamTransport_MetadataValueSource{
    52  			{
    53  				Kind: &metadata.MetadataKind{Kind: &metadata.MetadataKind_Host_{}},
    54  				Name: util.OriginalDstMetadataKey,
    55  			},
    56  			{
    57  				Kind: &metadata.MetadataKind{Kind: &metadata.MetadataKind_Cluster_{
    58  					Cluster: &metadata.MetadataKind_Cluster{},
    59  				}},
    60  				Name: "istio",
    61  			},
    62  			{
    63  				Kind: &metadata.MetadataKind{Kind: &metadata.MetadataKind_Host_{
    64  					Host: &metadata.MetadataKind_Host{},
    65  				}},
    66  				Name: "istio",
    67  			},
    68  		},
    69  		TransportSocket: xdsfilters.RawBufferTransportSocket,
    70  	})},
    71  }
    72  
    73  var hboneTransportSocket = &cluster.Cluster_TransportSocketMatch{
    74  	Name: "hbone",
    75  	Match: &structpb.Struct{
    76  		Fields: map[string]*structpb.Value{
    77  			model.TunnelLabelShortName: {Kind: &structpb.Value_StringValue{StringValue: model.TunnelHTTP}},
    78  		},
    79  	},
    80  	TransportSocket: internalUpstreamSocket,
    81  }
    82  
    83  var hboneOrPlaintextSocket = []*cluster.Cluster_TransportSocketMatch{
    84  	hboneTransportSocket,
    85  	defaultTransportSocketMatch(),
    86  }
    87  
    88  // applyUpstreamTLSSettings applies upstream tls context to the cluster
    89  func (cb *ClusterBuilder) applyUpstreamTLSSettings(
    90  	opts *buildClusterOpts,
    91  	tls *networking.ClientTLSSettings,
    92  	mtlsCtxType mtlsContextType,
    93  ) {
    94  	c := opts.mutable
    95  	tlsContext, err := cb.buildUpstreamClusterTLSContext(opts, tls)
    96  	if err != nil {
    97  		log.Errorf("failed to build Upstream TLSContext: %s", err.Error())
    98  		return
    99  	}
   100  
   101  	if tlsContext != nil {
   102  		c.cluster.TransportSocket = &core.TransportSocket{
   103  			Name:       wellknown.TransportSocketTLS,
   104  			ConfigType: &core.TransportSocket_TypedConfig{TypedConfig: protoconv.MessageToAny(tlsContext)},
   105  		}
   106  	}
   107  	istioAutodetectedMtls := tls != nil && tls.Mode == networking.ClientTLSSettings_ISTIO_MUTUAL &&
   108  		mtlsCtxType == autoDetected
   109  	if cb.sendHbone {
   110  		cb.applyHBONETransportSocketMatches(c.cluster, tls, istioAutodetectedMtls)
   111  	} else if c.cluster.GetType() != cluster.Cluster_ORIGINAL_DST {
   112  		// For headless service, discovery type will be `Cluster_ORIGINAL_DST`
   113  		// Apply auto mtls to clusters excluding these kind of headless services.
   114  		if istioAutodetectedMtls {
   115  			// convert to transport socket matcher if the mode was auto detected
   116  			transportSocket := c.cluster.TransportSocket
   117  			c.cluster.TransportSocket = nil
   118  			c.cluster.TransportSocketMatches = []*cluster.Cluster_TransportSocketMatch{
   119  				{
   120  					Name:            "tlsMode-" + model.IstioMutualTLSModeLabel,
   121  					Match:           istioMtlsTransportSocketMatch,
   122  					TransportSocket: transportSocket,
   123  				},
   124  				defaultTransportSocketMatch(),
   125  			}
   126  		}
   127  	}
   128  }
   129  
   130  func (cb *ClusterBuilder) buildUpstreamClusterTLSContext(opts *buildClusterOpts, tls *networking.ClientTLSSettings) (*tlsv3.UpstreamTlsContext, error) {
   131  	if tls == nil {
   132  		return nil, nil
   133  	}
   134  	// Hack to avoid egress sds cluster config generation for sidecar when
   135  	// CredentialName is set in DestinationRule without a workloadSelector.
   136  	// We do not want to support CredentialName setting in non workloadSelector based DestinationRules, because
   137  	// that would result in the CredentialName being supplied to all the sidecars which the DestinationRule is scoped to,
   138  	// resulting in delayed startup of sidecars who do not have access to the credentials.
   139  	if tls.CredentialName != "" && cb.sidecarProxy() && !opts.isDrWithSelector {
   140  		if tls.Mode == networking.ClientTLSSettings_SIMPLE || tls.Mode == networking.ClientTLSSettings_MUTUAL {
   141  			return nil, nil
   142  		}
   143  	}
   144  
   145  	c := opts.mutable
   146  	var tlsContext *tlsv3.UpstreamTlsContext
   147  
   148  	switch tls.Mode {
   149  	case networking.ClientTLSSettings_DISABLE:
   150  		tlsContext = nil
   151  	case networking.ClientTLSSettings_ISTIO_MUTUAL:
   152  		tlsContext = &tlsv3.UpstreamTlsContext{
   153  			CommonTlsContext: defaultUpstreamCommonTLSContext(),
   154  			Sni:              tls.Sni,
   155  		}
   156  
   157  		tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs = append(tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs,
   158  			sec_model.ConstructSdsSecretConfig(sec_model.SDSDefaultResourceName))
   159  
   160  		tlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_CombinedValidationContext{
   161  			CombinedValidationContext: &tlsv3.CommonTlsContext_CombinedCertificateValidationContext{
   162  				DefaultValidationContext:         &tlsv3.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)},
   163  				ValidationContextSdsSecretConfig: sec_model.ConstructSdsSecretConfig(sec_model.SDSRootResourceName),
   164  			},
   165  		}
   166  		// Set default SNI of cluster name for istio_mutual if sni is not set.
   167  		if len(tlsContext.Sni) == 0 {
   168  			tlsContext.Sni = c.cluster.Name
   169  		}
   170  		// `istio-peer-exchange` alpn is only used when using mtls communication between peers.
   171  		// We add `istio-peer-exchange` to the list of alpn strings.
   172  		// The code has repeated snippets because We want to use predefined alpn strings for efficiency.
   173  		if cb.isHttp2Cluster(c) {
   174  			// This is HTTP/2 in-mesh cluster, advertise it with ALPN.
   175  			if features.MetadataExchange && !features.DisableMxALPN {
   176  				tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMeshH2WithMxc
   177  			} else {
   178  				tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMeshH2
   179  			}
   180  		} else {
   181  			// This is in-mesh cluster, advertise it with ALPN.
   182  			if features.MetadataExchange && !features.DisableMxALPN {
   183  				tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMeshWithMxc
   184  			} else {
   185  				tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNInMesh
   186  			}
   187  		}
   188  	case networking.ClientTLSSettings_SIMPLE:
   189  		tlsContext = &tlsv3.UpstreamTlsContext{
   190  			CommonTlsContext: defaultUpstreamCommonTLSContext(),
   191  			Sni:              tls.Sni,
   192  		}
   193  
   194  		cb.setAutoSniAndAutoSanValidation(c, tls)
   195  
   196  		// Use subject alt names specified in service entry if TLS settings does not have subject alt names.
   197  		if opts.serviceRegistry == provider.External && len(tls.SubjectAltNames) == 0 {
   198  			tls = tls.DeepCopy()
   199  			tls.SubjectAltNames = opts.serviceAccounts
   200  		}
   201  		if tls.CredentialName != "" {
   202  			// If  credential name is specified at Destination Rule config and originating node is egress gateway, create
   203  			// SDS config for egress gateway to fetch key/cert at gateway agent.
   204  			sec_model.ApplyCustomSDSToClientCommonTLSContext(tlsContext.CommonTlsContext, tls, cb.credentialSocketExist)
   205  		} else {
   206  			// If CredentialName is not set fallback to files specified in DR.
   207  			res := security.SdsCertificateConfig{
   208  				CaCertificatePath: tls.CaCertificates,
   209  			}
   210  			// If tls.CaCertificate or CaCertificate in Metadata isn't configured, or tls.InsecureSkipVerify is true,
   211  			// don't set up SdsSecretConfig
   212  			if !res.IsRootCertificate() || tls.GetInsecureSkipVerify().GetValue() {
   213  				tlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContext{}
   214  			} else {
   215  				defaultValidationContext := &tlsv3.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}
   216  				if tls.GetCaCrl() != "" {
   217  					defaultValidationContext.Crl = &core.DataSource{
   218  						Specifier: &core.DataSource_Filename{
   219  							Filename: tls.GetCaCrl(),
   220  						},
   221  					}
   222  				}
   223  				tlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_CombinedValidationContext{
   224  					CombinedValidationContext: &tlsv3.CommonTlsContext_CombinedCertificateValidationContext{
   225  						DefaultValidationContext:         defaultValidationContext,
   226  						ValidationContextSdsSecretConfig: sec_model.ConstructSdsSecretConfig(res.GetRootResourceName()),
   227  					},
   228  				}
   229  
   230  			}
   231  		}
   232  
   233  		applyTLSDefaults(tlsContext, opts.mesh.GetTlsDefaults())
   234  
   235  		if cb.isHttp2Cluster(c) {
   236  			// This is HTTP/2 cluster, advertise it with ALPN.
   237  			tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNH2Only
   238  		}
   239  
   240  	case networking.ClientTLSSettings_MUTUAL:
   241  		tlsContext = &tlsv3.UpstreamTlsContext{
   242  			CommonTlsContext: defaultUpstreamCommonTLSContext(),
   243  			Sni:              tls.Sni,
   244  		}
   245  
   246  		cb.setAutoSniAndAutoSanValidation(c, tls)
   247  
   248  		// Use subject alt names specified in service entry if TLS settings does not have subject alt names.
   249  		if opts.serviceRegistry == provider.External && len(tls.SubjectAltNames) == 0 {
   250  			tls = tls.DeepCopy()
   251  			tls.SubjectAltNames = opts.serviceAccounts
   252  		}
   253  		if tls.CredentialName != "" {
   254  			// If credential name is specified at Destination Rule config and originating node is egress gateway, create
   255  			// SDS config for egress gateway to fetch key/cert at gateway agent.
   256  			sec_model.ApplyCustomSDSToClientCommonTLSContext(tlsContext.CommonTlsContext, tls, cb.credentialSocketExist)
   257  		} else {
   258  			// If CredentialName is not set fallback to file based approach
   259  			if tls.ClientCertificate == "" || tls.PrivateKey == "" {
   260  				err := fmt.Errorf("failed to apply tls setting for %s: client certificate and private key must not be empty",
   261  					c.cluster.Name)
   262  				return nil, err
   263  			}
   264  			// These are certs being mounted from within the pod and specified in Destination Rules.
   265  			// Rather than reading directly in Envoy, which does not support rotation, we will
   266  			// serve them over SDS by reading the files.
   267  			res := security.SdsCertificateConfig{
   268  				CertificatePath:   tls.ClientCertificate,
   269  				PrivateKeyPath:    tls.PrivateKey,
   270  				CaCertificatePath: tls.CaCertificates,
   271  			}
   272  			tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs = append(tlsContext.CommonTlsContext.TlsCertificateSdsSecretConfigs,
   273  				sec_model.ConstructSdsSecretConfig(res.GetResourceName()))
   274  
   275  			// If tls.CaCertificate or CaCertificate in Metadata isn't configured, or tls.InsecureSkipVerify is true,
   276  			// don't set up SdsSecretConfig
   277  			if !res.IsRootCertificate() || tls.GetInsecureSkipVerify().GetValue() {
   278  				tlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_ValidationContext{}
   279  			} else {
   280  				defaultValidationContext := &tlsv3.CertificateValidationContext{MatchSubjectAltNames: util.StringToExactMatch(tls.SubjectAltNames)}
   281  				if tls.GetCaCrl() != "" {
   282  					defaultValidationContext.Crl = &core.DataSource{
   283  						Specifier: &core.DataSource_Filename{
   284  							Filename: tls.GetCaCrl(),
   285  						},
   286  					}
   287  				}
   288  				tlsContext.CommonTlsContext.ValidationContextType = &tlsv3.CommonTlsContext_CombinedValidationContext{
   289  					CombinedValidationContext: &tlsv3.CommonTlsContext_CombinedCertificateValidationContext{
   290  						DefaultValidationContext:         defaultValidationContext,
   291  						ValidationContextSdsSecretConfig: sec_model.ConstructSdsSecretConfig(res.GetRootResourceName()),
   292  					},
   293  				}
   294  			}
   295  		}
   296  
   297  		applyTLSDefaults(tlsContext, opts.mesh.GetTlsDefaults())
   298  
   299  		if cb.isHttp2Cluster(c) {
   300  			// This is HTTP/2 cluster, advertise it with ALPN.
   301  			tlsContext.CommonTlsContext.AlpnProtocols = util.ALPNH2Only
   302  		}
   303  	}
   304  
   305  	// Compliance for Envoy TLS upstreams.
   306  	if tlsContext != nil {
   307  		sec_model.EnforceCompliance(tlsContext.CommonTlsContext)
   308  	}
   309  	return tlsContext, nil
   310  }
   311  
   312  // applyTLSDefaults applies tls default settings from mesh config to UpstreamTlsContext.
   313  func applyTLSDefaults(tlsContext *tlsv3.UpstreamTlsContext, tlsDefaults *v1alpha1.MeshConfig_TLSConfig) {
   314  	if tlsDefaults == nil {
   315  		return
   316  	}
   317  	if len(tlsDefaults.EcdhCurves) > 0 {
   318  		tlsContext.CommonTlsContext.TlsParams.EcdhCurves = tlsDefaults.EcdhCurves
   319  	}
   320  	if len(tlsDefaults.CipherSuites) > 0 {
   321  		tlsContext.CommonTlsContext.TlsParams.CipherSuites = tlsDefaults.CipherSuites
   322  	}
   323  }
   324  
   325  // Set auto_sni if EnableAutoSni feature flag is enabled and if sni field is not explicitly set in DR.
   326  // Set auto_san_validation if VerifyCertAtClient feature flag is enabled and if there is no explicit SubjectAltNames specified  in DR.
   327  func (cb *ClusterBuilder) setAutoSniAndAutoSanValidation(mc *clusterWrapper, tls *networking.ClientTLSSettings) {
   328  	if mc == nil || !features.EnableAutoSni {
   329  		return
   330  	}
   331  
   332  	setAutoSni := false
   333  	setAutoSanValidation := false
   334  	if len(tls.Sni) == 0 {
   335  		setAutoSni = true
   336  	}
   337  	if features.VerifyCertAtClient && setAutoSni && len(tls.SubjectAltNames) == 0 && !tls.GetInsecureSkipVerify().GetValue() {
   338  		setAutoSanValidation = true
   339  	}
   340  
   341  	if setAutoSni || setAutoSanValidation {
   342  		if mc.httpProtocolOptions == nil {
   343  			mc.httpProtocolOptions = &http.HttpProtocolOptions{}
   344  		}
   345  		if mc.httpProtocolOptions.UpstreamHttpProtocolOptions == nil {
   346  			mc.httpProtocolOptions.UpstreamHttpProtocolOptions = &core.UpstreamHttpProtocolOptions{}
   347  		}
   348  		if setAutoSni {
   349  			mc.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSni = true
   350  		}
   351  		if setAutoSanValidation {
   352  			mc.httpProtocolOptions.UpstreamHttpProtocolOptions.AutoSanValidation = true
   353  		}
   354  	}
   355  }
   356  
   357  func (cb *ClusterBuilder) applyHBONETransportSocketMatches(c *cluster.Cluster, tls *networking.ClientTLSSettings,
   358  	istioAutoDetectedMtls bool,
   359  ) {
   360  	if tls == nil {
   361  		c.TransportSocketMatches = hboneOrPlaintextSocket
   362  		return
   363  	}
   364  	// For headless service, discovery type will be `Cluster_ORIGINAL_DST`
   365  	// Apply auto mtls to clusters excluding these kind of headless services.
   366  	if c.GetType() != cluster.Cluster_ORIGINAL_DST {
   367  		// convert to transport socket matcher if the mode was auto detected
   368  		if istioAutoDetectedMtls {
   369  			transportSocket := c.TransportSocket
   370  			c.TransportSocket = nil
   371  			c.TransportSocketMatches = []*cluster.Cluster_TransportSocketMatch{
   372  				hboneTransportSocket,
   373  				{
   374  					Name:            "tlsMode-" + model.IstioMutualTLSModeLabel,
   375  					Match:           istioMtlsTransportSocketMatch,
   376  					TransportSocket: transportSocket,
   377  				},
   378  				defaultTransportSocketMatch(),
   379  			}
   380  		} else {
   381  			if c.TransportSocket == nil {
   382  				c.TransportSocketMatches = hboneOrPlaintextSocket
   383  			} else {
   384  				ts := c.TransportSocket
   385  				c.TransportSocket = nil
   386  
   387  				c.TransportSocketMatches = []*cluster.Cluster_TransportSocketMatch{
   388  					hboneTransportSocket,
   389  					{
   390  						Name:            "tlsMode-" + model.IstioMutualTLSModeLabel,
   391  						TransportSocket: ts,
   392  					},
   393  				}
   394  			}
   395  		}
   396  	}
   397  }
   398  
   399  func defaultUpstreamCommonTLSContext() *tlsv3.CommonTlsContext {
   400  	return &tlsv3.CommonTlsContext{
   401  		TlsParams: &tlsv3.TlsParameters{
   402  			// if not specified, envoy use TLSv1_2 as default for client.
   403  			TlsMaximumProtocolVersion: tlsv3.TlsParameters_TLSv1_3,
   404  			TlsMinimumProtocolVersion: tlsv3.TlsParameters_TLSv1_2,
   405  		},
   406  	}
   407  }
   408  
   409  // defaultTransportSocketMatch applies to endpoints that have no security.istio.io/tlsMode label
   410  // or those whose label value does not match "istio"
   411  func defaultTransportSocketMatch() *cluster.Cluster_TransportSocketMatch {
   412  	return &cluster.Cluster_TransportSocketMatch{
   413  		Name:            "tlsMode-disabled",
   414  		Match:           &structpb.Struct{},
   415  		TransportSocket: xdsfilters.RawBufferTransportSocket,
   416  	}
   417  }
   418  
   419  // buildUpstreamTLSSettings fills key cert fields for all TLSSettings when the mode is `ISTIO_MUTUAL`.
   420  // If the (input) TLS setting is nil (i.e not set), *and* the service mTLS mode is STRICT, it also
   421  // creates and populates the config as if they are set as ISTIO_MUTUAL.
   422  func (cb *ClusterBuilder) buildUpstreamTLSSettings(
   423  	tls *networking.ClientTLSSettings,
   424  	serviceAccounts []string,
   425  	sni string,
   426  	autoMTLSEnabled bool,
   427  	meshExternal bool,
   428  	serviceMTLSMode model.MutualTLSMode,
   429  ) (*networking.ClientTLSSettings, mtlsContextType) {
   430  	if tls != nil {
   431  		if tls.Mode == networking.ClientTLSSettings_DISABLE || tls.Mode == networking.ClientTLSSettings_SIMPLE {
   432  			return tls, userSupplied
   433  		}
   434  		// For backward compatibility, use metadata certs if provided.
   435  		if cb.hasMetadataCerts() {
   436  			// When building Mutual TLS settings, we should always use user supplied SubjectAltNames and SNI
   437  			// in destination rule. The Service Accounts and auto computed SNI should only be used for
   438  			// ISTIO_MUTUAL.
   439  			return cb.buildMutualTLS(tls.SubjectAltNames, tls.Sni), userSupplied
   440  		}
   441  		if tls.Mode != networking.ClientTLSSettings_ISTIO_MUTUAL {
   442  			return tls, userSupplied
   443  		}
   444  		// Update TLS settings for ISTIO_MUTUAL. Use client provided SNI if set. Otherwise,
   445  		// overwrite with the auto generated SNI. User specified SNIs in the istio mtls settings
   446  		// are useful when routing via gateways. Use Service Accounts if Subject Alt names
   447  		// are not specified in TLS settings.
   448  		sniToUse := tls.Sni
   449  		if len(sniToUse) == 0 {
   450  			sniToUse = sni
   451  		}
   452  		subjectAltNamesToUse := tls.SubjectAltNames
   453  		if subjectAltNamesToUse == nil {
   454  			subjectAltNamesToUse = serviceAccounts
   455  		}
   456  		return cb.buildIstioMutualTLS(subjectAltNamesToUse, sniToUse), userSupplied
   457  	}
   458  
   459  	if meshExternal || !autoMTLSEnabled || serviceMTLSMode == model.MTLSUnknown || serviceMTLSMode == model.MTLSDisable {
   460  		return nil, userSupplied
   461  	}
   462  
   463  	// For backward compatibility, use metadata certs if provided.
   464  	if cb.hasMetadataCerts() {
   465  		return cb.buildMutualTLS(serviceAccounts, sni), autoDetected
   466  	}
   467  
   468  	// Build settings for auto MTLS.
   469  	return cb.buildIstioMutualTLS(serviceAccounts, sni), autoDetected
   470  }
   471  
   472  func (cb *ClusterBuilder) hasMetadataCerts() bool {
   473  	return cb.metadataCerts != nil
   474  }
   475  
   476  // buildMutualTLS returns a `TLSSettings` for MUTUAL mode with proxy metadata certificates.
   477  func (cb *ClusterBuilder) buildMutualTLS(serviceAccounts []string, sni string) *networking.ClientTLSSettings {
   478  	return &networking.ClientTLSSettings{
   479  		Mode:              networking.ClientTLSSettings_MUTUAL,
   480  		CaCertificates:    cb.metadataCerts.tlsClientRootCert,
   481  		ClientCertificate: cb.metadataCerts.tlsClientCertChain,
   482  		PrivateKey:        cb.metadataCerts.tlsClientKey,
   483  		SubjectAltNames:   serviceAccounts,
   484  		Sni:               sni,
   485  	}
   486  }
   487  
   488  // buildIstioMutualTLS returns a `TLSSettings` for ISTIO_MUTUAL mode.
   489  func (cb *ClusterBuilder) buildIstioMutualTLS(san []string, sni string) *networking.ClientTLSSettings {
   490  	return &networking.ClientTLSSettings{
   491  		Mode:            networking.ClientTLSSettings_ISTIO_MUTUAL,
   492  		SubjectAltNames: san,
   493  		Sni:             sni,
   494  	}
   495  }