ZooKeeper的ACL權限控制和Unix/Linux作業系統的ACL有一些差別,讀者可以從三個方面來了解ACL機制,分别是:權限控制(Scheme)、授權對象(ID)和權限(Permission),通常使用“scheme:id:permission”來辨別一個有效的ACL資訊。
權限模式:Scheme
權限模式用來确定權限驗證中使用校驗政策。在ZooKeeper中,開發人員使用最多的就是以下四種權限模式。
IP
IP模式通過IP位址粒度來進行權限控制,例如配置了“ip:192.168.0.110”,即表示權限控制都是針對這個IP位址的。同時,IP模式也支援按照網段的方式進行配置,例如“ip:192.168.0.1/24”表示針對192.168.0.*這個IP段進行權限控制。
Digest
Digest是最常用的權限控制模式,也更符合我們對于權限控制的認識,其以類似于“username:password”形式的權限辨別來進行權限配置,便于區分不同應用來進行權限控制。
當我們通過“username:password”形式配置了權限辨別後,ZooKeeper會對其先後進行兩次編碼處理,分别是SHA-1算法加密和BASE64編碼,其具體實作由DigestAuthenticationProvider.generateDigest(String idPassword)函數進行封裝,下面代碼所示為使用該函數進行“username:password”編碼的一個執行個體。
public class DigestAuthenticationProviderUsage {
public static void main(String[] args) {
try {
System.out.println(DigestAuthenticationProvider.generateDigest("foo:zk-book"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
運作程式,輸出結果如下:
從上面的運作結果中可以看出,“username:password”最終會被混淆為一個無法辨識的字元串。
World
World是一種最開放的權限控制模式,從其名字中也可以看出,事實上這種權限控制方式幾乎沒有任何作用,資料節點的通路權限對所有使用者開放,即所有使用者都可以在不進行任何權限校驗的情況下操作ZooKeeper上的資料。另外,World模式也可以看作是一種特殊的Digest模式,他隻有一個權限辨別,即“world:anyone”。
Super
Super模式,顧名思義就是超級使用者的意思,也是一種特殊的Digest模式。在Super模式下,超級使用者可以對任意ZooKeeper上的資料節點進行任何操作。
授權對象:ID
授權對象指的是權限賦予的使用者或一個指定實體,例如IP位址或是機器等。在不同的權限模式下,授權對象是不同的,下表中列出了各個權限模式和授權對象之間的對應關系。
權限模式 授權對象 IP 通常是一個IP位址或是IP段,例如“192.168.0.110”或“192.168.0.1/24” Digest 自定義,通常是“username:BASE64(SHA-1(username:password))”,例如“foo:kWN6aNSbjcKWPqjiV7cg0N24raU=” World 隻有一個ID:"anyone" Super 與Digest模式一緻
權限:Permission
權限就是指那些通過權限檢查後可以被允許執行的操作。在ZooKeeper中,所有對資料的操作權限分為以下五大類:
- CREATE(C):資料節點的建立權限,允許授權對象在該資料節點下建立子節點。
- DELETE(D):子節點的删除權限,允許授權對象删除該資料節點的子節點。
- READ(R):資料節點的讀取權限,允許授權對象通路該資料節點并讀取其資料内容或子節點清單等。
- WRITE(W):資料節點的更新權限,允許授權對象對該資料節點進行更新操作。
- ADMIN(A):資料節點的管理權限,允許授權對象對該資料節點進行ACL相關的設定操作。
權限擴充體系
在上文中,我們已經講解了ZooKeeper預設提供的IP、Digest、World和Super這四種權限模式,在絕大部分的場景下,這四種權限模式已經能夠很好的實作權限控制的目的。同時ZooKeeper提供了特殊的權限控制插件體系,允許開發人員通過指定方式對ZooKeeper的權限進行擴充。這些擴充的權限控制方式就像插件一樣插入到ZooKeeper的權限體系中去,是以在ZooKeeper的官方文檔中,也稱該機制為“Pluggable ZooKeeper Authentication”。
實作自定義權限控制器
要實作自定義權限控制器非常簡單,ZooKeeper定義了一個标準權限控制器需要實作的接口:org.apache.zookeeper.server.auth.AuthenticationProvider,其接口定義如下圖所示。
使用者可以基于該接口來進行自定義權限控制器的實作。事實上,在前面内容中提到的幾個權限模式,對應的就是ZooKeeper自帶的DigestAuthenticationProvider和IPAuthenticationProvider兩個權限控制器。
注冊自定義權限控制器
完成自定義權限控制器的開發後,接下來就需要将該權限控制器注冊到ZooKeeper伺服器中去了。ZooKeeper支援通過系統屬性和配置檔案兩種方式來注冊自定義的權限控制器。
- 系統屬性 -Dzookeeper.authProvider.X
在ZooKeeper啟動參數中配置類似于如下的系統屬性:
-Dzookeeper.authProvider.1=com.zkbook.CustomAuthenticationProvider
- 配置檔案方式
在zoo.cfg配置檔案中配置類似于如下的配置項:
authProvider.1=com.zkbook.CustomAuthenticationProvider
對于權限控制器的注冊,ZooKeeper采用了延遲加載的政策,即隻有在第一次處理包含權限控制的用戶端請求時,才會進行權限控制器的初始化。同時,ZooKeeper還會将所有的權限控制器都注冊到ProviderRegistry中去。在具體的實作中,ZooKeeper首先會将DigestAuthenticationProvider和IPAuthenticationProvider這兩個預設的控制器初始化,然後通過掃描zookeeper.authProvider.這一系統屬性,擷取到所有使用者配置的自定義權限控制器,并完成其初始化。
ACL管理
講解完ZooKeeper的ACL及其擴充機制後,我們來看看如何進行ACL管理。
設定ACL
通過zkCli腳本登入ZooKeeper伺服器後,可以通過兩種方式進行ACL的設定。一種是在資料節點建立的同時進行ACL權限的設定,命名格式如下:
- create [-s] [-e] path data acl
具體使用如下所示。
另一種方式則是使用setAcl命名單獨對已經存在的資料節點進行ACL設定:
- setAcl path acl
具體使用如下所示。
Super模式的用法
根據ACL權限控制的原理,一旦對一個資料節點設定了ACL權限控制,那麼其他沒有被授權的ZooKeeper用戶端将無法通路該資料節點,這的确很好的保證了ZooKeeper的資料安全。但同時,ACL權限控制也給ZooKeeper的運維人員帶來了一個困擾:如果一個持久資料節點包含了ACL權限控制,而其建立者用戶端已經退出或已不再使用,那麼這些資料節點該如何清理呢?這個時候,就需要在ACL的Super模式下,使用超級管理者權限來進行處理了。要使用超級管理者權限,首先需要在ZooKeeper伺服器上開啟Super模式,方法是在ZooKeeper伺服器啟動的時候,添加如下系統屬性:
- -Dzookeeper.DigestAuthenticationProvider.superDigest=foo:kWN6aNSbjcKWPqjiV7cg0N24raU=
其中,“foo”代表了一個超級管理者的使用者名;“kWN6aNSbjcKWPqjiV7cg0N24raU=”是可變的,由ZooKeeper的系統管理者來進行自主配置,此例中使用的是“foo:zk-book”的編碼。完成對ZooKeeper伺服器的Super模式的開啟後,就可以在應用程式中使用了,下面是一個使用超級管理者權限操作ZooKeeper資料節點的示例程式。
public class AuthSample_Super {
final static String PATH = "/zk-book";
public static void main(String[] args) throws Exception {
ZooKeeper zooKeeper1 = new ZooKeeper("127.0.0.1:2181", 5000, null);
zooKeeper1.addAuthInfo("digest", "foo:true".getBytes());
// 判斷是否為空
if (zooKeeper1.exists(PATH, false) == null) {
zooKeeper1.create(PATH, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);
}
// 用管理者權限
ZooKeeper zooKeeper2 = new ZooKeeper("127.0.0.1:2181", 500000, null);
zooKeeper2.addAuthInfo("digest", "foo:zk-book".getBytes());
System.out.println(zooKeeper2.getData(PATH, false, null));
// 用其他使用者通路
ZooKeeper zooKeeper3 = new ZooKeeper("127.0.0.1:2181", 50000, null);
zooKeeper3.addAuthInfo("digest", "foo:false".getBytes());
System.out.println(zooKeeper3.getData(PATH, false, null));
}
}
從上面的輸出結果中,我們可以看出,由于“foo:zk-book”是一個超級管理者賬戶,是以能夠針對一個受權限控制的資料節點zk-book随意進行操作,但是對于“foo:false”這個普通使用者,就無法通過權限校驗了。