Fabric通道创建权限策略分析

作者 tinywell 日期 2019-04-01
Fabric通道创建权限策略分析

fabric 平台是一个具备权限管理的区块链平台,这是有别于其他公共区块链平台的一大特点。

fabric 是通过策略(Policy)这种机制来实现对各种操作的权限管控的。本文将针对 fabric 中的通道创建操作的权限控制实现方式进行分析,研究其使用何种策略,以及策略如何验证等。

尝试

根据官方文档,第一次启动 fabric 网络一般会使用BYFN(Build Your First Network),这个例子中包含全套脚本能够自动完成配置生成、证书签发、网络启动、通道创建加入、链码安装实例化、合约调用查询等等所有操作。

分析脚本可得到通道创建相关的操作如下:

1
2
3
4
5
6
7
8
9
10
createChannel() {
setGlobals 0 1

...

peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt
res=$?

...
}

即通过 peer channel create 命令即可完成。与之相关的几个要素分别是:

  • setGlobals 指定了执行这个命令的上下文环境,也就是使用什么用户身份执行这个命令
  • 创建通道所需要的交易配置信息 channel.tx

setGlobals

分析 setGlobals 功能后,命令 setGlobals 0 1 的作用是设置下列环境变量的值:

1
2
3
4
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051

其中有两个要素:

  • 交易执行的用户属于 Org1MSP
  • 交易执行的用户身份为 Admin@org1.example.com

channel.tx

channel.tx是用configtxgen工具依据configtx.yaml 配置的相关Profile 生成,eg:

1
2
3
4
5
6
7
8
9
TwoOrgsChannel:
Consortium: SampleConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
Capabilities:
<<: *ApplicationCapabilities

上述配置包含了以下要素:

  • 此 channel 所关联的Consortium(联盟,相关定义在 orderer 的创始块中)名称
  • 此 channel 的 Application 相关信息,如包含的组织、权限策略等

操作尝试

根据上述理解,我们尝试用多个组织的用户进行通道创建操作,试着探索出这个操作对权限的要求,相关结果如下:

身份 目标 结果
应用组织 admin (Org1MSP) 创建 channel 成功
应用组织 user (Org1MSP) 创建 channel 失败
orderer 组织 admin (OrdererMSP) 创建 channel 失败
非应用组织内组织 user (Org2MSP) 创建 channel 失败
非应用组织内组织 admin (Org2MSP) 创建 channel 失败

非应用组织内组织是指该 channel 的 Applicaiton 内没有包含的组织,但是属于联盟组织成员

从这个尝试可以粗略的得出初步结论:通道创建需要这个 channel 的 Applicaiton 中的组织的 Admin 用户。

源码分析

通道创建交易实际上是通过 orderer 的Broadcast 接口发送一个配置更新交易,当配置更新的目标 channel 在 orderer 不存在的时候,orderer 就认为这是一个通道创建交易。根据官方文档的说明,通道创建过程如下:

  1. orderer 通过检查配置信息中的顶层的 Consortium 项来确定这个通道创建请求在那个联盟内执行;
  2. orderer 会检查配置信息的 Application 组内的组织是否是所绑定的联盟的组织的子集。并且配置中Application 组的 Version 需要为 1
  3. orderer 会检查联盟内的组织成员和所创建的通道的Application 组织成员是否都为空;(创建通道允许两者都为空,用于测试目的)
  4. ☆orderer 创建一个配置模版,这个模版的Orderer 组配置取自系统通道的 orderer 组配置 ,然后采用通道创建请求配置中的 Application 成员来创建模版的 Applicaiton 组配置,并将它的 mod_policy 修改为 ChannelCreationPolicy - 这个策略来自系统通道配置的 Consortium 内;
  5. orderer 将通道创建请求的配置追加应用到这个模版配置上(类似一次配置更新),由于这个更新需要修改Application 组这个配置项(它的Version1),所以更新动作会需要核验 ChannelCreationPolicy 策略。如果通道创建请求中还包含对其他配置项的修改,那么对应项的 mod_policy 也会被核验;
  6. 新的配置块生成后被封装成一个配置交易发给到系统通道进行排序,排序完成后通道就创建成功了。

这个过程对应的主要源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
func (dt *DefaultTemplator) NewChannelConfig(envConfigUpdate *cb.Envelope) (channelconfig.Resources, error) {

... // 解包

// 检查 Application 组配置项的 Version
if uv := configUpdate.WriteSet.Groups[channelconfig.ApplicationGroupKey].Version; uv != 1 {
return nil, fmt.Errorf("Config update for channel creation does not set application group version to 1, was %d", uv)
}

...
// 从系统配置中取 ChannelCreationPolicy 给新的 applicationGroup 模版
applicationGroup := cb.NewConfigGroup()
consortiumsConfig, ok := dt.support.ConsortiumsConfig()
if !ok {
return nil, fmt.Errorf("The ordering system channel does not appear to support creating channels")
}

consortiumConf, ok := consortiumsConfig.Consortiums()[consortium.Name]
if !ok {
return nil, fmt.Errorf("Unknown consortium name: %s", consortium.Name)
}

applicationGroup.Policies[channelconfig.ChannelCreationPolicyKey] = &cb.ConfigPolicy{
Policy: consortiumConf.ChannelCreationPolicy(),
}
applicationGroup.ModPolicy = channelconfig.ChannelCreationPolicyKey

// Get the current system channel config
systemChannelGroup := dt.support.ConfigtxValidator().ConfigProto().ChannelGroup

// 如果 consortium group 没有组织成员, 允许 channel 创建中的 Application 中不包含组织成员
// 如果 consortium group 中有任何组织成员, 那么 channel 创建中的 Applicaiton 组织成员至少要有其中一个
if len(systemChannelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups[consortium.Name].Groups) > 0 &&
len(configUpdate.WriteSet.Groups[channelconfig.ApplicationGroupKey].Groups) == 0 {
return nil, fmt.Errorf("Proposed configuration has no application group members, but consortium contains members")
}

// 检查 channel 创建请求中的组织是否属于联盟组织成员
if len(systemChannelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups[consortium.Name].Groups) > 0 {
for orgName := range configUpdate.WriteSet.Groups[channelconfig.ApplicationGroupKey].Groups {
consortiumGroup, ok := systemChannelGroup.Groups[channelconfig.ConsortiumsGroupKey].Groups[consortium.Name].Groups[orgName]
if !ok {
return nil, fmt.Errorf("Attempted to include a member which is not in the consortium")
}
applicationGroup.Groups[orgName] = proto.Clone(consortiumGroup).(*cb.ConfigGroup)
}
}

channelGroup := cb.NewConfigGroup()

... // 组装新的config

return bundle, nil
}

其中与通道创建策略相关的代码为:

1
2
3
4
applicationGroup.Policies[channelconfig.ChannelCreationPolicyKey] = &cb.ConfigPolicy{
Policy: consortiumConf.ChannelCreationPolicy(),
}
applicationGroup.ModPolicy = channelconfig.ChannelCreationPolicyKey

通过为模版中的 Application 组设置一个修改策略,然后利用创建通道必须修改此项从而需要检查这个策略来实现对通道创建这个动作的权限控制,这是一个很巧妙的设计。

consortiumConf.ChannelCreationPolicy() 这个策略来自系统通道的配置信息中,其根源是启动 orderer 服务所用的创始块,我们解开示例网络中的 genesis.block ,找到其值为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
"config": {
"channel_group": {
"groups": {
"Consortiums": {
"groups": {
"SampleConsortium": {
"groups": {
},
"mod_policy": "/Channel/Orderer/Admins",
"policies": {},
"values": {
"ChannelCreationPolicy": {
"mod_policy": "/Channel/Orderer/Admins",
"value": {
"type": 3,
"value": {
"rule": "ANY",
"sub_policy": "Admins"
}
},
"version": "0"
}
},
...

ChannelCreationPolicy 策略是一个 IMPLICIT_META 类型的策略,值为 ANY Admins,它最终通过子策略 Admins 来进行检查,也就是需要满足 Application 内的任意 Admins 策略即可。Application 中最底层的 Admins 子策略为 Application 组内的各组织的 Admins 策略,例如Org1MSP 的一个默认值如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
"Admins": {
"mod_policy": "Admins",
"policy": {
"type": 1,
"value": {
"identities": [
{
"principal": {
"msp_identifier": "Org1MSP",
"role": "ADMIN"
},
"principal_classification": "ROLE"
}
],
"rule": {
"n_out_of": {
"n": 1,
"rules": [
{
"signed_by": 0
}
]
}
},
"version": 0
}
},
"version": "0"
},