github.com/anjalikarhana/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  }