Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xds: client-side security: xDS credentials implementation #3888

Merged
merged 8 commits into from Sep 29, 2020
13 changes: 8 additions & 5 deletions credentials/xds/xds.go
Expand Up @@ -38,6 +38,7 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/tls/certprovider"
credinternal "google.golang.org/grpc/internal/credentials"
"google.golang.org/grpc/resolver"
)

// ClientOptions contains parameters to configure a new client-side xDS
Expand Down Expand Up @@ -73,9 +74,11 @@ type credsImpl struct {
// the Attributes field of resolver.Address.
type handshakeAttrKey struct{}

// SetHandshakeInfo returns a copy of attr which is updated with hInfo.
func SetHandshakeInfo(attr *attributes.Attributes, hInfo *HandshakeInfo) *attributes.Attributes {
return attr.WithValues(handshakeAttrKey{}, hInfo)
// 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be unexported? Does anyone else need access to it? If so that would eliminate the asymmetry of the API (setter takes address, getter takes attributes).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Expand Down Expand Up @@ -128,13 +131,13 @@ func (hi *HandshakeInfo) validate(isClient bool) error {
// On the client side, rootProvider is mandatory. IdentityProvider is
// optional based on whether the client is doing TLS or mTLS.
ZhenLian marked this conversation as resolved.
Show resolved Hide resolved
if isClient && hi.rootProvider == nil {
return errors.New("xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake")
return errors.New("xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server")
}

// On the server side, identityProvider is mandatory. RootProvider is
// optional based on whether the server is doing TLS or mTLS.
if !isClient && hi.identityProvider == nil {
return errors.New("xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake")
return errors.New("xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake. Please check configuration on the management server")
}

return nil
Expand Down
9 changes: 5 additions & 4 deletions credentials/xds/xds_test.go
Expand Up @@ -35,6 +35,7 @@ import (
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/testdata"
)

Expand Down Expand Up @@ -227,13 +228,13 @@ func newTestContextWithHandshakeInfo(parent context.Context, root, identity cert
// similar to what the CDS balancer would do when it intercepts calls to
// NewSubConn().
info := NewHandshakeInfo(root, identity, sans...)
attr := SetHandshakeInfo(nil, info)
addr := 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
// handshaker in these tests, we need to do the same here.
contextWithHandshakeInfo := internal.NewClientHandshakeInfoContext.(func(context.Context, credentials.ClientHandshakeInfo) context.Context)
return contextWithHandshakeInfo(parent, credentials.ClientHandshakeInfo{Attributes: attr})
return contextWithHandshakeInfo(parent, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
}

// compareAuthInfo compares the AuthInfo received on the client side after a
Expand Down Expand Up @@ -503,9 +504,9 @@ func (s) TestClientCredsProviderSwitch(t *testing.T) {
// 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().
attr := SetHandshakeInfo(nil, handshakeInfo)
addr := SetHandshakeInfo(resolver.Address{}, handshakeInfo)
contextWithHandshakeInfo := internal.NewClientHandshakeInfoContext.(func(context.Context, credentials.ClientHandshakeInfo) context.Context)
ctx = contextWithHandshakeInfo(ctx, credentials.ClientHandshakeInfo{Attributes: attr})
ctx = contextWithHandshakeInfo(ctx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
if _, _, err := creds.ClientHandshake(ctx, authority, conn); err == nil {
t.Fatal("ClientHandshake() succeeded when expected to fail")
}
Expand Down