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 }