istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/security/model/authentication.go (about)

     1  // Copyright Istio Authors
     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 model
    16  
    17  import (
    18  	gotls "crypto/tls"
    19  	"strings"
    20  
    21  	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    22  	tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    23  
    24  	networking "istio.io/api/networking/v1alpha3"
    25  	"istio.io/istio/pilot/pkg/model"
    26  	"istio.io/istio/pilot/pkg/model/credentials"
    27  	"istio.io/istio/pilot/pkg/networking/util"
    28  	pm "istio.io/istio/pkg/model"
    29  	"istio.io/istio/pkg/security"
    30  	"istio.io/istio/pkg/spiffe"
    31  )
    32  
    33  const (
    34  	// SDSClusterName is the name of the cluster for SDS connections
    35  	SDSClusterName = pm.SDSClusterName
    36  
    37  	// SDSDefaultResourceName is the default name in sdsconfig, used for fetching normal key/cert.
    38  	SDSDefaultResourceName = pm.SDSDefaultResourceName
    39  
    40  	// SDSRootResourceName is the sdsconfig name for root CA, used for fetching root cert.
    41  	SDSRootResourceName = pm.SDSRootResourceName
    42  
    43  	// ThirdPartyJwtPath is the token volume mount file name for k8s trustworthy jwt token.
    44  	ThirdPartyJwtPath = "/var/run/secrets/tokens/istio-token"
    45  
    46  	// SdsCaSuffix is the suffix of the sds resource name for root CA.
    47  	SdsCaSuffix = credentials.SdsCaSuffix
    48  
    49  	// EnvoyJwtFilterName is the name of the Envoy JWT filter. This should be the same as the name defined
    50  	// in https://github.com/envoyproxy/envoy/blob/v1.9.1/source/extensions/filters/http/well_known_names.h#L48
    51  	EnvoyJwtFilterName = "envoy.filters.http.jwt_authn"
    52  )
    53  
    54  var SDSAdsConfig = &core.ConfigSource{
    55  	ConfigSourceSpecifier: &core.ConfigSource_Ads{
    56  		Ads: &core.AggregatedConfigSource{},
    57  	},
    58  	// We intentionally do *not* set InitialFetchTimeout to 0s here, as this is used for
    59  	// credentialName SDS which may refer to secrets which do not exist. We do not want to block the
    60  	// entire listener/cluster in these cases.
    61  	ResourceApiVersion: core.ApiVersion_V3,
    62  }
    63  
    64  // ConstructSdsSecretConfigForCredential constructs SDS secret configuration used
    65  // from certificates referenced by credentialName in DestinationRule or Gateway.
    66  // Currently this is served by a local SDS server, but in the future replaced by
    67  // Istiod SDS server.
    68  func ConstructSdsSecretConfigForCredential(name string, credentialSocketExist bool) *tls.SdsSecretConfig {
    69  	if name == "" {
    70  		return nil
    71  	}
    72  	if name == credentials.BuiltinGatewaySecretTypeURI {
    73  		return ConstructSdsSecretConfig(SDSDefaultResourceName)
    74  	}
    75  	if name == credentials.BuiltinGatewaySecretTypeURI+SdsCaSuffix {
    76  		return ConstructSdsSecretConfig(SDSRootResourceName)
    77  	}
    78  	// if credentialSocketExist exists and credentialName is using SDSExternalCredentialPrefix
    79  	// SDS will be served via SDSExternalClusterName
    80  	if credentialSocketExist && strings.HasPrefix(name, security.SDSExternalCredentialPrefix) {
    81  		return ConstructSdsSecretConfigForCredentialSocket(name)
    82  	}
    83  
    84  	return &tls.SdsSecretConfig{
    85  		Name:      credentials.ToResourceName(name),
    86  		SdsConfig: SDSAdsConfig,
    87  	}
    88  }
    89  
    90  // ConstructSdsSecretConfigForCredentialSocket constructs SDS Secret Configuration based on CredentialNameSocketPath
    91  // if CredentialNameSocketPath exists, use a static cluster 'sds-external'
    92  func ConstructSdsSecretConfigForCredentialSocket(name string) *tls.SdsSecretConfig {
    93  	return &tls.SdsSecretConfig{
    94  		Name: name,
    95  		SdsConfig: &core.ConfigSource{
    96  			ConfigSourceSpecifier: &core.ConfigSource_ApiConfigSource{
    97  				ApiConfigSource: &core.ApiConfigSource{
    98  					ApiType:                   core.ApiConfigSource_GRPC,
    99  					SetNodeOnFirstMessageOnly: true,
   100  					TransportApiVersion:       core.ApiVersion_V3,
   101  					GrpcServices: []*core.GrpcService{
   102  						{
   103  							TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
   104  								EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: security.SDSExternalClusterName},
   105  							},
   106  						},
   107  					},
   108  				},
   109  			},
   110  			ResourceApiVersion: core.ApiVersion_V3,
   111  		},
   112  	}
   113  }
   114  
   115  // ConstructSdsSecretConfig constructs SDS Secret Configuration for workload proxy.
   116  func ConstructSdsSecretConfig(name string) *tls.SdsSecretConfig {
   117  	return pm.ConstructSdsSecretConfig(name)
   118  }
   119  
   120  func AppendURIPrefixToTrustDomain(trustDomainAliases []string) []string {
   121  	res := make([]string, 0, len(trustDomainAliases))
   122  	for _, td := range trustDomainAliases {
   123  		res = append(res, spiffe.URIPrefix+td+"/")
   124  	}
   125  	return res
   126  }
   127  
   128  // ApplyToCommonTLSContext completes the commonTlsContext
   129  func ApplyToCommonTLSContext(tlsContext *tls.CommonTlsContext, proxy *model.Proxy,
   130  	subjectAltNames []string, crl string, trustDomainAliases []string, validateClient bool,
   131  ) {
   132  	// These are certs being mounted from within the pod. Rather than reading directly in Envoy,
   133  	// which does not support rotation, we will serve them over SDS by reading the files.
   134  	// We should check if these certs have values, if yes we should use them or otherwise fall back to defaults.
   135  	res := security.SdsCertificateConfig{
   136  		CertificatePath:   proxy.Metadata.TLSServerCertChain,
   137  		PrivateKeyPath:    proxy.Metadata.TLSServerKey,
   138  		CaCertificatePath: proxy.Metadata.TLSServerRootCert,
   139  	}
   140  
   141  	// TODO: if subjectAltName ends with *, create a prefix match as well.
   142  	// TODO: if user explicitly specifies SANs - should we alter his explicit config by adding all spifee aliases?
   143  	matchSAN := util.StringToExactMatch(subjectAltNames)
   144  	if len(trustDomainAliases) > 0 {
   145  		matchSAN = append(matchSAN, util.StringToPrefixMatch(AppendURIPrefixToTrustDomain(trustDomainAliases))...)
   146  	}
   147  
   148  	// configure server listeners with SDS.
   149  	if validateClient {
   150  		defaultValidationContext := &tls.CertificateValidationContext{
   151  			MatchSubjectAltNames: matchSAN,
   152  		}
   153  		if crl != "" {
   154  			defaultValidationContext.Crl = &core.DataSource{
   155  				Specifier: &core.DataSource_Filename{
   156  					Filename: crl,
   157  				},
   158  			}
   159  		}
   160  		tlsContext.ValidationContextType = &tls.CommonTlsContext_CombinedValidationContext{
   161  			CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
   162  				DefaultValidationContext:         defaultValidationContext,
   163  				ValidationContextSdsSecretConfig: ConstructSdsSecretConfig(model.GetOrDefault(res.GetRootResourceName(), SDSRootResourceName)),
   164  			},
   165  		}
   166  
   167  	}
   168  	tlsContext.TlsCertificateSdsSecretConfigs = []*tls.SdsSecretConfig{
   169  		ConstructSdsSecretConfig(model.GetOrDefault(res.GetResourceName(), SDSDefaultResourceName)),
   170  	}
   171  }
   172  
   173  // ApplyCustomSDSToClientCommonTLSContext applies the customized sds to CommonTlsContext
   174  // Used for building upstream TLS context for egress gateway's TLS/mTLS origination
   175  func ApplyCustomSDSToClientCommonTLSContext(tlsContext *tls.CommonTlsContext,
   176  	tlsOpts *networking.ClientTLSSettings, credentialSocketExist bool,
   177  ) {
   178  	if tlsOpts.Mode == networking.ClientTLSSettings_MUTUAL {
   179  		// create SDS config for gateway to fetch key/cert from agent.
   180  		tlsContext.TlsCertificateSdsSecretConfigs = []*tls.SdsSecretConfig{
   181  			ConstructSdsSecretConfigForCredential(tlsOpts.CredentialName, credentialSocketExist),
   182  		}
   183  	}
   184  
   185  	// If the InsecureSkipVerify is true, there is no need to configure CA Cert and SAN.
   186  	if tlsOpts.GetInsecureSkipVerify().GetValue() {
   187  		return
   188  	}
   189  
   190  	// create SDS config for gateway to fetch certificate validation context
   191  	// at gateway agent.
   192  	defaultValidationContext := &tls.CertificateValidationContext{
   193  		MatchSubjectAltNames: util.StringToExactMatch(tlsOpts.SubjectAltNames),
   194  	}
   195  	tlsContext.ValidationContextType = &tls.CommonTlsContext_CombinedValidationContext{
   196  		CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
   197  			DefaultValidationContext: defaultValidationContext,
   198  			ValidationContextSdsSecretConfig: ConstructSdsSecretConfigForCredential(
   199  				tlsOpts.CredentialName+SdsCaSuffix, credentialSocketExist),
   200  		},
   201  	}
   202  }
   203  
   204  // ApplyCredentialSDSToServerCommonTLSContext applies the credentialName sds (Gateway/DestinationRule) to CommonTlsContext
   205  // Used for building both gateway/sidecar TLS context
   206  func ApplyCredentialSDSToServerCommonTLSContext(tlsContext *tls.CommonTlsContext,
   207  	tlsOpts *networking.ServerTLSSettings, credentialSocketExist bool,
   208  ) {
   209  	// create SDS config for gateway/sidecar to fetch key/cert from agent.
   210  	tlsContext.TlsCertificateSdsSecretConfigs = []*tls.SdsSecretConfig{
   211  		ConstructSdsSecretConfigForCredential(tlsOpts.CredentialName, credentialSocketExist),
   212  	}
   213  	// If tls mode is MUTUAL/OPTIONAL_MUTUAL, create SDS config for gateway/sidecar to fetch certificate validation context
   214  	// at gateway agent. Otherwise, use the static certificate validation context config.
   215  	if tlsOpts.Mode == networking.ServerTLSSettings_MUTUAL || tlsOpts.Mode == networking.ServerTLSSettings_OPTIONAL_MUTUAL {
   216  		defaultValidationContext := &tls.CertificateValidationContext{
   217  			MatchSubjectAltNames:  util.StringToExactMatch(tlsOpts.SubjectAltNames),
   218  			VerifyCertificateSpki: tlsOpts.VerifyCertificateSpki,
   219  			VerifyCertificateHash: tlsOpts.VerifyCertificateHash,
   220  		}
   221  		tlsContext.ValidationContextType = &tls.CommonTlsContext_CombinedValidationContext{
   222  			CombinedValidationContext: &tls.CommonTlsContext_CombinedCertificateValidationContext{
   223  				DefaultValidationContext: defaultValidationContext,
   224  				ValidationContextSdsSecretConfig: ConstructSdsSecretConfigForCredential(
   225  					tlsOpts.CredentialName+SdsCaSuffix, credentialSocketExist),
   226  			},
   227  		}
   228  	} else if len(tlsOpts.SubjectAltNames) > 0 {
   229  		tlsContext.ValidationContextType = &tls.CommonTlsContext_ValidationContext{
   230  			ValidationContext: &tls.CertificateValidationContext{
   231  				MatchSubjectAltNames: util.StringToExactMatch(tlsOpts.SubjectAltNames),
   232  			},
   233  		}
   234  	}
   235  }
   236  
   237  func EnforceGoCompliance(ctx *gotls.Config) {
   238  	pm.EnforceGoCompliance(ctx)
   239  }
   240  
   241  func EnforceCompliance(ctx *tls.CommonTlsContext) {
   242  	pm.EnforceCompliance(ctx)
   243  }