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