Skip to content

Commit 3f74d44

Browse files
committed
[FAB-3359] Mutual TLS support in gossip handshake
This commit enforces mutual TLS in the gossip handshake, and removes the flag that was used to skip mutual TLS. It also adds more test scenarios for the handshake test that give 100% code coverage for the handshake method. Signed-off-by: Yacov Manevich <[email protected]> Change-Id: I69d6e482a1b594b392121b92b437a1ad644c590e Signed-off-by: Yacov Manevich <[email protected]>
1 parent 670be92 commit 3f74d44

File tree

12 files changed

+279
-215
lines changed

12 files changed

+279
-215
lines changed

core/comm/connection.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,11 @@ func (cas *CASupport) GetDeliverServiceCredentials() credentials.TransportCreden
102102

103103
// GetPeerCredentials returns GRPC transport credentials for use by GRPC
104104
// clients which communicate with remote peer endpoints.
105-
func (cas *CASupport) GetPeerCredentials() credentials.TransportCredentials {
105+
func (cas *CASupport) GetPeerCredentials(tlsCert tls.Certificate) credentials.TransportCredentials {
106106
var creds credentials.TransportCredentials
107-
var tlsConfig = &tls.Config{}
107+
var tlsConfig = &tls.Config{
108+
Certificates: []tls.Certificate{tlsCert},
109+
}
108110
var certPool = x509.NewCertPool()
109111
// loop through the orderer CAs
110112
roots, _ := cas.GetServerRootCAs()

core/comm/connection_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424

2525
"github.com/spf13/viper"
2626

27+
"crypto/tls"
28+
2729
"github.com/hyperledger/fabric/core/testutil"
2830
"github.com/stretchr/testify/assert"
2931
"google.golang.org/grpc"
@@ -142,7 +144,7 @@ func TestCASupport(t *testing.T) {
142144
creds := cas.GetDeliverServiceCredentials()
143145
assert.Equal(t, "1.2", creds.Info().SecurityVersion,
144146
"Expected Security version to be 1.2")
145-
creds = cas.GetPeerCredentials()
147+
creds = cas.GetPeerCredentials(tls.Certificate{})
146148
assert.Equal(t, "1.2", creds.Info().SecurityVersion,
147149
"Expected Security version to be 1.2")
148150

@@ -152,7 +154,7 @@ func TestCASupport(t *testing.T) {
152154
creds = cas.GetDeliverServiceCredentials()
153155
assert.Equal(t, "1.2", creds.Info().SecurityVersion,
154156
"Expected Security version to be 1.2")
155-
creds = cas.GetPeerCredentials()
157+
creds = cas.GetPeerCredentials(tls.Certificate{})
156158
assert.Equal(t, "1.2", creds.Info().SecurityVersion,
157159
"Expected Security version to be 1.2")
158160

core/comm/server.go

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func NewGRPCServerFromListener(listener net.Listener, secureConfig SecureServerC
140140
Certificates: certificates,
141141
SessionTicketsDisabled: true,
142142
}
143+
grpcServer.tlsConfig.ClientAuth = tls.RequestClientCert
143144
//checkif client authentication is required
144145
if secureConfig.RequireClientCert {
145146
//require TLS client auth

examples/cluster/config/core.yaml

-3
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,6 @@ peer:
155155
# This is an endpoint that is published to peers outside of the organization.
156156
# If this isn't set, the peer will not be known to other organizations.
157157
externalEndpoint:
158-
# Makes gossip skip verification of remote peer signature when performing
159-
# the authentication handshake with remote peers
160-
skipHandshake: true
161158

162159
# Leader election service configuration
163160
election:

examples/e2e_cli/base/peer-base.yaml

-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ services:
1919
- CORE_PEER_ENDORSER_ENABLED=true
2020
- CORE_PEER_GOSSIP_USELEADERELECTION=true
2121
- CORE_PEER_GOSSIP_ORGLEADER=false
22-
# The following setting skips the gossip handshake since we are
23-
# are not doing mutual TLS
24-
- CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
2522
- CORE_PEER_PROFILE_ENABLED=true
2623
- CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
2724
- CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key

gossip/comm/comm_impl.go

+26-29
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,6 @@ func NewCommInstanceWithServer(port int, idMapper identity.Mapper, peerIdentity
108108
proto.RegisterGossipServer(s, commInst)
109109
}
110110

111-
if viper.GetBool("peer.gossip.skipHandshake") {
112-
commInst.skipHandshake = true
113-
}
114-
115111
return commInst, nil
116112
}
117113

@@ -416,10 +412,11 @@ func (c *commImpl) authenticateRemotePeer(stream stream) (*proto.ConnectionInfo,
416412
var err error
417413
var cMsg *proto.SignedGossipMessage
418414
var signer proto.Signer
415+
useTLS := c.selfCertHash != nil
419416

420-
// If TLS is detected, sign the hash of our cert to bind our TLS cert
421-
// to the gRPC session
422-
if remoteCertHash != nil && c.selfCertHash != nil && !c.skipHandshake {
417+
// If TLS is enabled, sign the connection message in order to bind
418+
// the TLS session to the peer's identity
419+
if useTLS {
423420
signer = func(msg []byte) ([]byte, error) {
424421
return c.idMapper.Sign(msg)
425422
}
@@ -430,50 +427,57 @@ func (c *commImpl) authenticateRemotePeer(stream stream) (*proto.ConnectionInfo,
430427
}
431428
}
432429

430+
// TLS enabled but not detected on other side
431+
if useTLS && len(remoteCertHash) == 0 {
432+
c.logger.Warningf("%s didn't send TLS certificate", remoteAddress)
433+
return nil, errors.New("No TLS certificate")
434+
}
435+
433436
cMsg = c.createConnectionMsg(c.PKIID, c.selfCertHash, c.peerIdentity, signer)
434437

435438
c.logger.Debug("Sending", cMsg, "to", remoteAddress)
436439
stream.Send(cMsg.Envelope)
437440
m, err := readWithTimeout(stream, util.GetDurationOrDefault("peer.gossip.connTimeout", defConnTimeout), remoteAddress)
438441
if err != nil {
439-
err := fmt.Errorf("Failed reading messge from %s, reason: %v", remoteAddress, err)
440-
c.logger.Warning(err)
442+
c.logger.Warningf("Failed reading messge from %s, reason: %v", remoteAddress, err)
441443
return nil, err
442444
}
443445
receivedMsg := m.GetConn()
444446
if receivedMsg == nil {
445-
c.logger.Warning("Expected connection message but got", receivedMsg)
447+
c.logger.Warning("Expected connection message from", remoteAddress, "but got", receivedMsg)
446448
return nil, errors.New("Wrong type")
447449
}
448450

449451
if receivedMsg.PkiId == nil {
450-
c.logger.Warning("%s didn't send a pkiID")
451-
return nil, fmt.Errorf("%s didn't send a pkiID", remoteAddress)
452+
c.logger.Warning("%s didn't send a pkiID", remoteAddress)
453+
return nil, errors.New("No PKI-ID")
452454
}
453455

454456
c.logger.Debug("Received", receivedMsg, "from", remoteAddress)
455-
err = c.idMapper.Put(receivedMsg.PkiId, receivedMsg.Cert)
457+
err = c.idMapper.Put(receivedMsg.PkiId, receivedMsg.Identity)
456458
if err != nil {
457459
c.logger.Warning("Identity store rejected", remoteAddress, ":", err)
458460
return nil, err
459461
}
460462

461463
connInfo := &proto.ConnectionInfo{
462464
ID: receivedMsg.PkiId,
463-
Identity: receivedMsg.Cert,
465+
Identity: receivedMsg.Identity,
464466
Endpoint: remoteAddress,
465467
}
466468

467469
// if TLS is enabled and detected, verify remote peer
468-
if remoteCertHash != nil && c.selfCertHash != nil && !c.skipHandshake {
469-
if !bytes.Equal(remoteCertHash, receivedMsg.Hash) {
470-
return nil, fmt.Errorf("Expected %v in remote hash, but got %v", remoteCertHash, receivedMsg.Hash)
470+
if useTLS {
471+
// If the remote peer sent its TLS certificate, make sure it actually matches the TLS cert
472+
// that the peer used.
473+
if !bytes.Equal(remoteCertHash, receivedMsg.TlsCertHash) {
474+
return nil, fmt.Errorf("Expected %v in remote hash of TLS cert, but got %v", remoteCertHash, receivedMsg.TlsCertHash)
471475
}
472476
verifier := func(peerIdentity []byte, signature, message []byte) error {
473477
pkiID := c.idMapper.GetPKIidOfCert(api.PeerIdentityType(peerIdentity))
474478
return c.idMapper.Verify(pkiID, signature, message)
475479
}
476-
err = m.Verify(receivedMsg.Cert, verifier)
480+
err = m.Verify(receivedMsg.Identity, verifier)
477481
if err != nil {
478482
c.logger.Error("Failed verifying signature from", remoteAddress, ":", err)
479483
return nil, err
@@ -484,13 +488,6 @@ func (c *commImpl) authenticateRemotePeer(stream stream) (*proto.ConnectionInfo,
484488
}
485489
}
486490

487-
// TLS enabled but not detected on other side, and we're not configured to skip handshake verification
488-
if remoteCertHash == nil && c.selfCertHash != nil && !c.skipHandshake {
489-
err = fmt.Errorf("Remote peer %s didn't send TLS certificate", remoteAddress)
490-
c.logger.Warning(err)
491-
return nil, err
492-
}
493-
494491
c.logger.Debug("Authenticated", remoteAddress)
495492

496493
return connInfo, nil
@@ -583,15 +580,15 @@ func readWithTimeout(stream interface{}, timeout time.Duration, address string)
583580
}
584581
}
585582

586-
func (c *commImpl) createConnectionMsg(pkiID common.PKIidType, hash []byte, cert api.PeerIdentityType, signer proto.Signer) *proto.SignedGossipMessage {
583+
func (c *commImpl) createConnectionMsg(pkiID common.PKIidType, certHash []byte, cert api.PeerIdentityType, signer proto.Signer) *proto.SignedGossipMessage {
587584
m := &proto.GossipMessage{
588585
Tag: proto.GossipMessage_EMPTY,
589586
Nonce: 0,
590587
Content: &proto.GossipMessage_Conn{
591588
Conn: &proto.ConnEstablish{
592-
Hash: hash,
593-
Cert: cert,
594-
PkiId: pkiID,
589+
TlsCertHash: certHash,
590+
Identity: cert,
591+
PkiId: pkiID,
595592
},
596593
},
597594
}

0 commit comments

Comments
 (0)