對(duì)zookeeper設(shè)置ACL屬性
我們以zkCli為例,來說明zookeeper對(duì)ACL的設(shè)置。
使用zkCli時(shí),ACL的格式由<schema>:<id>:<acl>三段組成。
- schema:可以取下列值:world, auth, digest, host/ip
- id: 標(biāo)識(shí)身份,值依賴于schema做解析。
- acl:就是權(quán)限:cdwra分別表示create, delete,write,read, admin
注意:zookeeper對(duì)權(quán)限的控制是znode級(jí)別的,不具有繼承性,即子節(jié)點(diǎn)不繼承父節(jié)點(diǎn)的權(quán)限。這種設(shè)計(jì)在使用上還是有缺陷的,因?yàn)楹芏鄨?chǎng)景下,我們還是會(huì)把相關(guān)資源組織一下,放在同一個(gè)路徑下面,這樣就會(huì)有對(duì)一個(gè)路徑統(tǒng)一授權(quán)的需求。
- schema world
這是默認(rèn)方式,表示沒有認(rèn)證。當(dāng)創(chuàng)建一個(gè)新的節(jié)點(diǎn)(znode),而又沒有設(shè)置任何權(quán)限時(shí),就是這個(gè)值,例如:
[zk: localhost:2181(CONNECTED) 18] create /noacl 'noacl'
Created /noacl
[zk: localhost:2181(CONNECTED) 19] getAcl /noacl
'world,'anyone
: cdrwa
看到/noacl的ACL屬于就是world schema的,因?yàn)樗鼪]有設(shè)置ACL屬性,這樣任何人都可以訪問這個(gè)節(jié)點(diǎn)。
如果要手工設(shè)置這個(gè)屬性,那么此時(shí)的id域只允許一個(gè)值,即anyone,格式如下:
setAcl /newznode world:anyone:crdwa
- schema auth
這種授權(quán)不針對(duì)任何特點(diǎn)ID,而是對(duì)所有已經(jīng)添加認(rèn)證的用戶,換句話說,就是對(duì)所有已經(jīng)通過認(rèn)證的用戶授權(quán)。
用法如下:
addauth digest <user>:<password>
setAcl <path> auth:<id>:<acl>
注意:
- 比須要先添加認(rèn)證用戶,否則會(huì)失敗呢。如下所示:
[zk: localhost:2181(CONNECTED) 0] create /test 'test'
[zk: localhost:2181(CONNECTED) 1] setAcl /test auth::crdwa
Acl is not valid : /test
- setAcl命令中的id域是被忽略的,可以填任意值,或者空串,例如:`setAcl <path> auth::crdwa。因?yàn)檫@個(gè)域是忽略的,會(huì)把所有已經(jīng)添加的認(rèn)證用戶都加進(jìn)來。
舉例:
[zk: localhost:2181(CONNECTED) 2] addauth digest tom1:tom1
[zk: localhost:2181(CONNECTED) 3] addauth digest tom2:tom2
[zk: localhost:2181(CONNECTED) 4] addauth digest tom3:tom3
[zk: localhost:2181(CONNECTED) 5] setAcl /test auth:tom2:crdwa
[zk: localhost:2181(CONNECTED) 6] getAcl /test
'digest,'tom1:ben+k/3JomjGj4mfd4fYsfM6p0A=
: cdrwa
'digest,'tom2:2iJM00A7+qkeKdEXt8Bhgq+IACw=
: cdrwa
'digest,'tom3:TAZPWLs6IaYRS8mlvcfyCOwyBJ8=
: cdrwa
這個(gè)例子中,我們先添加了三個(gè)認(rèn)證用戶tom1,tom2,tom3,然后通過setAcl設(shè)置ACL,命令中指定了id為tom2,根據(jù)前面的說法,這個(gè)id值是被忽略的,寫任何值,甚至空值也得到一樣的結(jié)果。我們看到最后getAcl查詢出來的結(jié)果包含所有前面添加的三個(gè)認(rèn)證用戶。
補(bǔ)充說明:zkCli的命令addauth digest user:pwd是用來添加當(dāng)前上下文中的認(rèn)證用戶的:
addauth digest user1:password1(明文)
其實(shí)我不是很理解這個(gè)功能,難道在一個(gè)會(huì)話(session)里可以添加多個(gè)認(rèn)證用戶嗎,那驗(yàn)證的時(shí)候按哪一個(gè)算呢;如果不同的用戶有不同的授權(quán)會(huì)導(dǎo)致授權(quán)沖突嗎?以誰為準(zhǔn)?
幾點(diǎn)總結(jié):
- auth的id值是無效的,表示給所有認(rèn)證用戶設(shè)置acl權(quán)限。
1.1. 認(rèn)證用戶的添加,通過addauth命令(addauth digest <username>:<password>),只在當(dāng)前會(huì)話(session)有效。 - 當(dāng)使用addauth命令添加多個(gè)認(rèn)證用戶后,再用auth setAcl來設(shè)置acl時(shí),那么所有之前addauth的用戶都被會(huì)加入到acl中。
- 如果在當(dāng)前會(huì)話中還沒有認(rèn)證過的用戶就使用auth setAcl來設(shè)置acl權(quán)限時(shí)會(huì)失敗,前面已經(jīng)討論過。
[zk: localhost:2181(CONNECTED) 1] setAcl /test auth::crdwa
Acl is not valid : /test
- 在auth setAcl之后再使用addauth添加的認(rèn)證用戶是沒有acl權(quán)限的,必須重新執(zhí)行auth setAcl來設(shè)置權(quán)限。
- 使用addauth添加的認(rèn)證用戶只在當(dāng)前會(huì)話(session)有效,如果此時(shí)在另外一個(gè)會(huì)話中,不添加對(duì)應(yīng)的認(rèn)證用戶,那么就沒有相應(yīng)訪問權(quán)限的,而且如果再使用auth setAcl來設(shè)置acl權(quán)限,則會(huì)覆蓋之前的acl權(quán)限信息,而且只會(huì)針對(duì)當(dāng)前會(huì)話中的認(rèn)證用戶來設(shè)置acl權(quán)限。
所以這種授權(quán)方式更傾向于用作測(cè)試開發(fā)環(huán)境,而不是產(chǎn)品環(huán)境中。
- schema digest
這就是最普通的用戶名:密碼的驗(yàn)證方式,在一般業(yè)務(wù)系統(tǒng)中最常用。
格式如下:
setAcl <path> digest:<user>:<password(密文)>:<acl>
和schema auth相比,有兩點(diǎn)不同:
- 第一不需要預(yù)先添加認(rèn)證用戶(但是在zkCli訪問的時(shí)候,肯定還是要添加認(rèn)證用戶的)。
- 第二密碼是經(jīng)過sha1及base64處理的密文。
密碼可以通過如下shell的方式生成:
echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
例如:
echo -n root:root | openssl dgst -binary -sha1 | openssl base64
qiTlqPLK7XM2ht3HMn02qRpkKIE=
或者可以使用zookeeper的庫(kù)文件生成:
java -cp /zookeeper-3.4.13/zookeeper-3.4.13.jar:/zookeeper-3.4.13/lib/slf4j-api-1.7.25.jar \
org.apache.zookeeper.server.auth.DigestAuthenticationProvider \
root:root
root:root->root:qiTlqPLK7XM2ht3HMn02qRpkKIE=
輸出的root:jalRr+knv/6L2uXdenC93dEDNuE=就是傳遞給setAcl使用的id串。
setAcl /test2 digest:root:jalRr+knv/6L2uXdenC93dEDNuE=:rwdca
注意,只有通過zkCli.sh設(shè)置digest的ACL時(shí)id才需要密文,而通過zookeeper的客戶端設(shè)置digest的ACL時(shí)對(duì)應(yīng)的auth數(shù)據(jù)是明文。這個(gè)屬于編碼實(shí)現(xiàn)的問題了。
和auth比較,digest有如下特性:
- setAcl不需要事先添加認(rèn)證用戶。
- 授權(quán)是針對(duì)單個(gè)特定用戶。
- setAcl使用的密碼不是明文,是sha1摘要值,無法反推出用戶密碼內(nèi)容。
- schema host/ip
就是客戶機(jī)地址,或者是主機(jī)名、或者是IP地址。
主機(jī)名可以是單個(gè)主機(jī)名,也可以是域名。IP可以是單個(gè)IP地址,也可以是IP地址段,比如ip:192.168.1.0/16。
這個(gè)不細(xì)說了,比較簡(jiǎn)單,也沒有驗(yàn)證過。
- super用戶
設(shè)置一個(gè)超級(jí)用戶,這個(gè)超級(jí)用戶的設(shè)置必須在zookeeper內(nèi)部,zookeeper啟動(dòng)之前設(shè)置好。在這種scheme情況下,超級(jí)用戶具有超級(jí)權(quán)限,可以做任何事情(cdrwa),不需要授權(quán)。
5.1 設(shè)置zookeeper環(huán)境變量SERVER_JVMFLAGS:
export SERVER_JVMFLAGS="-Dzookeeper.DigestAuthenticationProvider.superDigest=root:qiTlqPLK7XM2ht3HMn02qRpkKIE="
5.2 重啟zookeeper
創(chuàng)建/test節(jié)點(diǎn),并且設(shè)置acl為jerry1用戶。
[zk: localhost:2181(CONNECTED) 0] create /test 'test'
Created /test
[zk: localhost:2181(CONNECTED) 1] setAcl /test digest:jerry1:dJJW56m9FIOfUDDHVC5wVWNsFEo=:rwdca
[zk: localhost:2181(CONNECTED) 2] getAcl /test
'digest,'jerry1:dJJW56m9FIOfUDDHVC5wVWNsFEo=
: cdrwa
5.3 添加認(rèn)證用戶tom
[zk: localhost:2181(CONNECTED) 3] addauth digest tom:tom
5.4 訪問節(jié)點(diǎn)/test
[zk: localhost:2181(CONNECTED) 4] get /test
Authentication is not valid : /test
這時(shí)失敗,因?yàn)閠om用戶沒有權(quán)限。
5.3 添加認(rèn)證用戶root
[zk: localhost:2181(CONNECTED) 6] addauth digest root:root
5.4 再次訪問節(jié)點(diǎn)/test
[zk: localhost:2181(CONNECTED) 6] get /test
test
...
成功,雖然root也沒有在/test的acl列表里面(是有jerry1),但是也能訪問,因?yàn)閞oot在zookeeper集群里面被配置成了超級(jí)用戶。