github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/pkg/comm/config.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package comm
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"time"
    14  
    15  	"github.com/hechain20/hechain/common/flogging"
    16  	"github.com/hechain20/hechain/common/metrics"
    17  	"github.com/pkg/errors"
    18  	"google.golang.org/grpc"
    19  	"google.golang.org/grpc/keepalive"
    20  )
    21  
    22  // Configuration defaults
    23  
    24  // Max send and receive bytes for grpc clients and servers
    25  const (
    26  	DefaultMaxRecvMsgSize = 100 * 1024 * 1024
    27  	DefaultMaxSendMsgSize = 100 * 1024 * 1024
    28  )
    29  
    30  var (
    31  	// Default peer keepalive options
    32  	DefaultKeepaliveOptions = KeepaliveOptions{
    33  		ClientInterval:    time.Duration(1) * time.Minute,  // 1 min
    34  		ClientTimeout:     time.Duration(20) * time.Second, // 20 sec - gRPC default
    35  		ServerInterval:    time.Duration(2) * time.Hour,    // 2 hours - gRPC default
    36  		ServerTimeout:     time.Duration(20) * time.Second, // 20 sec - gRPC default
    37  		ServerMinInterval: time.Duration(1) * time.Minute,  // match ClientInterval
    38  	}
    39  	// strong TLS cipher suites
    40  	DefaultTLSCipherSuites = []uint16{
    41  		tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
    42  		tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
    43  		tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
    44  		tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
    45  		tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
    46  		tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
    47  	}
    48  	// default connection timeout
    49  	DefaultConnectionTimeout = 5 * time.Second
    50  )
    51  
    52  // ServerConfig defines the parameters for configuring a GRPCServer instance
    53  type ServerConfig struct {
    54  	// ConnectionTimeout specifies the timeout for connection establishment
    55  	// for all new connections
    56  	ConnectionTimeout time.Duration
    57  	// SecOpts defines the security parameters
    58  	SecOpts SecureOptions
    59  	// KaOpts defines the keepalive parameters
    60  	KaOpts KeepaliveOptions
    61  	// StreamInterceptors specifies a list of interceptors to apply to
    62  	// streaming RPCs.  They are executed in order.
    63  	StreamInterceptors []grpc.StreamServerInterceptor
    64  	// UnaryInterceptors specifies a list of interceptors to apply to unary
    65  	// RPCs.  They are executed in order.
    66  	UnaryInterceptors []grpc.UnaryServerInterceptor
    67  	// Logger specifies the logger the server will use
    68  	Logger *flogging.FabricLogger
    69  	// HealthCheckEnabled enables the gRPC Health Checking Protocol for the server
    70  	HealthCheckEnabled bool
    71  	// ServerStatsHandler should be set if metrics on connections are to be reported.
    72  	ServerStatsHandler *ServerStatsHandler
    73  	// Maximum message size the server can receive
    74  	MaxRecvMsgSize int
    75  	// Maximum message size the server can send
    76  	MaxSendMsgSize int
    77  }
    78  
    79  // ClientConfig defines the parameters for configuring a GRPCClient instance
    80  type ClientConfig struct {
    81  	// SecOpts defines the security parameters
    82  	SecOpts SecureOptions
    83  	// KaOpts defines the keepalive parameters
    84  	KaOpts KeepaliveOptions
    85  	// DialTimeout controls how long the client can block when attempting to
    86  	// establish a connection to a server
    87  	DialTimeout time.Duration
    88  	// AsyncConnect makes connection creation non blocking
    89  	AsyncConnect bool
    90  	// Maximum message size the client can receive
    91  	MaxRecvMsgSize int
    92  	// Maximum message size the client can send
    93  	MaxSendMsgSize int
    94  }
    95  
    96  // Convert the ClientConfig to the approriate set of grpc.DialOptions.
    97  func (cc ClientConfig) DialOptions() ([]grpc.DialOption, error) {
    98  	var dialOpts []grpc.DialOption
    99  	dialOpts = append(dialOpts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
   100  		Time:                cc.KaOpts.ClientInterval,
   101  		Timeout:             cc.KaOpts.ClientTimeout,
   102  		PermitWithoutStream: true,
   103  	}))
   104  
   105  	// Unless asynchronous connect is set, make connection establishment blocking.
   106  	if !cc.AsyncConnect {
   107  		dialOpts = append(dialOpts,
   108  			grpc.WithBlock(),
   109  			grpc.FailOnNonTempDialError(true),
   110  		)
   111  	}
   112  	// set send/recv message size to package defaults
   113  	maxRecvMsgSize := DefaultMaxRecvMsgSize
   114  	if cc.MaxRecvMsgSize != 0 {
   115  		maxRecvMsgSize = cc.MaxRecvMsgSize
   116  	}
   117  	maxSendMsgSize := DefaultMaxSendMsgSize
   118  	if cc.MaxSendMsgSize != 0 {
   119  		maxSendMsgSize = cc.MaxSendMsgSize
   120  	}
   121  	dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(
   122  		grpc.MaxCallRecvMsgSize(maxRecvMsgSize),
   123  		grpc.MaxCallSendMsgSize(maxSendMsgSize),
   124  	))
   125  
   126  	tlsConfig, err := cc.SecOpts.TLSConfig()
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	if tlsConfig != nil {
   131  		transportCreds := &DynamicClientCredentials{TLSConfig: tlsConfig}
   132  		dialOpts = append(dialOpts, grpc.WithTransportCredentials(transportCreds))
   133  	} else {
   134  		dialOpts = append(dialOpts, grpc.WithInsecure())
   135  	}
   136  
   137  	return dialOpts, nil
   138  }
   139  
   140  func (cc ClientConfig) Dial(address string) (*grpc.ClientConn, error) {
   141  	dialOpts, err := cc.DialOptions()
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	ctx, cancel := context.WithTimeout(context.Background(), cc.DialTimeout)
   147  	defer cancel()
   148  
   149  	conn, err := grpc.DialContext(ctx, address, dialOpts...)
   150  	if err != nil {
   151  		return nil, errors.Wrap(err, "failed to create new connection")
   152  	}
   153  	return conn, nil
   154  }
   155  
   156  // SecureOptions defines the TLS security parameters for a GRPCServer or
   157  // GRPCClient instance.
   158  type SecureOptions struct {
   159  	// VerifyCertificate, if not nil, is called after normal
   160  	// certificate verification by either a TLS client or server.
   161  	// If it returns a non-nil error, the handshake is aborted and that error results.
   162  	VerifyCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
   163  	// PEM-encoded X509 public key to be used for TLS communication
   164  	Certificate []byte
   165  	// PEM-encoded private key to be used for TLS communication
   166  	Key []byte
   167  	// Set of PEM-encoded X509 certificate authorities used by clients to
   168  	// verify server certificates
   169  	ServerRootCAs [][]byte
   170  	// Set of PEM-encoded X509 certificate authorities used by servers to
   171  	// verify client certificates
   172  	ClientRootCAs [][]byte
   173  	// Whether or not to use TLS for communication
   174  	UseTLS bool
   175  	// Whether or not TLS client must present certificates for authentication
   176  	RequireClientCert bool
   177  	// CipherSuites is a list of supported cipher suites for TLS
   178  	CipherSuites []uint16
   179  	// TimeShift makes TLS handshakes time sampling shift to the past by a given duration
   180  	TimeShift time.Duration
   181  	// ServerNameOverride is used to verify the hostname on the returned certificates. It
   182  	// is also included in the client's handshake to support virtual hosting
   183  	// unless it is an IP address.
   184  	ServerNameOverride string
   185  }
   186  
   187  func (so SecureOptions) TLSConfig() (*tls.Config, error) {
   188  	// if TLS is not enabled, return
   189  	if !so.UseTLS {
   190  		return nil, nil
   191  	}
   192  
   193  	tlsConfig := &tls.Config{
   194  		MinVersion:            tls.VersionTLS12,
   195  		ServerName:            so.ServerNameOverride,
   196  		VerifyPeerCertificate: so.VerifyCertificate,
   197  	}
   198  	if len(so.ServerRootCAs) > 0 {
   199  		tlsConfig.RootCAs = x509.NewCertPool()
   200  		for _, certBytes := range so.ServerRootCAs {
   201  			if !tlsConfig.RootCAs.AppendCertsFromPEM(certBytes) {
   202  				return nil, errors.New("error adding root certificate")
   203  			}
   204  		}
   205  	}
   206  
   207  	if so.RequireClientCert {
   208  		cert, err := so.ClientCertificate()
   209  		if err != nil {
   210  			return nil, errors.WithMessage(err, "failed to load client certificate")
   211  		}
   212  		tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
   213  	}
   214  
   215  	if so.TimeShift > 0 {
   216  		tlsConfig.Time = func() time.Time {
   217  			return time.Now().Add((-1) * so.TimeShift)
   218  		}
   219  	}
   220  
   221  	return tlsConfig, nil
   222  }
   223  
   224  // ClientCertificate returns the client certificate that will be used
   225  // for mutual TLS.
   226  func (so SecureOptions) ClientCertificate() (tls.Certificate, error) {
   227  	if so.Key == nil || so.Certificate == nil {
   228  		return tls.Certificate{}, errors.New("both Key and Certificate are required when using mutual TLS")
   229  	}
   230  	cert, err := tls.X509KeyPair(so.Certificate, so.Key)
   231  	if err != nil {
   232  		return tls.Certificate{}, errors.WithMessage(err, "failed to create key pair")
   233  	}
   234  	return cert, nil
   235  }
   236  
   237  // KeepaliveOptions is used to set the gRPC keepalive settings for both
   238  // clients and servers
   239  type KeepaliveOptions struct {
   240  	// ClientInterval is the duration after which if the client does not see
   241  	// any activity from the server it pings the server to see if it is alive
   242  	ClientInterval time.Duration
   243  	// ClientTimeout is the duration the client waits for a response
   244  	// from the server after sending a ping before closing the connection
   245  	ClientTimeout time.Duration
   246  	// ServerInterval is the duration after which if the server does not see
   247  	// any activity from the client it pings the client to see if it is alive
   248  	ServerInterval time.Duration
   249  	// ServerTimeout is the duration the server waits for a response
   250  	// from the client after sending a ping before closing the connection
   251  	ServerTimeout time.Duration
   252  	// ServerMinInterval is the minimum permitted time between client pings.
   253  	// If clients send pings more frequently, the server will disconnect them
   254  	ServerMinInterval time.Duration
   255  }
   256  
   257  // ServerKeepaliveOptions returns gRPC keepalive options for a server.
   258  func (ka KeepaliveOptions) ServerKeepaliveOptions() []grpc.ServerOption {
   259  	var serverOpts []grpc.ServerOption
   260  	kap := keepalive.ServerParameters{
   261  		Time:    ka.ServerInterval,
   262  		Timeout: ka.ServerTimeout,
   263  	}
   264  	serverOpts = append(serverOpts, grpc.KeepaliveParams(kap))
   265  	kep := keepalive.EnforcementPolicy{
   266  		MinTime: ka.ServerMinInterval,
   267  		// allow keepalive w/o rpc
   268  		PermitWithoutStream: true,
   269  	}
   270  	serverOpts = append(serverOpts, grpc.KeepaliveEnforcementPolicy(kep))
   271  	return serverOpts
   272  }
   273  
   274  // ClientKeepaliveOptions returns gRPC keepalive dial options for clients.
   275  func (ka KeepaliveOptions) ClientKeepaliveOptions() []grpc.DialOption {
   276  	var dialOpts []grpc.DialOption
   277  	kap := keepalive.ClientParameters{
   278  		Time:                ka.ClientInterval,
   279  		Timeout:             ka.ClientTimeout,
   280  		PermitWithoutStream: true,
   281  	}
   282  	dialOpts = append(dialOpts, grpc.WithKeepaliveParams(kap))
   283  	return dialOpts
   284  }
   285  
   286  type Metrics struct {
   287  	// OpenConnCounter keeps track of number of open connections
   288  	OpenConnCounter metrics.Counter
   289  	// ClosedConnCounter keeps track of number connections closed
   290  	ClosedConnCounter metrics.Counter
   291  }