链客,专为开发者而生,有问必答!
此文章来自区块链技术社区,未经允许拒绝转载。
超级账本(Hyperledger Fabric)之权限管理浅析
超级账本是联盟链的代表,而其相对于共链(例如比特币,以太坊)一个较大的区别在于其强大的角色管理和权限把控上,本文主要介绍其角色管理和权限把控的一些核心机制。
证书生成
证书认证是超级账本中权限管理体系的最基础的机制,而所有证书的生成超级账本提供了一个工具cryptogen用于根据配置文件生成一套证书,并且将这些证书组织为peer和orderer两个核心组件可以直接使用的形式。
配置文件
Cryptogen的配置文件demo如下
#################################################################
OrdererOrgs: #用于orderer 组件使用的组织信息
Domain: example.com #orderer 的域名后缀
CA: #CA信息,不用关注,照着填写即可
Country: US
Province: California
Locality: San Francisco
Specs: #orderer域名前缀,和Domain一起组成了域名用于tls认证使用
PeerOrgs: #用于peer 组件使用的组织信息
Domain: org1.example.com #组织域名
CA:
Country: US
Province: California
Locality: San Francisco
Template: #组织内参与个体的个数
Count: 2
Users: #组织内除了管理员用户外的其他用户个数
Count: 1
Domain: org2.example.com
CA:
Country: US
Province: California
Locality: San Francisco
Template:
Count: 2
Users:
Count: 1
###########################################################################
上面的配置文件定义了一个orderer和两个组织(org1和org2),而每一个组织有两个参与方(template/count=2),同时每一个组织有一个普通用户(users/count=1)和一个管理员。其中域名的信息是为了生成tls证书,不用太关注。
生成工具
将上面的配置文件另存为crypto-config.yaml,然后运行
./cryptogen generate --config=./crypto-config.yaml --output ./crypto-config
其中cryptogen是超级账本源码自带的一个工具,编译方式是找到Fabric的目录,使用命令行:
Make build/bin/cryptogen
编译后,cryptogen存放在Fabric目录的build/bin目录下
参数—config代表了配置文件的路径
参数—output代表了生成的一套证书存放的位置
生成结果
命令运行后生成的目录结构如下
其中ordererOrganizations目录对应的是配置文件中的OrdererOrgs一节,而peerOrganizations目录对应的是PeerOrgs一节。ordererOrganizations/example.com、peerOrganizations/org1.example.com、peerOrganizations/org2.example.com这三个目录分别代表了三个机构,orderer机构一般是虚拟的,或者是超然的,是orderer节点的配置,而每一个机构都有两个根证书,一个用于功能性证书的签发,一个用于tls机制证书的签发,tls机制网上有比较成熟的资料,本文不过多介绍,只关注功能性证书。
每一个组织一般有这些目录
Ca
存放了一个功能性证书的根证书,和它对应的私钥。这个证书用于签发组织内所有非tls证书。
Tlsca
tls类证书的根证书,不关注
Users
代表所有的用户,这个一般是用于通过命令行cli节点连接Fabric网络时证明自己身份的,Admin代表是这个组织的管理员,而User代表的是组织的成员
Msp
存放了管理员证书admincerts,根证书cacerts,tls根证书tlscacerts 三类证书的拷贝,但是只有证书没有私钥,这个目录目前笔者没有发现在实际应用中的用途。
Orderers/peers目录
存放的一般是orderer.example.com或者peer0.org2.example.com
其中msp和tls两个目录直接用于orderer或者对应的peer节点启动的配置。Msp一般存放了功能性证书如下:
Admincerts存放了本peer对应的组织的管理员证书和users下的admin证书是一个,只是这里只有证书没有私钥。这个是用于后面验证访问peer节点的cli(用户使用的命令行)的身份是否是该组织的管理员。
Cacerts 存放了本组织根证书的拷贝,用于验证所有的本组织的证书是否合法。
Signcerts和keyStore两个目录是配套的证书和私钥,用于peer节点对交易做签名,供别的节点(orderer或者别的peer)验证,以证明交易的合法性。这个证书是peer节点独有的,用于证明peer节点(代表这个机器或者节点,区别于user代表的是实际人)的身份。
其中有几个重点需要关注:
1、每一个组织都有一个自己的根证书,各个组织的根证书互相独立。
2、每一个组织有两类实体,一类是代表人的user,一类是代表机器或者节点的peer
3、每一个组织的用户user分成管理员Admin和普通用户
4、Peer节点存放的是根证书、管理员证书、以及代表自己的证书,只有代表自己的证书有私钥,根证书和管理员证书是没有私钥的,仅仅是为了验证别人的身份
证书验证
证书验证
这里的证书验证是指,证明证书本身是否合法,以及使用这个证书签名的数据是否合法两个部分。
首先是证书合法,证书合法需要两个信息一个是证书对应的组织id,一个是证书本身。
验证过程为
其次是验证交易数据是否合法,其实交易包含三部分,一部分就是组织ID+证书,一部分就是交易数据,第三部分是使用证书给交易数据打的签名
交易的验证就是通过证书验证交易数据是否合法,验证过程如下
策略验证
除了证书本身的验证,超级账本还引入了策略验证,策略验证可以认为是多证书验证。是Fabric权限管理的基础。如创建通道需要同时两个组织都同意才可以,也就是需要两个组织的User都签名,那么这个策略就需要验证两个单证书是否合法。
策略分三种
1、n of m策略
这种策略指的是必须符合m个策略中的n个策略策略验证才算通过,n of m 策略可以嵌套 n of m 策略、meta策略、以及原子策略,比如创建链码需要 三个组织中有两个以上组织认可才算通过,就是一个2 of 3 策略。
“Policy”: {
"PolicyType": "1",
"Policy": {
"version": 0,
"rule": {
"nOutOf": {
"n": 1,
"rules": [
{
"signedBy": 0
}
]
}
},
"identities": [
{
"principalClassification": "ROLE",
"principal": "CgpPcmRlcmVyTVNQEAE="
}
]
}
}
这个例子代表了一个1 of n的策略
2、meta策略
这种策略指的是子(组织中的)策略集合必须满足一定规则才可以通过。一般有三种形式:
1)ANY 子策略集合中的任意一个策略满足就为通过
2)MAJORITY 子策略集合中的半数以上策略满足就为通过
3)ALL 子策略集合中的所有策略满足才算通过
“Policy”: {
"PolicyType": "3",
"Policy": {
"subPolicy": "Readers",
"rule": "ANY"
}
}
这个例子代表了,只需要满足一个子组织中的Readers策略就算是通过。
3、原子策略
原子策略是直接对应证书验证的策略,有三种形式
1)ROLE形式,ROLE形式又分为Admin和member两种,前者验证证书是否是某个组织的admin证书,后者只需要检查证书对应的组织id和预期的组织一致就可以
2)IDENTITY形式,代表了证书必须是和预期的证书是一个证书,二进制match
3)ORGANIZATION_UNIT形式,代表证书信息中的org信息必须是预期内的(这个和证书信息本身有关,虽然也是组织但是和上文谈到的组织id不是一个概念)
原子策略一般被嵌套在n of m策略或者meta策略内部使用,也可以单独使用
策略一般通过路径定位,比如channel/Readers 定位到的是通道读权限对应的策略。
系统配置
配置结构
上文中提到的策略等是以配置的形式存放在区块中的,而配置的结构是一个嵌套的结构,每一层叫做一个配置Group,每一个Group包含三个子结构
1、groups 存放的是子group
2、policies 存放的是这个group对应的策略
3、values 存放的是这个group对应的实际配置信息
一个通道的初始区块的json形式如下图
其中values不用过多解释,一般以key value的形式存放了一些配置如下图
其中HashingAlgorithm代表的是系统使用的hash算法是sha256
Policies一般存放Admins、Readers和Writers三种策略
Admins一般代表了管理员权限,Readers一般代表了读权限,Writers代表了写权限
Groups一般是一些子组织,也就是路径定位中的子路径,比如组织1的完整路径为
Channel/Application/Org1MSP
细心的读者可能会发现配置中基本所有的项(无论value policy还是group)都有一个mod_policy的项,这个代表的是修改这个配置需要的权限,一般是一个字符串,比如/Channel/Orderer/Admins代表了一个绝对路径,而"mod_policy":"Admins"代表了一个相对路径,前者代表的是找Channel下的Orderer下的policy的Admins节点来作为最终的策略;后者代表的是在本Group中找到Policy的admins节点作为最终的策略。
上文中提到的三种策略类型对应的json如下
1)n of m策略
2)meta策略
其中sub_policy 中的Readers代表了一个相对路径,代表了用所有子group中的Readers策略作为meta策略的策略集合。如果没有特殊说明就是ANY类型
3)原子策略
通道配置
通道配置一般可以通过如下方式获取
1)获取到通道最新的配置区块
CORE_PEER_LOCALMSPID=“OrdererMSP”
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp
peer channel fetch config newconfig -o orderer.example.com:7050 -c “businesschannel”
其中CORE_PEER_LOCALMSPID和CORE_PEER_MSPCONFIGPATH是代表了组织ID和msp目录的两个环境变量
Newconfig代表了取回来的区块的本地存放文件名
Businesschannel 代表了通道的名称
2)将通道区块转换成json
2.1)./configtxlator start --hostname=“0.0.0.0” --port 7059 运行一个解析器的服务端
2.2)curl -X POST --data-binary @newconfig http://127.0.0.1:7059/protolator/decode/common.Block > ./newconfig.json 解码区块为json,其中newconfig是刚刚取回来本地的配置区块,newconfg.json是要转换后的文件
3)将配置单独拿出来
jq .data.data[0].payload.data.config newconfig.json > newconfig_config.json
其中newconfig.json是第二步中转换的json,newconfig_config.json是纯配置文件
最后得到的配置读者可以自行研究,和上文中的配置结构对应着看会比较清楚
关键策略
经过上文的一些描述,希望读者可以了解到权限管理的基本概念和实现方式,本节主要讲解一些权限中一些重要的权限。另外需要提前解释的是身份证明是通过运行命令行时候配置CORE_PEER_LOCALMSPID和CORE_PEER_MSPCONFIGPATH两个环境变量完成的,一个代表组织ID一个代表证书。
通道创建策略
本策略是在使用channel create命令时需要审核的策略,对应的配置位置是orderer初始区块的channel/Consortiumms/SampleConsortium/Values/ChannelCreationPolicy节点内
这里如果看源码可能会有一个困惑,这个策略是怎么应用的呢?
这是一个巧妙的设计
在func (ml *multiLedger) NewChannelConfig(通过名字搜函数)中有一个设置applicationGroup.ModPolicy = config.ChannelCreationPolicyKey 也就是修改application需要有ChannelCreationPolicyKey权限,而从systemchannel也就是系统初始区块构造出来的配置是没有applicationGroup的
但是通道创建的时候会有对application修改的writeset(创建通道其实就是对系统配置的修改,然后拼装成了一个新的配置区块)
Write Set:
{
"Channel": {
"Values": {
"Consortium": {
"Version": "0",
"ModPolicy": "",
"Value": {
"name": "SampleConsortium"
}
}
},
也就是创建通道时候要修改application这个group,而这个group的ModPolicy被设置成了channel/Consortiumms/SampleConsortium/Values/ChannelCreationPolicy
加入通道策略
必须是localmsp的admin权限,也就是想加入通道必须配置msp为和peer相同组织的admin证书。
验证代码是在cscc/configure.go invoke函数的
if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp)
policy获取是 localMSPPrincipalGetter ,localMSP代表的就是本组织的,对应的是peer加载的时候配置的msp目录里的admin和根证书
更新配置策略
更新配置策略一般对应的是要更新的配置的ModPolicy对应的策略,如更新Org1的锚点需要Org1的admin证书
链码安装策略
链码安装也就是peer chaincode install 这个命令需要的是本组织(localmsp)的admin权限这个是在代码中写死的
验证在
func (lscc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface)
lscc.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp)
链码部署策略
链码部署有两种权限验证方式
1、通过peer chaincode package的方式打包链码并且安装的话会有一个-i的参数可以指定部署的策略,如:
peer chaincode package -n “testcc” -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -v 1.0 -s -S -i “AND (‘Org1MSP.admin’)” ccpack.out
其中“testcc” 是链码名称,"AND (‘Org1MSP.admin’)"代表了部署需要Org1的admin证书签名
生成的ccpack.out 可以通过peer chaincode install ccpack.out 直接安装
Ps:这个功能只有咋v1.0.6这个tag后的代码才有
2、如果普通的直接通过peer chaincode install 命令安装的则需要通道的admin权限,如:
peer chaincode install -n “testcc” -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
这两种机制的区分是在
func (lscc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chainname string, depSpec []byte, policy []byte, escc []byte, vscc []byte)
func (lscc *LifeCycleSysCC) getInstantiationPolicy
这个调用链路上有体现
链码更新策略
链码更新策略和链码部署策略一致
链码调用策略
链码调用策略写死在代码中的,需要的是通道的writes策略
代码调用链条是
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal)
func (e *Endorser) checkACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader, hdrext *pb.ChaincodeHeaderExtension)
e.policyChecker.CheckPolicy(chdr.ChannelId, policies.ChannelApplicationWriters, signedProp)
->func (p *policyChecker) CheckPolicy(channelID, policyName string, signedProp *pb.SignedProposal)
链码背书策略
链码背书策略是在链码部署的时候指定的一个参数,如:
peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n $CC_NAME -v 1.0 -c ‘{“Args”:[“init”,“a”,“100”,“b”,“200”]}’ -P “OR (‘Org1MSP.member’,‘Org2MSP.member’)”
其中-P "OR (‘Org1MSP.member’,‘Org2MSP.member’)"制定了链码的背书策略,这里也就是需要org1 或者org2 的成员背书
验证的代码链条为
func (v *txValidator) Validate
func (v *vsccValidatorImpl) VSCCValidateTx
func (v *vsccValidatorImpl) GetInfoForValidate
func (vscc *ValidatorOneValidSignature) Invoke
注意这个policy的验证是在区块生成好验证的,为啥不是在执行链码的时候就验证呢?
原因是执行链码时候还没有拿到验证需要的背书签名,只有获取够签名才能验证,而执行链码是在背书的时候进行的,执行后才能拿到背书签名,所以是在验证区块的时候验证,如果验证失败就不入库。这也是Fabric区别于以太坊和比特币的一个很重要的细节,Fabric是无论交易是否合法都写入了区块链,而以太坊和比特币只写入合法的交易。
事件监听策略
事件监听的策略是需要localmsp的member权限,这个也是写死在代码中的,验证的函数是func validateEventMessage(signedEvt *pb.SignedEvent)