...

Source file src/google.golang.org/grpc/credentials/xds/xds.go

Documentation: google.golang.org/grpc/credentials/xds

     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 provides a transport credentials implementation where the
    20  // security configuration is pushed by a management server using xDS APIs.
    21  package xds
    22  
    23  import (
    24  	"context"
    25  	"crypto/tls"
    26  	"crypto/x509"
    27  	"errors"
    28  	"fmt"
    29  	"net"
    30  	"sync/atomic"
    31  	"time"
    32  
    33  	"google.golang.org/grpc/credentials"
    34  	credinternal "google.golang.org/grpc/internal/credentials"
    35  	xdsinternal "google.golang.org/grpc/internal/credentials/xds"
    36  )
    37  
    38  // ClientOptions contains parameters to configure a new client-side xDS
    39  // credentials implementation.
    40  type ClientOptions struct {
    41  	// FallbackCreds specifies the fallback credentials to be used when either
    42  	// the `xds` scheme is not used in the user's dial target or when the
    43  	// management server does not return any security configuration. Attempts to
    44  	// create client credentials without fallback credentials will fail.
    45  	FallbackCreds credentials.TransportCredentials
    46  }
    47  
    48  // NewClientCredentials returns a new client-side transport credentials
    49  // implementation which uses xDS APIs to fetch its security configuration.
    50  func NewClientCredentials(opts ClientOptions) (credentials.TransportCredentials, error) {
    51  	if opts.FallbackCreds == nil {
    52  		return nil, errors.New("missing fallback credentials")
    53  	}
    54  	return &credsImpl{
    55  		isClient: true,
    56  		fallback: opts.FallbackCreds,
    57  	}, nil
    58  }
    59  
    60  // ServerOptions contains parameters to configure a new server-side xDS
    61  // credentials implementation.
    62  type ServerOptions struct {
    63  	// FallbackCreds specifies the fallback credentials to be used when the
    64  	// management server does not return any security configuration. Attempts to
    65  	// create server credentials without fallback credentials will fail.
    66  	FallbackCreds credentials.TransportCredentials
    67  }
    68  
    69  // NewServerCredentials returns a new server-side transport credentials
    70  // implementation which uses xDS APIs to fetch its security configuration.
    71  func NewServerCredentials(opts ServerOptions) (credentials.TransportCredentials, error) {
    72  	if opts.FallbackCreds == nil {
    73  		return nil, errors.New("missing fallback credentials")
    74  	}
    75  	return &credsImpl{
    76  		isClient: false,
    77  		fallback: opts.FallbackCreds,
    78  	}, nil
    79  }
    80  
    81  // credsImpl is an implementation of the credentials.TransportCredentials
    82  // interface which uses xDS APIs to fetch its security configuration.
    83  type credsImpl struct {
    84  	isClient bool
    85  	fallback credentials.TransportCredentials
    86  }
    87  
    88  // ClientHandshake performs the TLS handshake on the client-side.
    89  //
    90  // It looks for the presence of a HandshakeInfo value in the passed in context
    91  // (added using a call to NewContextWithHandshakeInfo()), and retrieves identity
    92  // and root certificates from there. It also retrieves a list of acceptable SANs
    93  // and uses a custom verification function to validate the certificate presented
    94  // by the peer. It uses fallback credentials if no HandshakeInfo is present in
    95  // the passed in context.
    96  func (c *credsImpl) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
    97  	if !c.isClient {
    98  		return nil, nil, errors.New("ClientHandshake() is not supported for server credentials")
    99  	}
   100  
   101  	// The CDS balancer constructs a new HandshakeInfo using a call to
   102  	// NewHandshakeInfo(), and then adds it to the attributes field of the
   103  	// resolver.Address when handling calls to NewSubConn(). The transport layer
   104  	// takes care of shipping these attributes in the context to this handshake
   105  	// function. We first read the credentials.ClientHandshakeInfo type from the
   106  	// context, which contains the attributes added by the CDS balancer. We then
   107  	// read the HandshakeInfo from the attributes to get to the actual data that
   108  	// we need here for the handshake.
   109  	chi := credentials.ClientHandshakeInfoFromContext(ctx)
   110  	// If there are no attributes in the received context or the attributes does
   111  	// not contain a HandshakeInfo, it could either mean that the user did not
   112  	// specify an `xds` scheme in their dial target or that the xDS server did
   113  	// not provide any security configuration. In both of these cases, we use
   114  	// the fallback credentials specified by the user.
   115  	if chi.Attributes == nil {
   116  		return c.fallback.ClientHandshake(ctx, authority, rawConn)
   117  	}
   118  
   119  	uPtr := xdsinternal.GetHandshakeInfo(chi.Attributes)
   120  	hi := (*xdsinternal.HandshakeInfo)(atomic.LoadPointer(uPtr))
   121  	if hi.UseFallbackCreds() {
   122  		return c.fallback.ClientHandshake(ctx, authority, rawConn)
   123  	}
   124  
   125  	// We build the tls.Config with the following values
   126  	// 1. Root certificate as returned by the root provider.
   127  	// 2. Identity certificate as returned by the identity provider. This may be
   128  	//    empty on the client side, if the client is not doing mTLS.
   129  	// 3. InsecureSkipVerify to true. Certificates used in Mesh environments
   130  	//    usually contains the identity of the workload presenting the
   131  	//    certificate as a SAN (instead of a hostname in the CommonName field).
   132  	//    This means that normal certificate verification as done by the
   133  	//    standard library will fail.
   134  	// 4. Key usage to match whether client/server usage.
   135  	// 5. A `VerifyPeerCertificate` function which performs normal peer
   136  	// 	  cert verification using configured roots, and the custom SAN checks.
   137  	cfg, err := hi.ClientSideTLSConfig(ctx)
   138  	if err != nil {
   139  		return nil, nil, err
   140  	}
   141  	cfg.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
   142  		// Parse all raw certificates presented by the peer.
   143  		var certs []*x509.Certificate
   144  		for _, rc := range rawCerts {
   145  			cert, err := x509.ParseCertificate(rc)
   146  			if err != nil {
   147  				return err
   148  			}
   149  			certs = append(certs, cert)
   150  		}
   151  
   152  		// Build the intermediates list and verify that the leaf certificate
   153  		// is signed by one of the root certificates.
   154  		intermediates := x509.NewCertPool()
   155  		for _, cert := range certs[1:] {
   156  			intermediates.AddCert(cert)
   157  		}
   158  		opts := x509.VerifyOptions{
   159  			Roots:         cfg.RootCAs,
   160  			Intermediates: intermediates,
   161  			KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   162  		}
   163  		if _, err := certs[0].Verify(opts); err != nil {
   164  			return err
   165  		}
   166  		// The SANs sent by the MeshCA are encoded as SPIFFE IDs. We need to
   167  		// only look at the SANs on the leaf cert.
   168  		if cert := certs[0]; !hi.MatchingSANExists(cert) {
   169  			// TODO: Print the complete certificate once the x509 package
   170  			// supports a String() method on the Certificate type.
   171  			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)
   172  		}
   173  		return nil
   174  	}
   175  
   176  	// Perform the TLS handshake with the tls.Config that we have. We run the
   177  	// actual Handshake() function in a goroutine because we need to respect the
   178  	// deadline specified on the passed in context, and we need a way to cancel
   179  	// the handshake if the context is cancelled.
   180  	conn := tls.Client(rawConn, cfg)
   181  	errCh := make(chan error, 1)
   182  	go func() {
   183  		errCh <- conn.Handshake()
   184  		close(errCh)
   185  	}()
   186  	select {
   187  	case err := <-errCh:
   188  		if err != nil {
   189  			conn.Close()
   190  			return nil, nil, err
   191  		}
   192  	case <-ctx.Done():
   193  		conn.Close()
   194  		return nil, nil, ctx.Err()
   195  	}
   196  	info := credentials.TLSInfo{
   197  		State: conn.ConnectionState(),
   198  		CommonAuthInfo: credentials.CommonAuthInfo{
   199  			SecurityLevel: credentials.PrivacyAndIntegrity,
   200  		},
   201  		SPIFFEID: credinternal.SPIFFEIDFromState(conn.ConnectionState()),
   202  	}
   203  	return credinternal.WrapSyscallConn(rawConn, conn), info, nil
   204  }
   205  
   206  // ServerHandshake performs the TLS handshake on the server-side.
   207  func (c *credsImpl) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
   208  	if c.isClient {
   209  		return nil, nil, errors.New("ServerHandshake is not supported for client credentials")
   210  	}
   211  
   212  	// An xds-enabled gRPC server wraps the underlying raw net.Conn in a type
   213  	// that provides a way to retrieve `HandshakeInfo`, which contains the
   214  	// certificate providers to be used during the handshake. If the net.Conn
   215  	// passed to this function does not implement this interface, or if the
   216  	// `HandshakeInfo` does not contain the information we are looking for, we
   217  	// delegate the handshake to the fallback credentials.
   218  	hiConn, ok := rawConn.(interface {
   219  		XDSHandshakeInfo() (*xdsinternal.HandshakeInfo, error)
   220  	})
   221  	if !ok {
   222  		return c.fallback.ServerHandshake(rawConn)
   223  	}
   224  	hi, err := hiConn.XDSHandshakeInfo()
   225  	if err != nil {
   226  		return nil, nil, err
   227  	}
   228  	if hi.UseFallbackCreds() {
   229  		return c.fallback.ServerHandshake(rawConn)
   230  	}
   231  
   232  	// An xds-enabled gRPC server is expected to wrap the underlying raw
   233  	// net.Conn in a type which provides a way to retrieve the deadline set on
   234  	// it. If we cannot retrieve the deadline here, we fail (by setting deadline
   235  	// to time.Now()), instead of using a default deadline and possibly taking
   236  	// longer to eventually fail.
   237  	deadline := time.Now()
   238  	if dConn, ok := rawConn.(interface{ GetDeadline() time.Time }); ok {
   239  		deadline = dConn.GetDeadline()
   240  	}
   241  	ctx, cancel := context.WithDeadline(context.Background(), deadline)
   242  	defer cancel()
   243  	cfg, err := hi.ServerSideTLSConfig(ctx)
   244  	if err != nil {
   245  		return nil, nil, err
   246  	}
   247  
   248  	conn := tls.Server(rawConn, cfg)
   249  	if err := conn.Handshake(); err != nil {
   250  		conn.Close()
   251  		return nil, nil, err
   252  	}
   253  	info := credentials.TLSInfo{
   254  		State: conn.ConnectionState(),
   255  		CommonAuthInfo: credentials.CommonAuthInfo{
   256  			SecurityLevel: credentials.PrivacyAndIntegrity,
   257  		},
   258  	}
   259  	info.SPIFFEID = credinternal.SPIFFEIDFromState(conn.ConnectionState())
   260  	return credinternal.WrapSyscallConn(rawConn, conn), info, nil
   261  }
   262  
   263  // Info provides the ProtocolInfo of this TransportCredentials.
   264  func (c *credsImpl) Info() credentials.ProtocolInfo {
   265  	return credentials.ProtocolInfo{SecurityProtocol: "tls"}
   266  }
   267  
   268  // Clone makes a copy of this TransportCredentials.
   269  func (c *credsImpl) Clone() credentials.TransportCredentials {
   270  	clone := *c
   271  	return &clone
   272  }
   273  
   274  func (c *credsImpl) OverrideServerName(_ string) error {
   275  	return errors.New("serverName for peer validation must be configured as a list of acceptable SANs")
   276  }
   277  
   278  // UsesXDS returns true if c uses xDS to fetch security configuration
   279  // used at handshake time, and false otherwise.
   280  func (c *credsImpl) UsesXDS() bool {
   281  	return true
   282  }
   283  

View as plain text