ZooKeeper的ACL機制
zookeeper通過ACL機制控制znode節點的訪問權限。
首先介紹下znode的5種操作權限:
CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、刪、改、查、管理權限,這5種權限簡寫為crwda(即:每個單詞的首字符縮寫)
注:這5種權限中,delete是指對子節點的刪除權限,其它4種權限指對自身節點的操作權限
身份的認證有4種方式:
- world:默認方式,相當於全世界都能訪問
- auth:代表已經認證通過的用戶(cli中可以通過addauth digest user:pwd 來添加當前上下文中的授權用戶)
- digest:即用戶名:密碼這種方式認證,這也是業務系統中最常用的,下面解決dubbo認證用的就是這種模式。
- ip:使用Ip地址認證
一般使用[scheme:id:permissions]
來表示acl權限。
我們在zk的客戶端可以進行節點權限的查看和設置。
[zk: localhost:2181(CONNECTED) 3] create /test data Created /test [zk: localhost:2181(CONNECTED) 2] getAcl /test 'world,'anyone : cdrwa [zk: localhost:2181(CONNECTED) 3] addauth digest user:password [zk: localhost:2181(CONNECTED) 4] setAcl /test auth:user:password:cdrwa [zk: localhost:2181(CONNECTED) 5] getAcl /test 'digest,'user:V28q/NynI4JI3Rk54h0r8O5kMug= : cdrwa
從上述操作可以看出,zk新創建的znode默認訪問方式為world。我們通過addauth和setAcl給/test節點設置訪問權限為digest,操作權限為cdrwa,用戶名為user,密碼為password。
另啟zk客戶端,執行ls /test,發現當前用戶已經無法訪問/test節點,提示信息為“Authentication is not valid”。解決方法就是addauth添加認證用戶了,並且必須使用用戶名和密碼明文進行認證。
[zk: localhost:2181(CONNECTED) 0] ls /test Authentication is not valid : /test [zk: localhost:2181(CONNECTED) 4] addauth digest user:password [zk: localhost:2181(CONNECTED) 5] ls /test [] [zk: localhost:2181(CONNECTED) 6] create /test/leaf data Created /test/leaf [zk: localhost:2181(CONNECTED) 7] getAcl /test/leaf 'world,'anyone : cdrwa
addauth添加digest認證用戶user后,即可正常訪問/test節點了。
另外,還有一點需要注意,znode的ACL是相互獨立的。也就是說,任意不同節點可以用不同的acl列表,互不影響,並且ACL是不可被繼承的,子節點並不會繼承父節點的訪問權限,且是節點級別控制的,有些節點可以啟用acl,有些可以不啟用,最典型的比如dubbo啟用、kafka不啟用。
我們在/test下創建leaf節點,可發現,leaf節點的認證方式為world,即任何用戶都有訪問權限。
設置super超級管理員用戶
一旦我們為某一個節點設置了acl,那么其余的未授權的節點是無法訪問或者操作該節點的,那么系統用久了以后,假如忘記了某一個節點的密碼,那么就無法再操作這個節點了,所以需要這個super超級管理員用戶權限,其作用還是很大的。超級用戶只能在啟動服務器的時候添加,且digest必須是密文。
String m = DigestAuthenticationProvider.generateDigest("super:admin"); // 得到哈希值
打開zk目錄下的/bin/zkServer.sh服務器腳本文件,找到如下一行:
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}"
加一個超管的配置項,如下:
"-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs="
然后重啟zkServer.sh即可。
四字命令
ZooKeeper中有一系列的命令可以查看服務器的運行狀態,它們的長度通常都是4個英文字母,因此又被稱之為“四字命令”。
使用方式: echo {command} | nc localhost 2181
需要首先使用yum install nc安裝。
詳細的四字命令介紹可參見https://www.jianshu.com/p/c96c9f8c2433。
分布式服務Dubbo+Zookeeper安全認證:KeeperErrorCode = NoAuth解決
網上搜了一圈,只有https://www.jianshu.com/p/02ada8d1858a和https://www.zhihu.com/question/45720203/answer/140188334提到了相關可行的解決方法。因為我們使用的是內部集成后的dubbo(maven-shade-plugin二次打包的LZ也經常這么干),且已經走的是curator而非zkclient,所以僅僅將dubbo.registry.client改成curator不解決問題。
public CuratorZookeeperClient(URL url) { super(url); Builder builder = CuratorFrameworkFactory.builder().connectString(url.getBackupAddress()).retryPolicy(new RetryNTimes(2147483647, 1000)).connectionTimeoutMs(url.getParameter("timeout", 5000)).sessionTimeoutMs(url.getParameter("session", 60000)); String authority = url.getAuthority(); if (authority != null && authority.length() > 0) { builder = builder.authorization("digest", authority.getBytes()); } this.client = builder.build(); this.client.getConnectionStateListenable().addListener(new ConnectionStateListener() { public void stateChanged(CuratorFramework client, ConnectionState state) { if (state == ConnectionState.LOST) { CuratorZookeeperClient.this.stateChanged(0); } else if (state == ConnectionState.CONNECTED) { CuratorZookeeperClient.this.stateChanged(1); } else if (state == ConnectionState.RECONNECTED) { CuratorZookeeperClient.this.stateChanged(2); } } }); this.client.start(); }
所以簡單的方法就是自己修改CuratorZookeeperClient,將zk的認證用戶名和密碼注入進來,如下:
public CuratorZookeeperClient(URL url) { super(url); String username = null; String password = null; // 加載配置文件 try { ResourceBundle bundle = ResourceBundle.getBundle("application"); username = bundle.getString("rpc.registry.username"); password = bundle.getString("rpc.registry.password"); } catch (Exception e) { // NOP } if (username != null && password != null) { url = url.setUsername(username).setPassword(password); } Builder builder = CuratorFrameworkFactory.builder().connectString(url.getBackupAddress()).retryPolicy(new RetryNTimes(2147483647, 1000)).connectionTimeoutMs(url.getParameter("timeout", 5000)).sessionTimeoutMs(url.getParameter("session", 60000)); String authority = url.getAuthority(); if (authority != null && authority.length() > 0) { builder = builder.authorization("digest", authority.getBytes()); } this.client = builder.build(); this.client.getConnectionStateListenable().addListener(new ConnectionStateListener() { public void stateChanged(CuratorFramework client, ConnectionState state) { if (state == ConnectionState.LOST) { CuratorZookeeperClient.this.stateChanged(0); } else if (state == ConnectionState.CONNECTED) { CuratorZookeeperClient.this.stateChanged(1); } else if (state == ConnectionState.RECONNECTED) { CuratorZookeeperClient.this.stateChanged(2); } } }); this.client.start(); }
dubbo認證是解決了,還有kafka、日常管理用的zooviewer和idea集成的zk插件呢。。。。所以繼續kafka。。
kafka連接zookeeper認證
注:僅僅啟用認證的話,dubbo客戶端即使不配置SASL,也是可以正常訪問的,但是這樣就失去了意義。
這一部分主要參考了https://www.orchome.com/500、https://codeforgeek.com/how-to-set-up-authentication-in-kafka-cluster/以及https://blog.csdn.net/sdksdk0/article/details/95336382。關於SASL和kerberos的詳細介紹,參見:kerberos與sasl入坑指南
除了配置認證外,還可以選擇啟用或者不啟用acl。
客戶端工具
zooinspector,3.4.14之后的版本都支持認證,可以下載使用。