google.golang.org/grpc@v1.74.2/internal/credentials/xds/handshake_info.go (about) 1 /* 2 * 3 * Copyright 2020 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 19 // Package xds contains non-user facing functionality of the xds credentials. 20 package xds 21 22 import ( 23 "context" 24 "crypto/tls" 25 "crypto/x509" 26 "errors" 27 "fmt" 28 "strings" 29 "unsafe" 30 31 "google.golang.org/grpc/attributes" 32 "google.golang.org/grpc/credentials/tls/certprovider" 33 "google.golang.org/grpc/internal" 34 "google.golang.org/grpc/internal/credentials/spiffe" 35 "google.golang.org/grpc/internal/xds/matcher" 36 "google.golang.org/grpc/resolver" 37 ) 38 39 func init() { 40 internal.GetXDSHandshakeInfoForTesting = GetHandshakeInfo 41 } 42 43 // handshakeAttrKey is the type used as the key to store HandshakeInfo in 44 // the Attributes field of resolver.Address. 45 type handshakeAttrKey struct{} 46 47 // Equal reports whether the handshake info structs are identical. 48 func (hi *HandshakeInfo) Equal(other *HandshakeInfo) bool { 49 if hi == nil && other == nil { 50 return true 51 } 52 if hi == nil || other == nil { 53 return false 54 } 55 if hi.rootProvider != other.rootProvider || 56 hi.identityProvider != other.identityProvider || 57 hi.requireClientCert != other.requireClientCert || 58 len(hi.sanMatchers) != len(other.sanMatchers) { 59 return false 60 } 61 for i := range hi.sanMatchers { 62 if !hi.sanMatchers[i].Equal(other.sanMatchers[i]) { 63 return false 64 } 65 } 66 return true 67 } 68 69 // SetHandshakeInfo returns a copy of addr in which the Attributes field is 70 // updated with hiPtr. 71 func SetHandshakeInfo(addr resolver.Address, hiPtr *unsafe.Pointer) resolver.Address { 72 addr.Attributes = addr.Attributes.WithValue(handshakeAttrKey{}, hiPtr) 73 return addr 74 } 75 76 // GetHandshakeInfo returns a pointer to the *HandshakeInfo stored in attr. 77 func GetHandshakeInfo(attr *attributes.Attributes) *unsafe.Pointer { 78 v := attr.Value(handshakeAttrKey{}) 79 hi, _ := v.(*unsafe.Pointer) 80 return hi 81 } 82 83 // HandshakeInfo wraps all the security configuration required by client and 84 // server handshake methods in xds credentials. The xDS implementation will be 85 // responsible for populating these fields. 86 type HandshakeInfo struct { 87 // All fields written at init time and read only after that, so no 88 // synchronization needed. 89 rootProvider certprovider.Provider 90 identityProvider certprovider.Provider 91 sanMatchers []matcher.StringMatcher // Only on the client side. 92 requireClientCert bool // Only on server side. 93 } 94 95 // NewHandshakeInfo returns a new handshake info configured with the provided 96 // options. 97 func NewHandshakeInfo(rootProvider certprovider.Provider, identityProvider certprovider.Provider, sanMatchers []matcher.StringMatcher, requireClientCert bool) *HandshakeInfo { 98 return &HandshakeInfo{ 99 rootProvider: rootProvider, 100 identityProvider: identityProvider, 101 sanMatchers: sanMatchers, 102 requireClientCert: requireClientCert, 103 } 104 } 105 106 // UseFallbackCreds returns true when fallback credentials are to be used based 107 // on the contents of the HandshakeInfo. 108 func (hi *HandshakeInfo) UseFallbackCreds() bool { 109 if hi == nil { 110 return true 111 } 112 return hi.identityProvider == nil && hi.rootProvider == nil 113 } 114 115 // GetSANMatchersForTesting returns the SAN matchers stored in HandshakeInfo. 116 // To be used only for testing purposes. 117 func (hi *HandshakeInfo) GetSANMatchersForTesting() []matcher.StringMatcher { 118 return append([]matcher.StringMatcher{}, hi.sanMatchers...) 119 } 120 121 // ClientSideTLSConfig constructs a tls.Config to be used in a client-side 122 // handshake based on the contents of the HandshakeInfo. 123 func (hi *HandshakeInfo) ClientSideTLSConfig(ctx context.Context) (*tls.Config, error) { 124 // On the client side, rootProvider is mandatory. IdentityProvider is 125 // optional based on whether the client is doing TLS or mTLS. 126 if hi.rootProvider == nil { 127 return nil, errors.New("xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server") 128 } 129 // Since the call to KeyMaterial() can block, we read the providers under 130 // the lock but call the actual function after releasing the lock. 131 rootProv, idProv := hi.rootProvider, hi.identityProvider 132 133 // InsecureSkipVerify needs to be set to true because we need to perform 134 // custom verification to check the SAN on the received certificate. 135 // Currently the Go stdlib does complete verification of the cert (which 136 // includes hostname verification) or none. We are forced to go with the 137 // latter and perform the normal cert validation ourselves. 138 cfg := &tls.Config{ 139 InsecureSkipVerify: true, 140 NextProtos: []string{"h2"}, 141 } 142 143 km, err := rootProv.KeyMaterial(ctx) 144 if err != nil { 145 return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err) 146 } 147 cfg.RootCAs = km.Roots 148 cfg.VerifyPeerCertificate = hi.buildVerifyFunc(km, true) 149 150 if idProv != nil { 151 km, err := idProv.KeyMaterial(ctx) 152 if err != nil { 153 return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err) 154 } 155 cfg.Certificates = km.Certs 156 } 157 return cfg, nil 158 } 159 160 func (hi *HandshakeInfo) buildVerifyFunc(km *certprovider.KeyMaterial, isClient bool) func(rawCerts [][]byte, _ [][]*x509.Certificate) error { 161 return func(rawCerts [][]byte, _ [][]*x509.Certificate) error { 162 // Parse all raw certificates presented by the peer. 163 var certs []*x509.Certificate 164 for _, rc := range rawCerts { 165 cert, err := x509.ParseCertificate(rc) 166 if err != nil { 167 return err 168 } 169 certs = append(certs, cert) 170 } 171 172 // Build the intermediates list and verify that the leaf certificate is 173 // signed by one of the root certificates. If a SPIFFE Bundle Map is 174 // configured, it is used to get the root certs. Otherwise, the 175 // configured roots in the root provider are used. 176 intermediates := x509.NewCertPool() 177 for _, cert := range certs[1:] { 178 intermediates.AddCert(cert) 179 } 180 roots := km.Roots 181 // If a SPIFFE Bundle Map is configured, find the roots for the trust 182 // domain of the leaf certificate. 183 if km.SPIFFEBundleMap != nil { 184 var err error 185 roots, err = spiffe.GetRootsFromSPIFFEBundleMap(km.SPIFFEBundleMap, certs[0]) 186 if err != nil { 187 return err 188 } 189 } 190 opts := x509.VerifyOptions{ 191 Roots: roots, 192 Intermediates: intermediates, 193 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, 194 } 195 if isClient { 196 opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} 197 } else { 198 opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth} 199 } 200 if _, err := certs[0].Verify(opts); err != nil { 201 return err 202 } 203 // The SANs sent by the MeshCA are encoded as SPIFFE IDs. We need to 204 // only look at the SANs on the leaf cert. 205 if cert := certs[0]; !hi.MatchingSANExists(cert) { 206 // TODO: Print the complete certificate once the x509 package 207 // supports a String() method on the Certificate type. 208 return fmt.Errorf("xds: received SANs {DNSNames: %v, EmailAddresses: %v, IPAddresses: %v, URIs: %v} do not match any of the accepted SANs", cert.DNSNames, cert.EmailAddresses, cert.IPAddresses, cert.URIs) 209 } 210 return nil 211 } 212 } 213 214 // ServerSideTLSConfig constructs a tls.Config to be used in a server-side 215 // handshake based on the contents of the HandshakeInfo. 216 func (hi *HandshakeInfo) ServerSideTLSConfig(ctx context.Context) (*tls.Config, error) { 217 cfg := &tls.Config{ 218 ClientAuth: tls.NoClientCert, 219 NextProtos: []string{"h2"}, 220 } 221 // On the server side, identityProvider is mandatory. RootProvider is 222 // optional based on whether the server is doing TLS or mTLS. 223 if hi.identityProvider == nil { 224 return nil, errors.New("xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake. Please check configuration on the management server") 225 } 226 // Since the call to KeyMaterial() can block, we read the providers under 227 // the lock but call the actual function after releasing the lock. 228 rootProv, idProv := hi.rootProvider, hi.identityProvider 229 if hi.requireClientCert { 230 cfg.ClientAuth = tls.RequireAndVerifyClientCert 231 } 232 233 // identityProvider is mandatory on the server side. 234 km, err := idProv.KeyMaterial(ctx) 235 if err != nil { 236 return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err) 237 } 238 cfg.Certificates = km.Certs 239 240 if rootProv != nil { 241 km, err := rootProv.KeyMaterial(ctx) 242 if err != nil { 243 return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err) 244 } 245 if km.SPIFFEBundleMap != nil && hi.requireClientCert { 246 // ClientAuth, if set greater than tls.RequireAnyClientCert, must be 247 // dropped to tls.RequireAnyClientCert so that custom verification 248 // to use SPIFFE Bundles is done. 249 cfg.ClientAuth = tls.RequireAnyClientCert 250 cfg.VerifyPeerCertificate = hi.buildVerifyFunc(km, false) 251 } else { 252 cfg.ClientCAs = km.Roots 253 } 254 } 255 return cfg, nil 256 } 257 258 // MatchingSANExists returns true if the SANs contained in cert match the 259 // criteria enforced by the list of SAN matchers in HandshakeInfo. 260 // 261 // If the list of SAN matchers in the HandshakeInfo is empty, this function 262 // returns true for all input certificates. 263 func (hi *HandshakeInfo) MatchingSANExists(cert *x509.Certificate) bool { 264 if len(hi.sanMatchers) == 0 { 265 return true 266 } 267 268 // SANs can be specified in any of these four fields on the parsed cert. 269 for _, san := range cert.DNSNames { 270 if hi.matchSAN(san, true) { 271 return true 272 } 273 } 274 for _, san := range cert.EmailAddresses { 275 if hi.matchSAN(san, false) { 276 return true 277 } 278 } 279 for _, san := range cert.IPAddresses { 280 if hi.matchSAN(san.String(), false) { 281 return true 282 } 283 } 284 for _, san := range cert.URIs { 285 if hi.matchSAN(san.String(), false) { 286 return true 287 } 288 } 289 return false 290 } 291 292 // Caller must hold mu. 293 func (hi *HandshakeInfo) matchSAN(san string, isDNS bool) bool { 294 for _, matcher := range hi.sanMatchers { 295 if em := matcher.ExactMatch(); em != "" && isDNS { 296 // This is a special case which is documented in the xDS protos. 297 // If the DNS SAN is a wildcard entry, and the match criteria is 298 // `exact`, then we need to perform DNS wildcard matching 299 // instead of regular string comparison. 300 if dnsMatch(em, san) { 301 return true 302 } 303 continue 304 } 305 if matcher.Match(san) { 306 return true 307 } 308 } 309 return false 310 } 311 312 // dnsMatch implements a DNS wildcard matching algorithm based on RFC2828 and 313 // grpc-java's implementation in `OkHostnameVerifier` class. 314 // 315 // NOTE: Here the `host` argument is the one from the set of string matchers in 316 // the xDS proto and the `san` argument is a DNS SAN from the certificate, and 317 // this is the one which can potentially contain a wildcard pattern. 318 func dnsMatch(host, san string) bool { 319 // Add trailing "." and turn them into absolute domain names. 320 if !strings.HasSuffix(host, ".") { 321 host += "." 322 } 323 if !strings.HasSuffix(san, ".") { 324 san += "." 325 } 326 // Domain names are case-insensitive. 327 host = strings.ToLower(host) 328 san = strings.ToLower(san) 329 330 // If san does not contain a wildcard, do exact match. 331 if !strings.Contains(san, "*") { 332 return host == san 333 } 334 335 // Wildcard dns matching rules 336 // - '*' is only permitted in the left-most label and must be the only 337 // character in that label. For example, *.example.com is permitted, while 338 // *a.example.com, a*.example.com, a*b.example.com, a.*.example.com are 339 // not permitted. 340 // - '*' matches a single domain name component. For example, *.example.com 341 // matches test.example.com but does not match sub.test.example.com. 342 // - Wildcard patterns for single-label domain names are not permitted. 343 if san == "*." || !strings.HasPrefix(san, "*.") || strings.Contains(san[1:], "*") { 344 return false 345 } 346 // Optimization: at this point, we know that the san contains a '*' and 347 // is the first domain component of san. So, the host name must be at 348 // least as long as the san to be able to match. 349 if len(host) < len(san) { 350 return false 351 } 352 // Hostname must end with the non-wildcard portion of san. 353 if !strings.HasSuffix(host, san[1:]) { 354 return false 355 } 356 // At this point we know that the hostName and san share the same suffix 357 // (the non-wildcard portion of san). Now, we just need to make sure 358 // that the '*' does not match across domain components. 359 hostPrefix := strings.TrimSuffix(host, san[1:]) 360 return !strings.Contains(hostPrefix, ".") 361 }