Zookeeper-Access Control List(ACL)

概述

Z K作爲一個分佈式協調框架、內部存儲着一些分佈式系統運行時狀態的元數據。如何有效的保護這些數據的安全、如何做一個比較好的權限控制顯得非常的重要。

ZK 爲我們提供一套完善的 ACL(access control list,訪問控制列表) 權限控制機制來保障數據的安全。

ACL 介紹

我們可以從三個方面來理解 ACL 機制

  • Scheme 權限模式
  • Id 授權對象
  • Permission 權限

通常使用 scheme:id:permission來標誌一個有效的 ACL 信息、我們先來看看我們默認的數據節點裏面的 ACL 數據

getACl /

我們也可以看到他也是分爲三部分的

  • world對應的就是 scheme
  • anyone對應的就是 id
  • cdrwa對應的就是 permission

下面我們就分別介紹它們

權限

  • create:c 數據節點的創建權限、允許授權對象在該數據節點下創建子節點。
  • delete:d 子節點的刪除權限、允許授權對象刪除該數據節點的子節點
  • read:r 數據節點的讀取權限、允許授權對象對該數據節點讀取數據內容和獲取子節點列表信息
  • write:w 數據節點的更新權限、允許授權對象對數據節點的數據內容進行更新
  • admin:a 數據節點的管理權限、允許授權對象對該數據節點進行 ACL 相關的設置操作

權限模式

如果按分類來說、ZK 中其實只有兩種權限模式,一種是基於IP/IP段的,一種是基於賬號密碼的。

但是可以細分爲以下四種

  • IP
  • digest
  • world
  • super

IP

IP 模式可以針對數據節點設置 IP 地址或設置 IP 網段的方式進行配置。

[zk: localhost:2181(CONNECTED) 44] create /acl_ip data ip:127.0.0.1:cdrwa
Created /acl_ip

我們創建了數據節點 acl_ip並且爲這個節點設置了 ACL ,使用的是 IP 這種模式、授權對象就是 127.0.0.1這個 ip,而權限則是五種權限全部都賦予了。我們在另外本機電腦的另一個 zkClient 中訪問該數據節點

[zk: 127.0.0.1:2181(CONNECTED) 21] get /acl_ip
data
cZxid = 0x520
ctime = Sat May 16 13:04:40 CST 2020
mZxid = 0x520
mtime = Sat May 16 13:04:40 CST 2020
pZxid = 0x520
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: 127.0.0.1:2181(CONNECTED) 22] getAcl /acl_ip
'ip,'127.0.0.1
: cdrwa

digest

就是我們常見的賬號密碼模式

username:password

但是對於我們在命令行中、我們輸入的並不是一個原始的密碼、而是需要我們對 username:password進行加密和編碼之後的值。

[zk: localhost:2181(CONNECTED) 46] create /acl_digest data digest:foo:Jfg7TYUBs/6KEtdDWd5OB6bdD2Q=:wrcda
Created /acl_digest
[zk: localhost:2181(CONNECTED) 47] getAcl /acl_digest
'digest,'foo:Jfg7TYUBs/6KEtdDWd5OB6bdD2Q=
: cdrwa

原始的數據是: username 爲 foo, password 爲 true,但是在命令行中輸入的 password 已然不是我們原本的 true 了

原因也很簡單、安全嘛、那它的加密以及編碼的邏輯是啥?

在源代碼中 org.apache.zookeeper.server.auth.DigestAuthenticationProvider#generateDigest

 public static String generateDigest(String idPassword) throws NoSuchAlgorithmException {
        String[] parts = idPassword.split(":", 2);
        byte[] digest = MessageDigest.getInstance("SHA1").digest(idPassword.getBytes());
        return parts[0] + ":" + base64Encode(digest);
    }

可以看到其先對 username:password進行SHA1 的加密、然後再進行 base64 的編碼,最後得出來的就是我們返回的就是我們在命令行中輸入的 foo:Jfg7TYUBs/6KEtdDWd5OB6bdD2Q=這個字符串。

   String idPassword = "foo:true";
   System.out.println(generateDigest(idPassword));
   // 打印結果爲
	 foo:Jfg7TYUBs/6KEtdDWd5OB6bdD2Q=

我們現在在另一個 zkClient 中增加 digest 信息然後訪問這個數據節點

[zk: localhost:2181(CONNECTED) 9] addauth digest foo:true
[zk: localhost:2181(CONNECTED) 10] get /acl_digest        
data
cZxid = 0x521
ctime = Sat May 16 13:41:55 CST 2020
mZxid = 0x521
mtime = Sat May 16 13:41:55 CST 2020
pZxid = 0x521
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0

world

world 是一種最開放的權限控制模式,事實上這種權限控制幾乎沒有任何的作用、數據節點的訪問權限對所有用戶開放,我們默認的就是這種權限模式。這種模式其實就是一種特殊的 digest 模式,只不過它的 id 只有一個 anyone

super

super 就是超級用戶的意思、也是一種特殊的 digest。 在這個模式下、超級用戶可以對任意的數據節點進行任意的操作。

授權對象

  • IP 授權模式下、授權對象就是 ip
  • digest 授權模式下、授權對象就是 username:base64(sha1(username:password))
  • world 授權模式下、只有一個授權對象 anyone
  • super 授權模式下、跟 digest 授權模式一樣

super 授權模式介紹

如何配置一個超級管理員的授權對象呢?

假如我們配置的賬號密碼爲 foo:foo我們可以在啓動 zkServer 的時候加入如下的系統屬性

-Dzookeeper.DigestAuthenticationProvider.superDigest=foo:qllW6iET90npPATKMTxiFSiQ5Ns=

可以在啓動 zkServer 的時候在 idea 的配置中加上這個參數

然後啓動 server 則可

如果我們使用的是已經是官方編譯好的zk、則可以在 bin 目錄下修改 zkServer.sh腳本的內容

nohup "$JAVA" 
"-Dzookeeper.log.dir=${ZOO_LOG_DIR}" 
"-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" 
"-Dzookeeper.DigestAuthenticationProvider.superDigest=foo:qllW6iET90npPATKMTxiFSiQ5Ns=" \

加上我們的系統變量、然後啓動則可

foo:foo 爲 super 授權對象 的 username 和 password

/acl_super 節點的 username 和 password 都是 super 這個字符串

// super:super
[zk: localhost:2181(CONNECTED) 2] create /acl_super data digest:super:gG7s8t3oDEtIqF6DM9LlI/R+9Ss=:wrdca
Created /acl_super
[zk: localhost:2181(CONNECTED) 3] getAcl /acl_super
'digest,'super:gG7s8t3oDEtIqF6DM9LlI/R+9Ss=
: cdrwa
[zk: localhost:2181(CONNECTED) 4] get /acl_super
Authentication is not valid : /acl_super
[zk: localhost:2181(CONNECTED) 5] addauth digest foo:foo
[zk: localhost:2181(CONNECTED) 6] get /acl_super        
data
cZxid = 0x52b
ctime = Sat May 16 15:18:18 CST 2020
mZxid = 0x52b
mtime = Sat May 16 15:18:18 CST 2020
pZxid = 0x52b
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0

super 授權模式驗證部分源碼

    private static final String superDigest = System.getProperty("zookeeper.DigestAuthenticationProvider.superDigest");
 
public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) {
        String id = new String(authData);
        try {
            String digest = generateDigest(id);
            if (digest.equals(superDigest)) {
                cnxn.addAuthInfo(new Id("super", ""));
            }
            cnxn.addAuthInfo(new Id(getScheme(), digest));
            return KeeperException.Code.OK;
        } catch (NoSuchAlgorithmException e) {
            LOG.error("Missing algorithm", e);
        }
        return KeeperException.Code.AUTHFAILED;
    }

我們看到當我們的 digest 等於 superDigest 的時候、就會向 ServerCnxn 中增加多一個 Id 對象

private Set<Id> authInfo = Collections.newSetFromMap(new ConcurrentHashMap<Id, Boolean>());

而在我們訪問節點的時候、觸發 checkACL

org.apache.zookeeper.server.ZooKeeperServer#checkACL

 public void checkACL(ServerCnxn cnxn, List<ACL> acl, int perm, List<Id> ids, String path, List<ACL> setAcls) throws KeeperException.NoAuthException {
        
				// acl 爲空
        if (acl == null || acl.size() == 0) {
            return;
        }
   			// super 授權模式
        for (Id authId : ids) {
            if (authId.getScheme().equals("super")) {
                return;
            }
        }
   
        for (ACL a : acl) {
            Id id = a.getId();
            if ((a.getPerms() & perm) != 0) {
              	// world 授權模式
                if (id.getScheme().equals("world") && id.getId().equals("anyone")) {
                    return;
                }
                ....
                ....
            }
        }
   			// 拋出異常
        throw new KeeperException.NoAuthException();
    }

相關文章

ZooKeeper 數據模型

編譯運行Zookeeper源碼

Zookeeper Watcher 流程分析(結合源碼)

img

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章