一、背景
在日常生产中最常用使用zookeeper作为dubbo注册中心使用,但是经常在使用过程中,由于安全扫描,出现zookeeper未授权访问漏洞,需要对zookeeper节点添加权限,但是dubbo至今没有增加zookeeper权限控制功能,所以需要自己改造源码实现。
二、zookeeper简单介绍
zookeeper是分布式服务架构,主要用来解决分布式应用中经常遇到的一些数据管理问题,比如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
zookeeper上节点最常用的5种操作权限:
Create、Read、Write、Delete、Admin也就是增、删、改、查、管理权限,简写crwda
(这五种权限中,delete是指对子节点删除权限,其他四种权限指本身节点权限)
身份验证有4中方式:
- world:有个单一的ID,anyone,表示任何人。
- auth:不使用任何ID,表示任何通过验证的用户(是通过ZK验证的用户?连接到此ZK服务器的用户?)。
- digest:使用 用户名:密码
字符串生成MD5哈希值作为ACL标识符ID。权限的验证通过直接发送用户名密码字符串的方式完成, - ip:使用客户端主机ip地址作为一个ACL标识符,ACL表达式是以 addr/bits
这种格式表示的。ZK服务器会将addr的前bits位与客户端地址的前bits位来进行匹配验证权限。
看到了授权需要的模式是sechemeperm形式,下面就说说secheme
secheme介绍
- world表示所有。创建节点的默认权限,有唯一的id是anyone授权的时候模式为world:anyone:rwcda
表示所有人都有对这个节点的rwcda权限。这里用的是id,而不是expression。 - auth不需要id。不过这里应该用expression来表示。
- digest使用用户名:密码编码成md5的方式作为访问权限列表id。但是这里id不作为授权语句的一部分,这里也是用expression方式。用户名:密码先进行shal编码后再用base64编码。这个比较恶心。
- host使用用户主机作为访问空着列表的id。但是这里需要注意的是表达式用的主句的后缀即可。举个栗子:如果表达式设置为
corp.com 可以匹配 host1.corp.com 和
host2.corp.com的主机名,但是host1.zookeeper.com则不能匹配。 - ip:跟主机名类似。这里使用客户端的ip地址作为权限访问列表的id。表达式可以用
addr/bits这种方式设置ip白名单。
常用加密命令:
方法一、auth设置权限
- 设置这个权限之前首先需要
addauth digest zookeeper:zookeeper
- 然后再设置auth
setAcl /test auth:zookeeper:zookeeper:crwda
- 检查权限
getAcl /test
- 可以看到权限已经设置成功了。
get /test
方法二、digest设置权限 (这个里面有个大坑)
可以看出比较明显的:
- 设置之前不需要通过addauth添加对应的授权认证,而是直接授权便可。
- 命令行设置了什么,然后在获取对应的权限的时候就会把对应的字符串显式记录下来,而不会进行任何编码,
如上面的zookeeper:zookeeper。这种方式就需要自己去编码了。 - 这也是个比较坑爹的事情,设置密码之前需要把密码先自己进行sha1编码然后吧结果进行base64编码。如果编码的方式错误你将永远失去这个节点的访问权。不编码就访问不了
使用sha1编码和base64编码之后设置digest方式保存起来
之后就可以使用addauth的方式访问啦
使用代码连接创建带权限的zookeeper节点
创建节点数据时:
读取Zookeeper数据时:
三、dubbo如何使用zookeeper作为注册中心
通常使用dubbo一般有四种方式,xml配置、API调用、properties加载、注解配置。使用zookeeper作为注册中心主要配置如下:
- xml schema扩展
- properties配置
- API代码设置
- 注解配置
四、dubbo实现zookeeper权限关联
最近又把dubbo拾起来啦,以前用过dubbo,后来12年阿里巴巴不维护啦,后来随着springcloud的不断崛起,使用dubbo的人越来越少,但是在国内还是有好多dubbo的热爱着,也是因为这样,在2017年的时候阿里将dubbo奉献给Apache社区,后面估计会越来越好。
这个无论是之前阿里维护还是现在放到Apache社区,直到目前为止,dubbo都没做好这块,使用zookeeper加权限连接,其实挺简单的,下面大致讲以下怎么做。
这里做之前先说一下实现dubbo连接加权限的zookeeper有两种方式,这个取决于dubbo连接zookeeper是通过两种客户端连接的,分别是zkclient和curator,而且dubbo提前预留量注册中心变量设置用户名和密码,这里使用的zookeeper加权方式是digest方式,使用用户名和密码方式对zookeeper节点加权限。
这里需要注意的是,自从dubbo转到Apache下面之后,源码改动不少,其中在dubbo
2.6.0以后版本,dubbo将默认使用zkclient访问zookeeper改成了默认使用curator客户端访问。
而且在2.5.4版本之后将com.github.sgroschupf 下面的zkclient 0.1版本改成了
com.101tec 下面的zkclient 0.2版本啦。
1、改造zkclient客户端dubbo源码实现
- 替换zkclient jar包
这里无论是新版本还是旧版本,都需要将zkclient包替换成com.101tec
的0.5以上版本,因为只有0.5以上的版本才支持ACL加权控制。(本人建议使用0.8以上的版本)
如果你是2.5.4以前的版本(包括2.5.4版本,就是12年之后17年之前发布的最新版本)需要将
替换成
(这个之前版本比较麻烦,需要到源码中将所有的pom都需要修改)
如果你是使用2.5.4以后的版本比较简单,只需要将dubbo-parent中的
改成
- dubbo使用zkclient客户端源码解析
这里简单的介绍也一下dubbo连接Zookeeper的实现原理,首先服务的提供者和消费者在RegistryProtocol利用注册中心暴露(export)和引用(refer)服务的时候会根据配置利用Dubbo的SPI机制获取具体注册中心注册器
这里的RegistryFactory是ZookeeperRegistryFactory看如下工厂代码
在这里创建zookeepr注册器ZookeeperRegistry。
ZookeeperTransporter是操作zookeepr的客户端的工厂类,用来创建zookeeper客户端,这里客户端并不是zookeeper源代码的自带的,而是采用第三方工具包,主要来简化对zookeeper的操作,例如用zookeeper做注册中心需要对zookeeper节点添加watcher做反向推送,但是每次回调后节点的watcher都会被删除,这些客户会自动维护了这些watcher,在自动添加到节点上去。
- dubbo源码改造适配zookeeper添加ACL权限
这里又要分版本对待,对dubbo添加zookeeper权限适应于2.2.0-2.5.6版本之间dubbo使用者,2.5.6之后版本添加了ZkClientWrapper类,对zkclient又做成守护线程形式,需要进一步改造,后面也会专门提高2.5.6之后怎么改造的。
适配2.2.0-2.5.6版本之间dubbo源码改造
- 在使用zkClient
连接Zookeeper中最重要的一个类是ZkclientZookeeperClient,Zookeeper的连接、节点创建和节点访问都在这个类中,我们需要重点改造的类。
- ZkclientZookeeperClient在构造函数中创建连接Zookeeper,在连接的时候使用用户名密码连接:
- 将ZkclientZookeeperClient中的“createPersistent(String
path)”和“createEphemeral(String path)”创建节点设置权限:
到目前为止,源码就改动这么多,连接Zookeeper的用户名和密码通过dubbo配置注册中心的时候配置,通过spring加载代URL中。
然后下面就说说具体怎么配置怎么用。
- 改造后的dubbo连接Zookeeper配置
从 2.2.0 版本开始缺省为 zkclient 实现,以提升 zookeeper 客户端的健状性,配置:
或
dubbo.registry.client=zkclient
dubbo.registry.username=admin
dubbo.registry.password=admin
或
zookeeper://10.20.153.10:2181?client=zkclient&username=admin&password=admin
到此直接启动即可,dubbo就可自动对注册dubbo的group节点添加ACL权限。
适配2.5.6以上版本dubbo源码改造
- 添加handleSessionEstablishmentError监控方法
- 重写ZkClientWrapper构造方法
- 针对client.start();设置权限登录
- 修改ZkClientWrapper类中“createPersistent(String
path)”和“createEphemeral(String path)”创建节点设置权限:
到目前为止,源码就改动这么多,连接Zookeeper的用户名和密码通过dubbo配置注册中心的时候配置,通过spring加载代URL中。
然后下面就说说具体怎么配置怎么用。
- 改造后的dubbo连接Zookeeper配置
从 2.2.0 版本开始缺省为 zkclient 实现,以提升 zookeeper 客户端的健状性,配置:
或
dubbo.registry.client=zkclient
dubbo.registry.username=admin
dubbo.registry.password=admin
或
zookeeper://10.20.153.10:2181?client=zkclient&username=admin&password=admin
到此直接启动即可,dubbo就可自动对注册dubbo的group节点添加ACL权限。
2、改造curator客户端dubbo源码实现
改造curator是最简单的方式,因为dubbo使用curator已经考虑到zookeeper加ACL了,只是代码设置不生效罢了,只要稍微动一下即可。
- 修改CuratorZookeeperClient源码
- 改造后的dubbo连接Zookeeper配置
从 2.6.0 版本开始缺省为 curator 实现,以提升 zookeeper 客户端的健状性,配置:
或
dubbo.registry.client=curator
dubbo.registry.username=admin
dubbo.registry.password=admin
或
zookeeper://10.20.153.10:2181?client=curator&username=admin&password=admin
启动之后即可实现对zookeeper添加权限控制啦。