fabric 平台是一个具备权限管理的区块链平台,这是有别于其他公共区块链平台的一大特点。
fabric 是通过策略(Policy)这种机制来实现对各种操作的权限管控的。本文将针对 fabric 中的通道创建操作的权限控制实现方式进行分析,研究其使用何种策略,以及策略如何验证等。
尝试
根据官方文档,第一次启动 fabric 网络一般会使用BYFN
(Build Your First Network),这个例子中包含全套脚本能够自动完成配置生成、证书签发、网络启动、通道创建加入、链码安装实例化、合约调用查询等等所有操作。
分析脚本可得到通道创建相关的操作如下:
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
的作用是设置下列环境变量的值:
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:
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 就认为这是一个通道创建交易。根据官方文档的说明,通道创建过程如下:
- orderer 通过检查配置信息中的顶层的
Consortium
项来确定这个通道创建请求在那个联盟内执行; - orderer 会检查配置信息的
Application
组内的组织是否是所绑定的联盟的组织的子集。并且配置中Application
组的Version
需要为1
; - orderer 会检查联盟内的组织成员和所创建的通道的
Application
组织成员是否都为空;(创建通道允许两者都为空,用于测试目的) - ☆orderer 创建一个配置模版,这个模版的
Orderer
组配置取自系统通道的orderer
组配置 ,然后采用通道创建请求配置中的Application
成员来创建模版的Applicaiton
组配置,并将它的mod_policy
修改为ChannelCreationPolicy
- 这个策略来自系统通道配置的Consortium
内; - orderer 将通道创建请求的配置追加应用到这个模版配置上(类似一次配置更新),由于这个更新需要修改
Application
组这个配置项(它的Version
为1
),所以更新动作会需要核验ChannelCreationPolicy
策略。如果通道创建请求中还包含对其他配置项的修改,那么对应项的mod_policy
也会被核验; - 新的配置块生成后被封装成一个配置交易发给到系统通道进行排序,排序完成后通道就创建成功了。
这个过程对应的主要源码如下:
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
}
其中与通道创建策略相关的代码为:
applicationGroup.Policies[channelconfig.ChannelCreationPolicyKey] = &cb.ConfigPolicy{
Policy: consortiumConf.ChannelCreationPolicy(),
}
applicationGroup.ModPolicy = channelconfig.ChannelCreationPolicyKey
通过为模版中的 Application
组设置一个修改策略,然后利用创建通道必须修改此项从而需要检查这个策略来实现对通道创建这个动作的权限控制,这是一个很巧妙的设计。
consortiumConf.ChannelCreationPolicy()
这个策略来自系统通道的配置信息中,其根源是启动 orderer 服务所用的创始块,我们解开示例网络中的 genesis.block
,找到其值为:
...
"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
的一个默认值如下:
"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"
},
意思为,需要满足 Org1MSP
下的角色为 ADMIN
的身份签名,对应为组织的 Admin 用户。
那么这个 ChannelCreationPolicy
到底从哪儿来的呢?我们通过configtxgen
工具生成了 genesis.block
创始块,跟踪相关源码发现:
func NewConsortiumGroup(conf *genesisconfig.Consortium) (*cb.ConfigGroup, error) {
consortiumGroup := cb.NewConfigGroup()
for _, org := range conf.Organizations {
var err error
// Note, NewOrdererOrgGroup is correct here, as the structure is identical
consortiumGroup.Groups[org.Name], err = NewOrdererOrgGroup(org)
if err != nil {
return nil, errors.Wrap(err, "failed to create consortium org")
}
}
addValue(consortiumGroup, channelconfig.ChannelCreationPolicyValue(policies.ImplicitMetaAnyPolicy(channelconfig.AdminsPolicyKey).Value()), ordererAdminsPolicyName)
consortiumGroup.ModPolicy = ordererAdminsPolicyName
return consortiumGroup, nil
}
这个工具会默认为每一个Consortium Group
增加一个 ImplicitMetaAnyPolicy
类型的 ChannelCreationPolicy
,这个策略是默认写死的。
总结
- 通道创建中
Application
组内的组织必须为所关联的联盟组织成员; - 通道创建权限默认为
Applicaiton
组内任一组织的 Admin 用户; - 通道创建策略来自 orderer 的创始块,通过工具
configtxgen
生成的创始块中,通道创建策略是写死的ANY Admins
,如果需要修改这个策略,需要采用其他方式生成创始块;
参考文档
- https://hyperledger-fabric.readthedocs.io/en/release-1.4/configtx.html#channel-creation
- https://blog.csdn.net/yeasy/article/details/88536882
- https://cloud.tencent.com/developer/article/1337863
欢迎专注我的公众号

网友评论