mirror of https://github.com/grpc/grpc-go.git
credentials/xds: Move non-user facing functionality to an internal package (#4117)
This commit is contained in:
parent
644d506ebb
commit
d79063fdde
|
@ -32,21 +32,13 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/attributes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||
"google.golang.org/grpc/internal"
|
||||
credinternal "google.golang.org/grpc/internal/credentials"
|
||||
"google.golang.org/grpc/resolver"
|
||||
xdsinternal "google.golang.org/grpc/internal/credentials/xds"
|
||||
)
|
||||
|
||||
func init() {
|
||||
internal.GetXDSHandshakeInfoForTesting = getHandshakeInfo
|
||||
}
|
||||
|
||||
// ClientOptions contains parameters to configure a new client-side xDS
|
||||
// credentials implementation.
|
||||
type ClientOptions struct {
|
||||
|
@ -97,193 +89,6 @@ type credsImpl struct {
|
|||
fallback credentials.TransportCredentials
|
||||
}
|
||||
|
||||
// handshakeAttrKey is the type used as the key to store HandshakeInfo in
|
||||
// the Attributes field of resolver.Address.
|
||||
type handshakeAttrKey struct{}
|
||||
|
||||
// SetHandshakeInfo returns a copy of addr in which the Attributes field is
|
||||
// updated with hInfo.
|
||||
func SetHandshakeInfo(addr resolver.Address, hInfo *HandshakeInfo) resolver.Address {
|
||||
addr.Attributes = addr.Attributes.WithValues(handshakeAttrKey{}, hInfo)
|
||||
return addr
|
||||
}
|
||||
|
||||
// getHandshakeInfo returns a pointer to the HandshakeInfo stored in attr.
|
||||
func getHandshakeInfo(attr *attributes.Attributes) *HandshakeInfo {
|
||||
v := attr.Value(handshakeAttrKey{})
|
||||
hi, _ := v.(*HandshakeInfo)
|
||||
return hi
|
||||
}
|
||||
|
||||
// HandshakeInfo wraps all the security configuration required by client and
|
||||
// server handshake methods in credsImpl. The xDS implementation will be
|
||||
// responsible for populating these fields.
|
||||
//
|
||||
// Safe for concurrent access.
|
||||
//
|
||||
// TODO(easwars): Move this type and any other non-user functionality to an
|
||||
// internal package.
|
||||
type HandshakeInfo struct {
|
||||
mu sync.Mutex
|
||||
rootProvider certprovider.Provider
|
||||
identityProvider certprovider.Provider
|
||||
acceptedSANs map[string]bool // Only on the client side.
|
||||
requireClientCert bool // Only on server side.
|
||||
}
|
||||
|
||||
// SetRootCertProvider updates the root certificate provider.
|
||||
func (hi *HandshakeInfo) SetRootCertProvider(root certprovider.Provider) {
|
||||
hi.mu.Lock()
|
||||
hi.rootProvider = root
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetIdentityCertProvider updates the identity certificate provider.
|
||||
func (hi *HandshakeInfo) SetIdentityCertProvider(identity certprovider.Provider) {
|
||||
hi.mu.Lock()
|
||||
hi.identityProvider = identity
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetAcceptedSANs updates the list of accepted SANs.
|
||||
func (hi *HandshakeInfo) SetAcceptedSANs(sans []string) {
|
||||
hi.mu.Lock()
|
||||
hi.acceptedSANs = make(map[string]bool, len(sans))
|
||||
for _, san := range sans {
|
||||
hi.acceptedSANs[san] = true
|
||||
}
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetRequireClientCert updates whether a client cert is required during the
|
||||
// ServerHandshake(). A value of true indicates that we are performing mTLS.
|
||||
func (hi *HandshakeInfo) SetRequireClientCert(require bool) {
|
||||
hi.mu.Lock()
|
||||
hi.requireClientCert = require
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// UseFallbackCreds returns true when fallback credentials are to be used based
|
||||
// on the contents of the HandshakeInfo.
|
||||
func (hi *HandshakeInfo) UseFallbackCreds() bool {
|
||||
if hi == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
hi.mu.Lock()
|
||||
defer hi.mu.Unlock()
|
||||
return hi.identityProvider == nil && hi.rootProvider == nil
|
||||
}
|
||||
|
||||
func (hi *HandshakeInfo) makeClientSideTLSConfig(ctx context.Context) (*tls.Config, error) {
|
||||
hi.mu.Lock()
|
||||
// On the client side, rootProvider is mandatory. IdentityProvider is
|
||||
// optional based on whether the client is doing TLS or mTLS.
|
||||
if hi.rootProvider == nil {
|
||||
return nil, errors.New("xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server")
|
||||
}
|
||||
// Since the call to KeyMaterial() can block, we read the providers under
|
||||
// the lock but call the actual function after releasing the lock.
|
||||
rootProv, idProv := hi.rootProvider, hi.identityProvider
|
||||
hi.mu.Unlock()
|
||||
|
||||
// InsecureSkipVerify needs to be set to true because we need to perform
|
||||
// custom verification to check the SAN on the received certificate.
|
||||
// Currently the Go stdlib does complete verification of the cert (which
|
||||
// includes hostname verification) or none. We are forced to go with the
|
||||
// latter and perform the normal cert validation ourselves.
|
||||
cfg := &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
km, err := rootProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.RootCAs = km.Roots
|
||||
|
||||
if idProv != nil {
|
||||
km, err := idProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.Certificates = km.Certs
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (hi *HandshakeInfo) makeServerSideTLSConfig(ctx context.Context) (*tls.Config, error) {
|
||||
cfg := &tls.Config{ClientAuth: tls.NoClientCert}
|
||||
hi.mu.Lock()
|
||||
// On the server side, identityProvider is mandatory. RootProvider is
|
||||
// optional based on whether the server is doing TLS or mTLS.
|
||||
if hi.identityProvider == nil {
|
||||
return nil, errors.New("xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake. Please check configuration on the management server")
|
||||
}
|
||||
// Since the call to KeyMaterial() can block, we read the providers under
|
||||
// the lock but call the actual function after releasing the lock.
|
||||
rootProv, idProv := hi.rootProvider, hi.identityProvider
|
||||
if hi.requireClientCert {
|
||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
hi.mu.Unlock()
|
||||
|
||||
// identityProvider is mandatory on the server side.
|
||||
km, err := idProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.Certificates = km.Certs
|
||||
|
||||
if rootProv != nil {
|
||||
km, err := rootProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.ClientCAs = km.Roots
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (hi *HandshakeInfo) matchingSANExists(cert *x509.Certificate) bool {
|
||||
if len(hi.acceptedSANs) == 0 {
|
||||
// An empty list of acceptedSANs means "accept everything".
|
||||
return true
|
||||
}
|
||||
|
||||
var sans []string
|
||||
// SANs can be specified in any of these four fields on the parsed cert.
|
||||
sans = append(sans, cert.DNSNames...)
|
||||
sans = append(sans, cert.EmailAddresses...)
|
||||
for _, ip := range cert.IPAddresses {
|
||||
sans = append(sans, ip.String())
|
||||
}
|
||||
for _, uri := range cert.URIs {
|
||||
sans = append(sans, uri.String())
|
||||
}
|
||||
|
||||
hi.mu.Lock()
|
||||
defer hi.mu.Unlock()
|
||||
for _, san := range sans {
|
||||
if hi.acceptedSANs[san] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewHandshakeInfo returns a new instance of HandshakeInfo with the given root
|
||||
// and identity certificate providers.
|
||||
func NewHandshakeInfo(root, identity certprovider.Provider, sans ...string) *HandshakeInfo {
|
||||
acceptedSANs := make(map[string]bool, len(sans))
|
||||
for _, san := range sans {
|
||||
acceptedSANs[san] = true
|
||||
}
|
||||
return &HandshakeInfo{
|
||||
rootProvider: root,
|
||||
identityProvider: identity,
|
||||
acceptedSANs: acceptedSANs,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientHandshake performs the TLS handshake on the client-side.
|
||||
//
|
||||
// It looks for the presence of a HandshakeInfo value in the passed in context
|
||||
|
@ -314,7 +119,7 @@ func (c *credsImpl) ClientHandshake(ctx context.Context, authority string, rawCo
|
|||
if chi.Attributes == nil {
|
||||
return c.fallback.ClientHandshake(ctx, authority, rawConn)
|
||||
}
|
||||
hi := getHandshakeInfo(chi.Attributes)
|
||||
hi := xdsinternal.GetHandshakeInfo(chi.Attributes)
|
||||
if hi.UseFallbackCreds() {
|
||||
return c.fallback.ClientHandshake(ctx, authority, rawConn)
|
||||
}
|
||||
|
@ -331,7 +136,7 @@ func (c *credsImpl) ClientHandshake(ctx context.Context, authority string, rawCo
|
|||
// 4. Key usage to match whether client/server usage.
|
||||
// 5. A `VerifyPeerCertificate` function which performs normal peer
|
||||
// cert verification using configured roots, and the custom SAN checks.
|
||||
cfg, err := hi.makeClientSideTLSConfig(ctx)
|
||||
cfg, err := hi.ClientSideTLSConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -362,7 +167,7 @@ func (c *credsImpl) ClientHandshake(ctx context.Context, authority string, rawCo
|
|||
}
|
||||
// The SANs sent by the MeshCA are encoded as SPIFFE IDs. We need to
|
||||
// only look at the SANs on the leaf cert.
|
||||
if !hi.matchingSANExists(certs[0]) {
|
||||
if !hi.MatchingSANExists(certs[0]) {
|
||||
return fmt.Errorf("SANs received in leaf certificate %+v does not match any of the accepted SANs", certs[0])
|
||||
}
|
||||
return nil
|
||||
|
@ -410,7 +215,9 @@ func (c *credsImpl) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.Aut
|
|||
// passed to this function does not implement this interface, or if the
|
||||
// `HandshakeInfo` does not contain the information we are looking for, we
|
||||
// delegate the handshake to the fallback credentials.
|
||||
hiConn, ok := rawConn.(interface{ XDSHandshakeInfo() *HandshakeInfo })
|
||||
hiConn, ok := rawConn.(interface {
|
||||
XDSHandshakeInfo() *xdsinternal.HandshakeInfo
|
||||
})
|
||||
if !ok {
|
||||
return c.fallback.ServerHandshake(rawConn)
|
||||
}
|
||||
|
@ -430,7 +237,7 @@ func (c *credsImpl) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.Aut
|
|||
}
|
||||
ctx, cancel := context.WithDeadline(context.Background(), deadline)
|
||||
defer cancel()
|
||||
cfg, err := hi.makeServerSideTLSConfig(ctx)
|
||||
cfg, err := hi.ServerSideTLSConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||
"google.golang.org/grpc/internal"
|
||||
xdsinternal "google.golang.org/grpc/internal/credentials/xds"
|
||||
"google.golang.org/grpc/internal/grpctest"
|
||||
"google.golang.org/grpc/internal/testutils"
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
@ -217,8 +218,8 @@ func newTestContextWithHandshakeInfo(parent context.Context, root, identity cert
|
|||
// Creating the HandshakeInfo and adding it to the attributes is very
|
||||
// similar to what the CDS balancer would do when it intercepts calls to
|
||||
// NewSubConn().
|
||||
info := NewHandshakeInfo(root, identity, sans...)
|
||||
addr := SetHandshakeInfo(resolver.Address{}, info)
|
||||
info := xdsinternal.NewHandshakeInfo(root, identity, sans...)
|
||||
addr := xdsinternal.SetHandshakeInfo(resolver.Address{}, info)
|
||||
|
||||
// Moving the attributes from the resolver.Address to the context passed to
|
||||
// the handshaker is done in the transport layer. Since we directly call the
|
||||
|
@ -529,12 +530,12 @@ func (s) TestClientCredsProviderSwitch(t *testing.T) {
|
|||
// Create a root provider which will fail the handshake because it does not
|
||||
// use the correct trust roots.
|
||||
root1 := makeRootProvider(t, "x509/client_ca_cert.pem")
|
||||
handshakeInfo := NewHandshakeInfo(root1, nil, defaultTestCertSAN)
|
||||
handshakeInfo := xdsinternal.NewHandshakeInfo(root1, nil, defaultTestCertSAN)
|
||||
|
||||
// We need to repeat most of what newTestContextWithHandshakeInfo() does
|
||||
// here because we need access to the underlying HandshakeInfo so that we
|
||||
// can update it before the next call to ClientHandshake().
|
||||
addr := SetHandshakeInfo(resolver.Address{}, handshakeInfo)
|
||||
addr := xdsinternal.SetHandshakeInfo(resolver.Address{}, handshakeInfo)
|
||||
contextWithHandshakeInfo := internal.NewClientHandshakeInfoContext.(func(context.Context, credentials.ClientHandshakeInfo) context.Context)
|
||||
ctx = contextWithHandshakeInfo(ctx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
|
||||
if _, _, err := creds.ClientHandshake(ctx, authority, conn); err == nil {
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||
xdsinternal "google.golang.org/grpc/internal/credentials/xds"
|
||||
"google.golang.org/grpc/testdata"
|
||||
)
|
||||
|
||||
|
@ -94,11 +95,11 @@ func (s) TestServerCredsWithoutFallback(t *testing.T) {
|
|||
|
||||
type wrapperConn struct {
|
||||
net.Conn
|
||||
xdsHI *HandshakeInfo
|
||||
xdsHI *xdsinternal.HandshakeInfo
|
||||
deadline time.Time
|
||||
}
|
||||
|
||||
func (wc *wrapperConn) XDSHandshakeInfo() *HandshakeInfo {
|
||||
func (wc *wrapperConn) XDSHandshakeInfo() *xdsinternal.HandshakeInfo {
|
||||
return wc.xdsHI
|
||||
}
|
||||
|
||||
|
@ -106,7 +107,7 @@ func (wc *wrapperConn) GetDeadline() time.Time {
|
|||
return wc.deadline
|
||||
}
|
||||
|
||||
func newWrappedConn(conn net.Conn, xdsHI *HandshakeInfo, deadline time.Time) *wrapperConn {
|
||||
func newWrappedConn(conn net.Conn, xdsHI *xdsinternal.HandshakeInfo, deadline time.Time) *wrapperConn {
|
||||
return &wrapperConn{Conn: conn, xdsHI: xdsHI, deadline: deadline}
|
||||
}
|
||||
|
||||
|
@ -120,7 +121,7 @@ func (s) TestServerCredsInvalidHandshakeInfo(t *testing.T) {
|
|||
t.Fatalf("NewServerCredentials(%v) failed: %v", opts, err)
|
||||
}
|
||||
|
||||
info := NewHandshakeInfo(&fakeProvider{}, nil)
|
||||
info := xdsinternal.NewHandshakeInfo(&fakeProvider{}, nil)
|
||||
conn := newWrappedConn(nil, info, time.Time{})
|
||||
if _, _, err := creds.ServerHandshake(conn); err == nil {
|
||||
t.Fatal("ServerHandshake succeeded without identity certificate provider in HandshakeInfo")
|
||||
|
@ -156,7 +157,7 @@ func (s) TestServerCredsProviderFailure(t *testing.T) {
|
|||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
info := NewHandshakeInfo(test.rootProvider, test.identityProvider)
|
||||
info := xdsinternal.NewHandshakeInfo(test.rootProvider, test.identityProvider)
|
||||
conn := newWrappedConn(nil, info, time.Time{})
|
||||
if _, _, err := creds.ServerHandshake(conn); err == nil || !strings.Contains(err.Error(), test.wantErr) {
|
||||
t.Fatalf("ServerHandshake() returned error: %q, wantErr: %q", err, test.wantErr)
|
||||
|
@ -178,7 +179,7 @@ func (s) TestServerCredsHandshakeTimeout(t *testing.T) {
|
|||
// Create a test server which uses the xDS server credentials created above
|
||||
// to perform TLS handshake on incoming connections.
|
||||
ts := newTestServerWithHandshakeFunc(func(rawConn net.Conn) handshakeResult {
|
||||
hi := NewHandshakeInfo(makeRootProvider(t, "x509/client_ca_cert.pem"), makeIdentityProvider(t, "x509/server2_cert.pem", "x509/server2_key.pem"))
|
||||
hi := xdsinternal.NewHandshakeInfo(makeRootProvider(t, "x509/client_ca_cert.pem"), makeIdentityProvider(t, "x509/server2_cert.pem", "x509/server2_key.pem"))
|
||||
hi.SetRequireClientCert(true)
|
||||
|
||||
// Create a wrapped conn which can return the HandshakeInfo created
|
||||
|
@ -231,7 +232,7 @@ func (s) TestServerCredsHandshakeFailure(t *testing.T) {
|
|||
ts := newTestServerWithHandshakeFunc(func(rawConn net.Conn) handshakeResult {
|
||||
// Create a HandshakeInfo which has a root provider which does not match
|
||||
// the certificate sent by the client.
|
||||
hi := NewHandshakeInfo(makeRootProvider(t, "x509/server_ca_cert.pem"), makeIdentityProvider(t, "x509/client2_cert.pem", "x509/client2_key.pem"))
|
||||
hi := xdsinternal.NewHandshakeInfo(makeRootProvider(t, "x509/server_ca_cert.pem"), makeIdentityProvider(t, "x509/client2_cert.pem", "x509/client2_key.pem"))
|
||||
hi.SetRequireClientCert(true)
|
||||
|
||||
// Create a wrapped conn which can return the HandshakeInfo and
|
||||
|
@ -313,7 +314,7 @@ func (s) TestServerCredsHandshakeSuccess(t *testing.T) {
|
|||
// created above to perform TLS handshake on incoming connections.
|
||||
ts := newTestServerWithHandshakeFunc(func(rawConn net.Conn) handshakeResult {
|
||||
// Create a HandshakeInfo with information from the test table.
|
||||
hi := NewHandshakeInfo(test.rootProvider, test.identityProvider)
|
||||
hi := xdsinternal.NewHandshakeInfo(test.rootProvider, test.identityProvider)
|
||||
hi.SetRequireClientCert(test.requireClientCert)
|
||||
|
||||
// Create a wrapped conn which can return the HandshakeInfo and
|
||||
|
@ -390,11 +391,11 @@ func (s) TestServerCredsProviderSwitch(t *testing.T) {
|
|||
// to perform TLS handshake on incoming connections.
|
||||
ts := newTestServerWithHandshakeFunc(func(rawConn net.Conn) handshakeResult {
|
||||
cnt++
|
||||
var hi *HandshakeInfo
|
||||
var hi *xdsinternal.HandshakeInfo
|
||||
if cnt == 1 {
|
||||
// Create a HandshakeInfo which has a root provider which does not match
|
||||
// the certificate sent by the client.
|
||||
hi = NewHandshakeInfo(makeRootProvider(t, "x509/server_ca_cert.pem"), makeIdentityProvider(t, "x509/client2_cert.pem", "x509/client2_key.pem"))
|
||||
hi = xdsinternal.NewHandshakeInfo(makeRootProvider(t, "x509/server_ca_cert.pem"), makeIdentityProvider(t, "x509/client2_cert.pem", "x509/client2_key.pem"))
|
||||
hi.SetRequireClientCert(true)
|
||||
|
||||
// Create a wrapped conn which can return the HandshakeInfo and
|
||||
|
@ -409,7 +410,7 @@ func (s) TestServerCredsProviderSwitch(t *testing.T) {
|
|||
return handshakeResult{}
|
||||
}
|
||||
|
||||
hi = NewHandshakeInfo(makeRootProvider(t, "x509/client_ca_cert.pem"), makeIdentityProvider(t, "x509/server1_cert.pem", "x509/server1_key.pem"))
|
||||
hi = xdsinternal.NewHandshakeInfo(makeRootProvider(t, "x509/client_ca_cert.pem"), makeIdentityProvider(t, "x509/server1_cert.pem", "x509/server1_key.pem"))
|
||||
hi.SetRequireClientCert(true)
|
||||
|
||||
// Create a wrapped conn which can return the HandshakeInfo and
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2020 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package xds contains non-user facing functionality of the xds credentials.
|
||||
package xds
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc/attributes"
|
||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
internal.GetXDSHandshakeInfoForTesting = GetHandshakeInfo
|
||||
}
|
||||
|
||||
// handshakeAttrKey is the type used as the key to store HandshakeInfo in
|
||||
// the Attributes field of resolver.Address.
|
||||
type handshakeAttrKey struct{}
|
||||
|
||||
// SetHandshakeInfo returns a copy of addr in which the Attributes field is
|
||||
// updated with hInfo.
|
||||
func SetHandshakeInfo(addr resolver.Address, hInfo *HandshakeInfo) resolver.Address {
|
||||
addr.Attributes = addr.Attributes.WithValues(handshakeAttrKey{}, hInfo)
|
||||
return addr
|
||||
}
|
||||
|
||||
// GetHandshakeInfo returns a pointer to the HandshakeInfo stored in attr.
|
||||
func GetHandshakeInfo(attr *attributes.Attributes) *HandshakeInfo {
|
||||
v := attr.Value(handshakeAttrKey{})
|
||||
hi, _ := v.(*HandshakeInfo)
|
||||
return hi
|
||||
}
|
||||
|
||||
// HandshakeInfo wraps all the security configuration required by client and
|
||||
// server handshake methods in xds credentials. The xDS implementation will be
|
||||
// responsible for populating these fields.
|
||||
//
|
||||
// Safe for concurrent access.
|
||||
type HandshakeInfo struct {
|
||||
mu sync.Mutex
|
||||
rootProvider certprovider.Provider
|
||||
identityProvider certprovider.Provider
|
||||
acceptedSANs map[string]bool // Only on the client side.
|
||||
requireClientCert bool // Only on server side.
|
||||
}
|
||||
|
||||
// SetRootCertProvider updates the root certificate provider.
|
||||
func (hi *HandshakeInfo) SetRootCertProvider(root certprovider.Provider) {
|
||||
hi.mu.Lock()
|
||||
hi.rootProvider = root
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetIdentityCertProvider updates the identity certificate provider.
|
||||
func (hi *HandshakeInfo) SetIdentityCertProvider(identity certprovider.Provider) {
|
||||
hi.mu.Lock()
|
||||
hi.identityProvider = identity
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetAcceptedSANs updates the list of accepted SANs.
|
||||
func (hi *HandshakeInfo) SetAcceptedSANs(sans []string) {
|
||||
hi.mu.Lock()
|
||||
hi.acceptedSANs = make(map[string]bool, len(sans))
|
||||
for _, san := range sans {
|
||||
hi.acceptedSANs[san] = true
|
||||
}
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetRequireClientCert updates whether a client cert is required during the
|
||||
// ServerHandshake(). A value of true indicates that we are performing mTLS.
|
||||
func (hi *HandshakeInfo) SetRequireClientCert(require bool) {
|
||||
hi.mu.Lock()
|
||||
hi.requireClientCert = require
|
||||
hi.mu.Unlock()
|
||||
}
|
||||
|
||||
// UseFallbackCreds returns true when fallback credentials are to be used based
|
||||
// on the contents of the HandshakeInfo.
|
||||
func (hi *HandshakeInfo) UseFallbackCreds() bool {
|
||||
if hi == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
hi.mu.Lock()
|
||||
defer hi.mu.Unlock()
|
||||
return hi.identityProvider == nil && hi.rootProvider == nil
|
||||
}
|
||||
|
||||
// ClientSideTLSConfig constructs a tls.Config to be used in a client-side
|
||||
// handshake based on the contents of the HandshakeInfo.
|
||||
func (hi *HandshakeInfo) ClientSideTLSConfig(ctx context.Context) (*tls.Config, error) {
|
||||
hi.mu.Lock()
|
||||
// On the client side, rootProvider is mandatory. IdentityProvider is
|
||||
// optional based on whether the client is doing TLS or mTLS.
|
||||
if hi.rootProvider == nil {
|
||||
return nil, errors.New("xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server")
|
||||
}
|
||||
// Since the call to KeyMaterial() can block, we read the providers under
|
||||
// the lock but call the actual function after releasing the lock.
|
||||
rootProv, idProv := hi.rootProvider, hi.identityProvider
|
||||
hi.mu.Unlock()
|
||||
|
||||
// InsecureSkipVerify needs to be set to true because we need to perform
|
||||
// custom verification to check the SAN on the received certificate.
|
||||
// Currently the Go stdlib does complete verification of the cert (which
|
||||
// includes hostname verification) or none. We are forced to go with the
|
||||
// latter and perform the normal cert validation ourselves.
|
||||
cfg := &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
km, err := rootProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.RootCAs = km.Roots
|
||||
|
||||
if idProv != nil {
|
||||
km, err := idProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.Certificates = km.Certs
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// ServerSideTLSConfig constructs a tls.Config to be used in a server-side
|
||||
// handshake based on the contents of the HandshakeInfo.
|
||||
func (hi *HandshakeInfo) ServerSideTLSConfig(ctx context.Context) (*tls.Config, error) {
|
||||
cfg := &tls.Config{ClientAuth: tls.NoClientCert}
|
||||
hi.mu.Lock()
|
||||
// On the server side, identityProvider is mandatory. RootProvider is
|
||||
// optional based on whether the server is doing TLS or mTLS.
|
||||
if hi.identityProvider == nil {
|
||||
return nil, errors.New("xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake. Please check configuration on the management server")
|
||||
}
|
||||
// Since the call to KeyMaterial() can block, we read the providers under
|
||||
// the lock but call the actual function after releasing the lock.
|
||||
rootProv, idProv := hi.rootProvider, hi.identityProvider
|
||||
if hi.requireClientCert {
|
||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
hi.mu.Unlock()
|
||||
|
||||
// identityProvider is mandatory on the server side.
|
||||
km, err := idProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.Certificates = km.Certs
|
||||
|
||||
if rootProv != nil {
|
||||
km, err := rootProv.KeyMaterial(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
|
||||
}
|
||||
cfg.ClientCAs = km.Roots
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// MatchingSANExists returns true if the SAN contained in the passed in
|
||||
// certificate is present in the list of accepted SANs in the HandshakeInfo.
|
||||
//
|
||||
// If the list of accepted SANs in the HandshakeInfo is empty, this function
|
||||
// returns true for all input certificates.
|
||||
func (hi *HandshakeInfo) MatchingSANExists(cert *x509.Certificate) bool {
|
||||
if len(hi.acceptedSANs) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
var sans []string
|
||||
// SANs can be specified in any of these four fields on the parsed cert.
|
||||
sans = append(sans, cert.DNSNames...)
|
||||
sans = append(sans, cert.EmailAddresses...)
|
||||
for _, ip := range cert.IPAddresses {
|
||||
sans = append(sans, ip.String())
|
||||
}
|
||||
for _, uri := range cert.URIs {
|
||||
sans = append(sans, uri.String())
|
||||
}
|
||||
|
||||
hi.mu.Lock()
|
||||
defer hi.mu.Unlock()
|
||||
for _, san := range sans {
|
||||
if hi.acceptedSANs[san] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewHandshakeInfo returns a new instance of HandshakeInfo with the given root
|
||||
// and identity certificate providers.
|
||||
func NewHandshakeInfo(root, identity certprovider.Provider, sans ...string) *HandshakeInfo {
|
||||
acceptedSANs := make(map[string]bool, len(sans))
|
||||
for _, san := range sans {
|
||||
acceptedSANs[san] = true
|
||||
}
|
||||
return &HandshakeInfo{
|
||||
rootProvider: root,
|
||||
identityProvider: identity,
|
||||
acceptedSANs: acceptedSANs,
|
||||
}
|
||||
}
|
|
@ -27,17 +27,16 @@ import (
|
|||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||
"google.golang.org/grpc/credentials/xds"
|
||||
"google.golang.org/grpc/internal/buffer"
|
||||
xdsinternal "google.golang.org/grpc/internal/credentials/xds"
|
||||
"google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/grpcsync"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/serviceconfig"
|
||||
"google.golang.org/grpc/xds/internal/balancer/edsbalancer"
|
||||
"google.golang.org/grpc/xds/internal/client"
|
||||
"google.golang.org/grpc/xds/internal/client/bootstrap"
|
||||
|
||||
xdsclient "google.golang.org/grpc/xds/internal/client"
|
||||
"google.golang.org/grpc/xds/internal/client/bootstrap"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -81,7 +80,7 @@ func (cdsBB) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.
|
|||
updateCh: buffer.NewUnbounded(),
|
||||
closed: grpcsync.NewEvent(),
|
||||
cancelWatch: func() {}, // No-op at this point.
|
||||
xdsHI: xds.NewHandshakeInfo(nil, nil),
|
||||
xdsHI: xdsinternal.NewHandshakeInfo(nil, nil),
|
||||
}
|
||||
b.logger = prefixLogger((b))
|
||||
b.logger.Infof("Created")
|
||||
|
@ -188,7 +187,7 @@ type cdsBalancer struct {
|
|||
// a new provider is to be created.
|
||||
cachedRoot certprovider.Provider
|
||||
cachedIdentity certprovider.Provider
|
||||
xdsHI *xds.HandshakeInfo
|
||||
xdsHI *xdsinternal.HandshakeInfo
|
||||
xdsCredsInUse bool
|
||||
}
|
||||
|
||||
|
@ -506,7 +505,7 @@ type ccWrapper struct {
|
|||
|
||||
// The certificate providers in this HandshakeInfo are updated based on the
|
||||
// received security configuration in the Cluster resource.
|
||||
xdsHI *xds.HandshakeInfo
|
||||
xdsHI *xdsinternal.HandshakeInfo
|
||||
}
|
||||
|
||||
// NewSubConn intercepts NewSubConn() calls from the child policy and adds an
|
||||
|
@ -515,7 +514,7 @@ type ccWrapper struct {
|
|||
func (ccw *ccWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
|
||||
newAddrs := make([]resolver.Address, len(addrs))
|
||||
for i, addr := range addrs {
|
||||
newAddrs[i] = xds.SetHandshakeInfo(addr, ccw.xdsHI)
|
||||
newAddrs[i] = xdsinternal.SetHandshakeInfo(addr, ccw.xdsHI)
|
||||
}
|
||||
return ccw.ClientConn.NewSubConn(newAddrs, opts)
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||
"google.golang.org/grpc/credentials/xds"
|
||||
"google.golang.org/grpc/internal"
|
||||
xdsinternal "google.golang.org/grpc/internal/credentials/xds"
|
||||
"google.golang.org/grpc/internal/testutils"
|
||||
"google.golang.org/grpc/resolver"
|
||||
xdsclient "google.golang.org/grpc/xds/internal/client"
|
||||
|
@ -188,7 +189,7 @@ func makeNewSubConn(ctx context.Context, edsCC balancer.ClientConn, parentCC *xd
|
|||
if got, want := gotAddrs[0].Addr, addrs[0].Addr; got != want {
|
||||
return fmt.Errorf("resolver.Address passed to parent ClientConn has address %q, want %q", got, want)
|
||||
}
|
||||
getHI := internal.GetXDSHandshakeInfoForTesting.(func(attr *attributes.Attributes) *xds.HandshakeInfo)
|
||||
getHI := internal.GetXDSHandshakeInfoForTesting.(func(attr *attributes.Attributes) *xdsinternal.HandshakeInfo)
|
||||
hi := getHI(gotAddrs[0].Attributes)
|
||||
if hi == nil {
|
||||
return errors.New("resolver.Address passed to parent ClientConn doesn't contain attributes")
|
||||
|
|
|
@ -29,9 +29,9 @@ import (
|
|||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/tls/certprovider"
|
||||
"google.golang.org/grpc/credentials/xds"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/internal"
|
||||
xdsinternal "google.golang.org/grpc/internal/credentials/xds"
|
||||
internalgrpclog "google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/grpcsync"
|
||||
xdsclient "google.golang.org/grpc/xds/internal/client"
|
||||
|
@ -210,7 +210,7 @@ func (s *GRPCServer) newListenerWrapper(lis net.Listener) (*listenerWrapper, err
|
|||
lw := &listenerWrapper{
|
||||
Listener: lis,
|
||||
closed: grpcsync.NewEvent(),
|
||||
xdsHI: xds.NewHandshakeInfo(nil, nil),
|
||||
xdsHI: xdsinternal.NewHandshakeInfo(nil, nil),
|
||||
}
|
||||
|
||||
// This is used to notify that a good update has been received and that
|
||||
|
@ -436,7 +436,7 @@ type listenerWrapper struct {
|
|||
cachedIdentity certprovider.Provider
|
||||
|
||||
// Wraps all information required by the xds handshaker.
|
||||
xdsHI *xds.HandshakeInfo
|
||||
xdsHI *xdsinternal.HandshakeInfo
|
||||
}
|
||||
|
||||
// Accept blocks on an Accept() on the underlying listener, and wraps the
|
||||
|
@ -480,7 +480,7 @@ type conn struct {
|
|||
// This is the same HandshakeInfo as stored in the listenerWrapper that
|
||||
// created this conn. The former updates the HandshakeInfo whenever it
|
||||
// receives new security configuration.
|
||||
xdsHI *xds.HandshakeInfo
|
||||
xdsHI *xdsinternal.HandshakeInfo
|
||||
|
||||
// The connection deadline as configured by the grpc.Server on the rawConn
|
||||
// that is returned by a call to Accept(). This is set to the connection
|
||||
|
@ -512,6 +512,6 @@ func (c *conn) GetDeadline() time.Time {
|
|||
|
||||
// XDSHandshakeInfo returns a pointer to the HandshakeInfo stored in conn. This
|
||||
// will be invoked by the ServerHandshake() method of the XdsCredentials.
|
||||
func (c *conn) XDSHandshakeInfo() *xds.HandshakeInfo {
|
||||
func (c *conn) XDSHandshakeInfo() *xdsinternal.HandshakeInfo {
|
||||
return c.xdsHI
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue