一、背景
在日常生產中最常用使用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位來進行匹配驗證權限。
看到了授權需要的模式是secheme🆔perm形式,下面就說說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中的
<zkclient_version>0.2</zkclient_version>
改成
<zkclient_version>0.8</zkclient_version>
- 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" username="admin" password="admin" />
或
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" username="admin" password="admin" />
或
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" username="admin" password="admin" />
或
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添加權限控制啦。