github.com/lzy4123/fabric@v2.1.1+incompatible/internal/pkg/comm/client.go (about) 1 /* 2 Copyright IBM Corp. 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/pkg/errors" 16 "google.golang.org/grpc" 17 "google.golang.org/grpc/keepalive" 18 ) 19 20 type GRPCClient struct { 21 // TLS configuration used by the grpc.ClientConn 22 tlsConfig *tls.Config 23 // Options for setting up new connections 24 dialOpts []grpc.DialOption 25 // Duration for which to block while established a new connection 26 timeout time.Duration 27 // Maximum message size the client can receive 28 maxRecvMsgSize int 29 // Maximum message size the client can send 30 maxSendMsgSize int 31 } 32 33 // NewGRPCClient creates a new implementation of GRPCClient given an address 34 // and client configuration 35 func NewGRPCClient(config ClientConfig) (*GRPCClient, error) { 36 client := &GRPCClient{} 37 38 // parse secure options 39 err := client.parseSecureOptions(config.SecOpts) 40 if err != nil { 41 return client, err 42 } 43 44 // keepalive options 45 46 kap := keepalive.ClientParameters{ 47 Time: config.KaOpts.ClientInterval, 48 Timeout: config.KaOpts.ClientTimeout, 49 PermitWithoutStream: true, 50 } 51 // set keepalive 52 client.dialOpts = append(client.dialOpts, grpc.WithKeepaliveParams(kap)) 53 // Unless asynchronous connect is set, make connection establishment blocking. 54 if !config.AsyncConnect { 55 client.dialOpts = append(client.dialOpts, grpc.WithBlock()) 56 client.dialOpts = append(client.dialOpts, grpc.FailOnNonTempDialError(true)) 57 } 58 client.timeout = config.Timeout 59 // set send/recv message size to package defaults 60 client.maxRecvMsgSize = MaxRecvMsgSize 61 client.maxSendMsgSize = MaxSendMsgSize 62 63 return client, nil 64 } 65 66 func (client *GRPCClient) parseSecureOptions(opts SecureOptions) error { 67 // if TLS is not enabled, return 68 if !opts.UseTLS { 69 return nil 70 } 71 72 client.tlsConfig = &tls.Config{ 73 VerifyPeerCertificate: opts.VerifyCertificate, 74 MinVersion: tls.VersionTLS12} // TLS 1.2 only 75 if len(opts.ServerRootCAs) > 0 { 76 client.tlsConfig.RootCAs = x509.NewCertPool() 77 for _, certBytes := range opts.ServerRootCAs { 78 err := AddPemToCertPool(certBytes, client.tlsConfig.RootCAs) 79 if err != nil { 80 commLogger.Debugf("error adding root certificate: %v", err) 81 return errors.WithMessage(err, 82 "error adding root certificate") 83 } 84 } 85 } 86 if opts.RequireClientCert { 87 // make sure we have both Key and Certificate 88 if opts.Key != nil && 89 opts.Certificate != nil { 90 cert, err := tls.X509KeyPair(opts.Certificate, 91 opts.Key) 92 if err != nil { 93 return errors.WithMessage(err, "failed to "+ 94 "load client certificate") 95 } 96 client.tlsConfig.Certificates = append( 97 client.tlsConfig.Certificates, cert) 98 } else { 99 return errors.New("both Key and Certificate " + 100 "are required when using mutual TLS") 101 } 102 } 103 104 if opts.TimeShift > 0 { 105 client.tlsConfig.Time = func() time.Time { 106 return time.Now().Add((-1) * opts.TimeShift) 107 } 108 } 109 110 return nil 111 } 112 113 // Certificate returns the tls.Certificate used to make TLS connections 114 // when client certificates are required by the server 115 func (client *GRPCClient) Certificate() tls.Certificate { 116 cert := tls.Certificate{} 117 if client.tlsConfig != nil && len(client.tlsConfig.Certificates) > 0 { 118 cert = client.tlsConfig.Certificates[0] 119 } 120 return cert 121 } 122 123 // TLSEnabled is a flag indicating whether to use TLS for client 124 // connections 125 func (client *GRPCClient) TLSEnabled() bool { 126 return client.tlsConfig != nil 127 } 128 129 // MutualTLSRequired is a flag indicating whether the client 130 // must send a certificate when making TLS connections 131 func (client *GRPCClient) MutualTLSRequired() bool { 132 return client.tlsConfig != nil && 133 len(client.tlsConfig.Certificates) > 0 134 } 135 136 // SetMaxRecvMsgSize sets the maximum message size the client can receive 137 func (client *GRPCClient) SetMaxRecvMsgSize(size int) { 138 client.maxRecvMsgSize = size 139 } 140 141 // SetMaxSendMsgSize sets the maximum message size the client can send 142 func (client *GRPCClient) SetMaxSendMsgSize(size int) { 143 client.maxSendMsgSize = size 144 } 145 146 // SetServerRootCAs sets the list of authorities used to verify server 147 // certificates based on a list of PEM-encoded X509 certificate authorities 148 func (client *GRPCClient) SetServerRootCAs(serverRoots [][]byte) error { 149 150 // NOTE: if no serverRoots are specified, the current cert pool will be 151 // replaced with an empty one 152 certPool := x509.NewCertPool() 153 for _, root := range serverRoots { 154 err := AddPemToCertPool(root, certPool) 155 if err != nil { 156 return errors.WithMessage(err, "error adding root certificate") 157 } 158 } 159 client.tlsConfig.RootCAs = certPool 160 return nil 161 } 162 163 type TLSOption func(tlsConfig *tls.Config) 164 165 func ServerNameOverride(name string) TLSOption { 166 return func(tlsConfig *tls.Config) { 167 tlsConfig.ServerName = name 168 } 169 } 170 171 func CertPoolOverride(pool *x509.CertPool) TLSOption { 172 return func(tlsConfig *tls.Config) { 173 tlsConfig.RootCAs = pool 174 } 175 } 176 177 // NewConnection returns a grpc.ClientConn for the target address and 178 // overrides the server name used to verify the hostname on the 179 // certificate returned by a server when using TLS 180 func (client *GRPCClient) NewConnection(address string, tlsOptions ...TLSOption) (*grpc.ClientConn, error) { 181 182 var dialOpts []grpc.DialOption 183 dialOpts = append(dialOpts, client.dialOpts...) 184 185 // set transport credentials and max send/recv message sizes 186 // immediately before creating a connection in order to allow 187 // SetServerRootCAs / SetMaxRecvMsgSize / SetMaxSendMsgSize 188 // to take effect on a per connection basis 189 if client.tlsConfig != nil { 190 dialOpts = append(dialOpts, grpc.WithTransportCredentials( 191 &DynamicClientCredentials{ 192 TLSConfig: client.tlsConfig, 193 TLSOptions: tlsOptions, 194 }, 195 )) 196 } else { 197 dialOpts = append(dialOpts, grpc.WithInsecure()) 198 } 199 200 dialOpts = append(dialOpts, grpc.WithDefaultCallOptions( 201 grpc.MaxCallRecvMsgSize(client.maxRecvMsgSize), 202 grpc.MaxCallSendMsgSize(client.maxSendMsgSize), 203 )) 204 205 ctx, cancel := context.WithTimeout(context.Background(), client.timeout) 206 defer cancel() 207 conn, err := grpc.DialContext(ctx, address, dialOpts...) 208 if err != nil { 209 return nil, errors.WithMessage(errors.WithStack(err), 210 "failed to create new connection") 211 } 212 return conn, nil 213 }