From a56549b47aee23e5208d25e5e31fbc9caa03ec8d Mon Sep 17 00:00:00 2001 From: YACOVM Date: Fri, 24 Feb 2017 23:34:12 +0200 Subject: [PATCH] Gossip- warn and abort on invalid join channel event When the MSP-ID of the peer isn't among any of the names of the organizations that are members of a channel, the gossip layer of the peer doesn't see any members of its own org as members of the channel. It doesn't make any sense to have a peer join a channel it isn't a member of, and this scenario confuses the gossip layer of the peer. Therefore, I am adding a check that prevents the gossip layer from joining a channel in such a case, and logs an error. Signed-off-by: Yacov Manevich Change-Id: Ida456f72b1ab6813fbdcfdb0393b8f0eb7b516e7 --- gossip/service/gossip_service.go | 25 ++++++ gossip/service/join_test.go | 144 +++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 gossip/service/join_test.go diff --git a/gossip/service/gossip_service.go b/gossip/service/gossip_service.go index a2f90cc4144..922ac7614d8 100644 --- a/gossip/service/gossip_service.go +++ b/gossip/service/gossip_service.go @@ -79,6 +79,7 @@ type gossipServiceImpl struct { lock sync.RWMutex msgCrypto identity.Mapper peerIdentity []byte + secAdv api.SecurityAdvisor } // This is an implementation of api.JoinChannelMessage. @@ -136,6 +137,7 @@ func InitGossipServiceCustomDeliveryFactory(peerIdentity []byte, endpoint string deliveryFactory: factory, msgCrypto: idMapper, peerIdentity: peerIdentity, + secAdv: secAdv, } }) } @@ -176,6 +178,12 @@ func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committe // configUpdated constructs a joinChannelMessage and sends it to the gossipSvc func (g *gossipServiceImpl) configUpdated(config Config) { + myOrg := string(g.secAdv.OrgByPeerIdentity(api.PeerIdentityType(g.peerIdentity))) + if !g.amIinChannel(myOrg, config) { + logger.Error("Tried joining channel", config.ChainID(), "but our org(", myOrg, "), isn't "+ + "among the orgs of the channel:", orgListFromConfig(config), ", aborting.") + return + } jcm := &joinChannelMessage{seqNum: config.Sequence(), anchorPeers: []api.AnchorPeer{}} for orgID, appOrg := range config.Organizations() { for _, ap := range appOrg.AnchorPeers() { @@ -221,6 +229,23 @@ func (g *gossipServiceImpl) Stop() { } } +func (g *gossipServiceImpl) amIinChannel(myOrg string, config Config) bool { + for _, orgName := range orgListFromConfig(config) { + if orgName == myOrg { + return true + } + } + return false +} + +func orgListFromConfig(config Config) []string { + var orgList []string + for orgName := range config.Organizations() { + orgList = append(orgList, orgName) + } + return orgList +} + type secImpl struct { identity []byte } diff --git a/gossip/service/join_test.go b/gossip/service/join_test.go new file mode 100644 index 00000000000..b0be0bac06d --- /dev/null +++ b/gossip/service/join_test.go @@ -0,0 +1,144 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package service + +import ( + "testing" + "time" + + api2 "github.com/hyperledger/fabric/common/configvalues" + "github.com/hyperledger/fabric/gossip/api" + "github.com/hyperledger/fabric/gossip/comm" + "github.com/hyperledger/fabric/gossip/common" + "github.com/hyperledger/fabric/gossip/discovery" + "github.com/hyperledger/fabric/protos/gossip" + "github.com/hyperledger/fabric/protos/peer" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type secAdvMock struct { +} + +func (s *secAdvMock) OrgByPeerIdentity(identity api.PeerIdentityType) api.OrgIdentityType { + return api.OrgIdentityType(identity) +} + +type gossipMock struct { + mock.Mock +} + +func (*gossipMock) Send(msg *gossip.GossipMessage, peers ...*comm.RemotePeer) { + panic("implement me") +} + +func (*gossipMock) Peers() []discovery.NetworkMember { + panic("implement me") +} + +func (*gossipMock) PeersOfChannel(common.ChainID) []discovery.NetworkMember { + panic("implement me") +} + +func (*gossipMock) UpdateMetadata(metadata []byte) { + panic("implement me") +} + +func (*gossipMock) UpdateChannelMetadata(metadata []byte, chainID common.ChainID) { + panic("implement me") +} + +func (*gossipMock) Gossip(msg *gossip.GossipMessage) { + panic("implement me") +} + +func (*gossipMock) Accept(acceptor common.MessageAcceptor, passThrough bool) (<-chan *gossip.GossipMessage, <-chan gossip.ReceivedMessage) { + panic("implement me") +} + +func (g *gossipMock) JoinChan(joinMsg api.JoinChannelMessage, chainID common.ChainID) { + g.Called() +} + +func (*gossipMock) Stop() { + panic("implement me") +} + +type appOrgMock struct { +} + +func (*appOrgMock) Name() string { + panic("implement me") +} + +func (*appOrgMock) MSPID() string { + panic("implement me") +} + +func (*appOrgMock) AnchorPeers() []*peer.AnchorPeer { + return []*peer.AnchorPeer{{Host: "1.2.3.4", Port: 5611}} +} + +type configMock struct { +} + +func (*configMock) ChainID() string { + return "A" +} + +func (*configMock) Organizations() map[string]api2.ApplicationOrg { + return map[string]api2.ApplicationOrg{ + "Org0": &appOrgMock{}, + } +} + +func (*configMock) Sequence() uint64 { + return 0 +} + +func TestJoinChannelConfig(t *testing.T) { + // Scenarios: The channel we're joining has a single org - Org0 + // but our org ID is actually Org0MSP in the negative path + // and Org0 in the positive path + + failChan := make(chan struct{}, 1) + g1SvcMock := &gossipMock{} + g1SvcMock.On("JoinChan", mock.Anything).Run(func(_ mock.Arguments) { + failChan <- struct{}{} + }) + g1 := &gossipServiceImpl{secAdv: &secAdvMock{}, peerIdentity: api.PeerIdentityType("OrgMSP0"), gossipSvc: g1SvcMock} + g1.configUpdated(&configMock{}) + select { + case <-time.After(time.Second): + case <-failChan: + assert.Fail(t, "Joined a badly configured channel") + } + + succChan := make(chan struct{}, 1) + g2SvcMock := &gossipMock{} + g2SvcMock.On("JoinChan", mock.Anything).Run(func(_ mock.Arguments) { + succChan <- struct{}{} + }) + g2 := &gossipServiceImpl{secAdv: &secAdvMock{}, peerIdentity: api.PeerIdentityType("Org0"), gossipSvc: g2SvcMock} + g2.configUpdated(&configMock{}) + select { + case <-time.After(time.Second): + assert.Fail(t, "Didn't join a channel (should have done so within the time period)") + case <-succChan: + + } +}