Fabric自定义插件的开发-Auth插件开发

mac2024-06-21  50

说明:链码开发语言是golang,源码分析是基于v1.4.3版本

系列文章 1、Fabric自定义插件的开发-扩展插件的组织方式 2、Fabric自定义插件的开发-Decorator插件开发 3、Fabric自定义插件的开发-Validators插件开发

Auth插件,可以理解为peer的一个前置防火墙,屏蔽掉一些非法的提案。虽然在链码侧也可以对提案做一些校验,但如果能在最初的入口处拒掉非法请求岂不是更好?本文梳理Auth插件的加载和使用方式,以及分享一个样例。

插件加载

读取插件,在自定义的插件中查找NewFilter函数,且函数格式为:func() auth.Filter,即需要返回一个实现了接口core.handlers.auth.Filter的对象,并将该对象存储在插件库对象registry的成员filters中。

函数:func (r *registry) initAuthPlugin(p *plugin.Plugin) {} 位置:core/handlers/library/registry.go:168

core.handlers.auth.Filter接口定义:

type Filter interface { peer.EndorserServer Init(next peer.EndorserServer) }

peer.EndorserServer接口提供了一个方法:ProcessProposal(context.Context, *SignedProposal) (*ProposalResponse, error),用于处理client提交的提案。 所以,Auth插件对象需要提供两个方法:Init和ProcessProposal。 Fabric对Filter接口,有三个默认实现:expirationCheckFilter、filter和reset,其中reset用在特殊场景中,本文不分析,而其它两个实现是对所有场景都使用的。 所有的实现,都有一个next接口成员,用于承载下一个过滤器对象,而Init就是对这个next进行赋值。

插件使用

同样,peer的插件库单例registry对象提供了Lookup方法给外部使用,以获取对应的插件。 Auth类型插件,都是单例,对所有通道的所有链码都生效。在peer收到client的提案时,就会依次执行插件的ProcessProposal方法。 peer进程,在加载完所有的Auth插件之后,会调用Auth插件的Init函数,把这些插件对象组成一个链,而最后一个插件对象的next接口成员指向最终的背书对象(core.endorser.Endorser)!

函数:func ChainFilters(endorser peer.EndorserServer, filters ...Filter) peer.EndorserServer {} 位置:core/handlers/auth/auth.go:23

即,提案只有经过所有Auth插件校验之后,才会进入最终的背书流程。 而Auth插件的顺序依赖于core.yaml中的配置:

handlers: authFilters: - name: DefaultAuth - name: ExpirationCheck # This filter checks identity x509 certificate expiration

插件开发样例

样例功能,识别提案消息头签名证书中的属性是否包含test字段。fabric支持对证书的属性进行扩展,OID是"1.2.3.4.5.6.7.8.1",也可以自行扩展。

package main import ( "context" "crypto/x509" "encoding/pem" "github.com/gogo/protobuf/proto" "github.com/hyperledger/fabric/common/attrmgr" "github.com/hyperledger/fabric/core/handlers/auth" "github.com/hyperledger/fabric/protos/msp" "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" "github.com/pkg/errors" ) // NewFilter creates a new Filter func NewFilter() auth.Filter { return &identityCheck{} } type identityCheck struct { next peer.EndorserServer } // Init initializes the Filter with the next EndorserServer func (f *identityCheck) Init(next peer.EndorserServer) { f.next = next } // ProcessProposal processes a signed proposal func (f *identityCheck) ProcessProposal(ctx context.Context, signedProp *peer.SignedProposal) (*peer.ProposalResponse, error) { prop, err := utils.GetProposal(signedProp.ProposalBytes) if err != nil { return nil, errors.Wrap(err, "failed parsing proposal") } hdr, err := utils.GetHeader(prop.Header) if err != nil { return nil, errors.Wrap(err, "failed parsing header") } sh, err := utils.GetSignatureHeader(hdr.SignatureHeader) if err != nil { return nil, errors.Wrap(err, "failed parsing signature header") } sID := &msp.SerializedIdentity{} if err := proto.Unmarshal(sh.Creator, sID); err != nil { return nil, errors.Wrap(err, "failed parsing creator") } block, _ := pem.Decode(sID.GetIdBytes()) if block == nil { return nil, errors.New("Expecting a PEM-encoded X509 certificate; PEM block not found") } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, errors.Wrap(err, "failed to parse certificate") } attrs, err := attrmgr.New().GetAttributesFromCert(cert) if err != nil { return nil, errors.WithMessage(err, "failed to get attributes from the transaction invoker's certificate") } if _,ok,_ := attrs.Value("test"); !ok { return nil, errors.New("Invalid certificate") } return f.next.ProcessProposal(ctx, signedProp) } func main() { }

插件的编译使用命令 go build -buildmod=plugin,生成so。 因为有依赖fabric源码,所以编译时,需要先clone一份fabric的代码,且在fabric的目录下进行编译,这样就可以直接用fabric项目的vendor了 这里有个限制,如果你的peer是1.4.0版本,那么在编译so时,也必须使用相同版本的fabric源码,否则会导致so加载失败! 多嘴一句,因为插件so和peer是同一个进程空间,所以,你懂得,peer有的数据,这里都可以获取到~ 修改core.yaml中配置,使插件生效:

authFilters: - name: DefaultAuth - name: ExpirationCheck # This filter checks identity x509 certificate expiration - library: /opt/lib/xx.so

使用此插件后,使用普通证书签名的提案就会被peer拒掉,流程并不会走到链码侧。需要在签发签名证书时,增加指定的证书属性。

最新回复(0)