Skip to content

Commit 844fe2d

Browse files
author
Jason Yellick
committed
[FAB-2773] Restrict the total count of channels
https://jira.hyperledger.org/browse/FAB-2773 There is a risk that a malicious user with channel creation rights could simply create an unlimited number of channels until exhausting the resources of the ordering network, causing it to fail. This CR introduces a new genesis block parameter ChannelRestrictions which for now, contains only max_count. This parameter is checked before accepting a new channel creation request. Note that this must be a genesis parameter so that it is synchronized across the network and all orderers agree to accept or reject channel creation requests deterministically. Change-Id: I582963925e05d50e6bad6d1aa7d2a490c0e45cc1 Signed-off-by: Jason Yellick <[email protected]>
1 parent 6e9229b commit 844fe2d

File tree

13 files changed

+109
-20
lines changed

13 files changed

+109
-20
lines changed

common/config/api.go

+3
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ type Orderer interface {
7777
// This field is only set for the system ordering chain
7878
ChainCreationPolicyNames() []string
7979

80+
// MaxChannelsCount returns the maximum count of channels to allow for an ordering network
81+
MaxChannelsCount() uint64
82+
8083
// KafkaBrokers returns the addresses (IP:port notation) of a set of "bootstrap"
8184
// Kafka brokers, i.e. this is not necessarily the entire set of Kafka brokers
8285
// used for ordering

common/config/orderer.go

+9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ const (
4545
// ChainCreationPolicyNamesKey is the cb.ConfigItem type key name for the ChainCreationPolicyNames message
4646
ChainCreationPolicyNamesKey = "ChainCreationPolicyNames"
4747

48+
// ChannelRestrictions is the key name for the ChannelRestrictions message
49+
ChannelRestrictionsKey = "ChannelRestrictions"
50+
4851
// KafkaBrokersKey is the cb.ConfigItem type key name for the KafkaBrokers message
4952
KafkaBrokersKey = "KafkaBrokers"
5053
)
@@ -57,6 +60,7 @@ type OrdererProtos struct {
5760
ChainCreationPolicyNames *ab.ChainCreationPolicyNames
5861
KafkaBrokers *ab.KafkaBrokers
5962
CreationPolicy *ab.CreationPolicy
63+
ChannelRestrictions *ab.ChannelRestrictions
6064
}
6165

6266
// Config is stores the orderer component configuration
@@ -142,6 +146,11 @@ func (oc *OrdererConfig) KafkaBrokers() []string {
142146
return oc.protos.KafkaBrokers.Brokers
143147
}
144148

149+
// MaxChannelsCount returns the maximum count of channels this orderer supports
150+
func (oc *OrdererConfig) MaxChannelsCount() uint64 {
151+
return oc.protos.ChannelRestrictions.MaxCount
152+
}
153+
145154
func (oc *OrdererConfig) Validate(tx interface{}, groups map[string]ValueProposer) error {
146155
for _, validator := range []func() error{
147156
oc.validateConsensusType,

common/config/orderer_util.go

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ func TemplateChainCreationPolicyNames(names []string) *cb.ConfigGroup {
5151
return ordererConfigGroup(ChainCreationPolicyNamesKey, utils.MarshalOrPanic(&ab.ChainCreationPolicyNames{Names: names}))
5252
}
5353

54+
// TemplateChannelRestrictions creates a config group with ChannelRestrictions specified
55+
func TemplateChannelRestrictions(maxChannels uint64) *cb.ConfigGroup {
56+
return ordererConfigGroup(ChannelRestrictionsKey, utils.MarshalOrPanic(&ab.ChannelRestrictions{MaxCount: maxChannels}))
57+
}
58+
5459
// TemplateKafkaBrokers creates a headerless config item representing the kafka brokers
5560
func TemplateKafkaBrokers(brokers []string) *cb.ConfigGroup {
5661
return ordererConfigGroup(KafkaBrokersKey, utils.MarshalOrPanic(&ab.KafkaBrokers{Brokers: brokers}))

common/configtx/tool/configtx.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ Orderer: &OrdererDefaults
121121
# bytes.
122122
PreferredMaxBytes: 512 KB
123123

124+
# Max Channels is the maximum number of channels to allow on the ordering network
125+
# When set to 0, this implies no maximum number of channels
126+
MaxChannels: 0
127+
124128
Kafka:
125129
# Brokers: A list of Kafka brokers to which the orderer connects.
126130
# NOTE: Use IP:port notation

common/configtx/tool/localconfig/config.go

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ type Orderer struct {
9292
BatchSize BatchSize `yaml:"BatchSize"`
9393
Kafka Kafka `yaml:"Kafka"`
9494
Organizations []*Organization `yaml:"Organizations"`
95+
MaxChannels uint64 `yaml:"MaxChannels"`
9596
}
9697

9798
// BatchSize contains configuration affecting the size of batches

common/configtx/tool/provisional/provisional.go

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ func New(conf *genesisconfig.Profile) Generator {
141141
PreferredMaxBytes: conf.Orderer.BatchSize.PreferredMaxBytes,
142142
}),
143143
config.TemplateBatchTimeout(conf.Orderer.BatchTimeout.String()),
144+
config.TemplateChannelRestrictions(conf.Orderer.MaxChannels),
144145

145146
// Initialize the default Reader/Writer/Admins orderer policies, as well as block validation policy
146147
policies.TemplateImplicitMetaPolicyWithSubPolicy([]string{config.OrdererGroupKey}, BlockValidationPolicyKey, configvaluesmsp.WritersPolicyKey, cb.ImplicitMetaPolicy_ANY),

common/mocks/configvalues/channel/orderer/sharedconfig.go

+7
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ type SharedConfig struct {
3535
IngressPolicyNamesVal []string
3636
// EgressPolicyNamesVal is returned as the result of EgressPolicyNames()
3737
EgressPolicyNamesVal []string
38+
// MaxChannelsCountVal is returns as the result of MaxChannelsCount()
39+
MaxChannelsCountVal uint64
3840
}
3941

4042
// ConsensusType returns the ConsensusTypeVal
@@ -62,6 +64,11 @@ func (scm *SharedConfig) KafkaBrokers() []string {
6264
return scm.KafkaBrokersVal
6365
}
6466

67+
// MaxChannelsCount returns the MaxChannelsCountVal
68+
func (scm *SharedConfig) MaxChannelsCount() uint64 {
69+
return scm.MaxChannelsCountVal
70+
}
71+
6572
// IngressPolicyNames returns the IngressPolicyNamesVal
6673
func (scm *SharedConfig) IngressPolicyNames() []string {
6774
return scm.IngressPolicyNamesVal

orderer/multichain/manager.go

+4
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,7 @@ func (ml *multiLedger) newChain(configtx *cb.Envelope) {
178178

179179
ml.chains = newChains
180180
}
181+
182+
func (ml *multiLedger) channelsCount() int {
183+
return len(ml.chains)
184+
}

orderer/multichain/systemchain.go

+10
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
// Define some internal interfaces for easier mocking
3535
type chainCreator interface {
3636
newChain(configTx *cb.Envelope)
37+
channelsCount() int
3738
}
3839

3940
type limitedSupport interface {
@@ -87,6 +88,15 @@ func (scf *systemChainFilter) Apply(env *cb.Envelope) (filter.Action, filter.Com
8788
return filter.Forward, nil
8889
}
8990

91+
maxChannels := scf.support.SharedConfig().MaxChannelsCount()
92+
if maxChannels > 0 {
93+
// We check for strictly greater than to accomodate the system channel
94+
if uint64(scf.cc.channelsCount()) > maxChannels {
95+
logger.Warningf("Rejecting channel creation because the orderer has reached the maximum number of channels, %d", maxChannels)
96+
return filter.Reject, nil
97+
}
98+
}
99+
90100
configTx := &cb.Envelope{}
91101
err = proto.Unmarshal(msgData.Data, configTx)
92102
if err != nil {

orderer/multichain/systemchain_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ func (mcc *mockChainCreator) newChain(configTx *cb.Envelope) {
6868
mcc.newChains = append(mcc.newChains, configTx)
6969
}
7070

71+
func (mcc *mockChainCreator) channelsCount() int {
72+
return len(mcc.newChains)
73+
}
74+
7175
func TestGoodProposal(t *testing.T) {
7276
newChainID := "NewChainID"
7377

@@ -131,3 +135,25 @@ func TestProposalWithMissingPolicy(t *testing.T) {
131135

132136
assert.EqualValues(t, filter.Reject, action, "Transaction had missing policy")
133137
}
138+
139+
func TestNumChainsExceeded(t *testing.T) {
140+
newChainID := "NewChainID"
141+
142+
mcc := newMockChainCreator()
143+
mcc.ms.msc.ChainCreationPolicyNamesVal = []string{provisional.AcceptAllPolicyKey}
144+
mcc.ms.mpm.Policy = &mockpolicies.Policy{}
145+
mcc.ms.msc.MaxChannelsCountVal = 1
146+
mcc.newChains = make([]*cb.Envelope, 2)
147+
148+
configEnv, err := configtx.NewChainCreationTemplate(provisional.AcceptAllPolicyKey, configtx.NewCompositeTemplate()).Envelope(newChainID)
149+
if err != nil {
150+
t.Fatalf("Error constructing configtx")
151+
}
152+
ingressTx := makeConfigTxFromConfigUpdateEnvelope(newChainID, configEnv)
153+
wrapped := wrapConfigTx(ingressTx)
154+
155+
sysFilter := newSystemChainFilter(mcc.ms, mcc)
156+
action, _ := sysFilter.Apply(wrapped)
157+
158+
assert.EqualValues(t, filter.Reject, action, "Transaction had created too many channels")
159+
}

protos/orderer/ab.pb.go

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

protos/orderer/configuration.pb.go

+33-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

protos/orderer/configuration.proto

+5
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,8 @@ message KafkaBrokers {
7676
// e.g. 127.0.0.1:7050, or localhost:7050 are valid entries
7777
repeated string brokers = 1;
7878
}
79+
80+
// ChannelRestrictions is the mssage which conveys restrictions on channel creation for an orderer
81+
message ChannelRestrictions {
82+
uint64 max_count = 1; // The max count of channels to allow to be created, a value of 0 indicates no limit
83+
}

0 commit comments

Comments
 (0)