github.com/lzy4123/fabric@v2.1.1+incompatible/orderer/common/cluster/connections.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cluster 8 9 import ( 10 "bytes" 11 "crypto/x509" 12 "sync" 13 14 "github.com/hyperledger/fabric/common/metrics" 15 "github.com/pkg/errors" 16 "google.golang.org/grpc" 17 ) 18 19 // RemoteVerifier verifies the connection to the remote host 20 type RemoteVerifier func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error 21 22 //go:generate mockery -dir . -name SecureDialer -case underscore -output ./mocks/ 23 24 // SecureDialer connects to a remote address 25 type SecureDialer interface { 26 Dial(address string, verifyFunc RemoteVerifier) (*grpc.ClientConn, error) 27 } 28 29 // ConnectionMapper maps certificates to connections 30 type ConnectionMapper interface { 31 Lookup(cert []byte) (*grpc.ClientConn, bool) 32 Put(cert []byte, conn *grpc.ClientConn) 33 Remove(cert []byte) 34 Size() int 35 } 36 37 // ConnectionStore stores connections to remote nodes 38 type ConnectionStore struct { 39 lock sync.RWMutex 40 Connections ConnectionMapper 41 dialer SecureDialer 42 } 43 44 // NewConnectionStore creates a new ConnectionStore with the given SecureDialer 45 func NewConnectionStore(dialer SecureDialer, tlsConnectionCount metrics.Gauge) *ConnectionStore { 46 connMapping := &ConnectionStore{ 47 Connections: &connMapperReporter{ 48 ConnectionMapper: make(ConnByCertMap), 49 tlsConnectionCountMetrics: tlsConnectionCount, 50 }, 51 dialer: dialer, 52 } 53 return connMapping 54 } 55 56 // verifyHandshake returns a predicate that verifies that the remote node authenticates 57 // itself with the given TLS certificate 58 func (c *ConnectionStore) verifyHandshake(endpoint string, certificate []byte) RemoteVerifier { 59 return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { 60 if bytes.Equal(certificate, rawCerts[0]) { 61 return nil 62 } 63 return errors.Errorf("certificate presented by %s doesn't match any authorized certificate", endpoint) 64 } 65 } 66 67 // Disconnect closes the gRPC connection that is mapped to the given certificate 68 func (c *ConnectionStore) Disconnect(expectedServerCert []byte) { 69 c.lock.Lock() 70 defer c.lock.Unlock() 71 72 conn, connected := c.Connections.Lookup(expectedServerCert) 73 if !connected { 74 return 75 } 76 conn.Close() 77 c.Connections.Remove(expectedServerCert) 78 } 79 80 // Connection obtains a connection to the given endpoint and expects the given server certificate 81 // to be presented by the remote node 82 func (c *ConnectionStore) Connection(endpoint string, expectedServerCert []byte) (*grpc.ClientConn, error) { 83 c.lock.RLock() 84 conn, alreadyConnected := c.Connections.Lookup(expectedServerCert) 85 c.lock.RUnlock() 86 87 if alreadyConnected { 88 return conn, nil 89 } 90 91 // Else, we need to connect to the remote endpoint 92 return c.connect(endpoint, expectedServerCert) 93 } 94 95 // connect connects to the given endpoint and expects the given TLS server certificate 96 // to be presented at the time of authentication 97 func (c *ConnectionStore) connect(endpoint string, expectedServerCert []byte) (*grpc.ClientConn, error) { 98 c.lock.Lock() 99 defer c.lock.Unlock() 100 // Check again to see if some other goroutine has already connected while 101 // we were waiting on the lock 102 conn, alreadyConnected := c.Connections.Lookup(expectedServerCert) 103 if alreadyConnected { 104 return conn, nil 105 } 106 107 v := c.verifyHandshake(endpoint, expectedServerCert) 108 conn, err := c.dialer.Dial(endpoint, v) 109 if err != nil { 110 return nil, err 111 } 112 113 c.Connections.Put(expectedServerCert, conn) 114 return conn, nil 115 } 116 117 type connMapperReporter struct { 118 tlsConnectionCountMetrics metrics.Gauge 119 ConnectionMapper 120 } 121 122 func (cmg *connMapperReporter) Put(cert []byte, conn *grpc.ClientConn) { 123 cmg.ConnectionMapper.Put(cert, conn) 124 cmg.reportSize() 125 } 126 127 func (cmg *connMapperReporter) Remove(cert []byte) { 128 cmg.ConnectionMapper.Remove(cert) 129 cmg.reportSize() 130 } 131 132 func (cmg *connMapperReporter) reportSize() { 133 cmg.tlsConnectionCountMetrics.Set(float64(cmg.ConnectionMapper.Size())) 134 }