权限介绍
开始之前先介绍一些Zookeeper的权限。zookeeper支持的权限有5种分别是
- CREATE: 你可以创建子节点。
- READ: 你可以获取节点数据以及当前节点的子节点列表。
- WRITE: 你可以为节点设置数据。
- DELETE: 你可以删除子节点。
- ADMIN: 可以为节点设置权限。
接下来看看授权所需要的语法
[zk: localhost:2181(CONNECTED) 0] create /zoo 16
Created /zoo
[zk: localhost:2181(CONNECTED) 1] setAcl /zoo rw
rw does not have the form scheme:id:perm
Acl is not valid : /zoo
看到了授权需要的模式是 scheme:id:perm
,额,不得不说这里说id
也是坑爹的。具体来说应该是用expression
或者 id
, 下面说一下scheme
scheme介绍
world 表示所有。创建节点的默认权限。有唯一的id是anyone授权的时候的模式为
world:anyone:rwadc
表示所有人都对这个节点有rwadc的权限。这里用的是id
而不是expression
auth 不需要id。不过这里应该用
expression
来表示。即(scheme:expression:perm)digest 使用
用户名:密码
编码成md5的方式来作为访问控制列表的id。但是这里id不作为授权语句的一部分,这里也是用expression
的方式。用户名: 密码先进行sha1编码后再用base64编码
。这个比较恶心,后面再详细介绍。host 使用用户主机名作为访问控制列表的id。但是这里需要注意的是表达式用的是主机名的后缀即可。举个例子。如果表达式设置为
corp.com
可以匹配如host1.corp.com
,host2.corp.com
的主机名,但是不能匹配host1.zookeeper.com
这个主机名。ip 跟主机名类似,这里用客户端的ip地址作为访问控制列表的id。表达式可以用
addr/bits
这种方式来设置ip白名单。
OK, 以上只是官方文档的粗略翻译,以及加上自己的一些理解所组织起来的语言。
下面对授权方式作些解释,这里只是讲auth
与digest
两种方式。
auth的授权方式
创建新的节点
[zk: localhost:2181(CONNECTED) 7] create /auth auth
Created /auth
授予auth权限,这里就有个坑,文档里面根本没有说明应该如何授予这个权限。
auth doesn't use any id, represents any authenticated user.
要他何用?也就是说文档没有说明该用何种expression
来表示这个权限,后面经过翻阅资料查得,username:password
的来作为expression
。所以有了下面的的语法。
[zk: localhost:2181(CONNECTED) 9] setAcl /auth auth:zookeeper:zookeeper:rwadc
Acl is not valid : /auth
从上面可以看到这里Acl不能识别。这里又是一个文档的不足的地方,我是后来翻阅博客才知道有下面这样的黑魔法
- 设置这个权限之前首先需要
[zk: localhost:2181(CONNECTED) 12] addauth digest zookeeper:zookeeper
- 然后再设置auth
[zk: localhost:2181(CONNECTED) 13] setAcl /auth auth:zookeeper:zookeeper:rwadc
cZxid = 0x3
ctime = Mon May 30 10:32:15 CST 2016
mZxid = 0x3
mtime = Mon May 30 10:32:15 CST 2016
pZxid = 0x3
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
- 检查权限
[zk: localhost:2181(CONNECTED) 14] getAcl /auth
'digest,'zookeeper:4lvlzsipXVaEhXMd+2qMrLc0at8=
: cdrwa
- 可以看到权限已经设置成功了。
我也不知道怎么吐槽官方文档好了。冷暖自知。(T_T)
digest授权方式
这种方式跟auth的方式是差不多的,不同的在于
[zk: localhost:2181(CONNECTED) 2] create /digest digest
Created /digest
[zk: localhost:2181(CONNECTED) 3] setAcl /digest digest:zookeeper:zookeeper:rwadc
cZxid = 0x15
ctime = Mon May 30 10:59:24 CST 2016
mZxid = 0x15
mtime = Mon May 30 10:59:24 CST 2016
pZxid = 0x15
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
[zk: localhost:2181(CONNECTED) 5] getAcl /digest
'digest,'zookeeper:zookeeper
: cdrwa
可以看出比较明显的:
- 设置之前不需要通过addauth添加对应的授权认证,而是直接授权便可。
- 命令行设置了什么,然后在获取对应的权限的时候就会把对应的字符串显式记录下来,而不会进行任何编码, 如上面的
zookeeper:zookeeper
。这种方式就需要自己去编码了。 - 这也是个比较坑爹的事情,设置密码之前需要把密码先自己进行sha1编码然后吧结果进行base64编码。如果编码的方式错误你将永远失去这个节点的访问权。
首先对想要的用户名:密码
进行处理
# 正确的方式
>> echo -n zookeeper:zookeeper | openssl dgst -binary -sha1 | openssl base64
>> 4lvlzsipXVaEhXMd+2qMrLc0at8=
# 错误的方式
>> echo zookeeper:zookeeper | openssl dgst -binary -sha1 | openssl base64
>> VWUr1lLcb0QrQUJmK+WEKUYYnV4=
>> echo -n zookeeper:zookeeper | sha1sum |base64
>> ZTI1YmU1Y2VjOGE5NWQ1Njg0ODU3MzFkZmI2YThjYWNiNzM0NmFkZiAgLQo=
上面是我踩过的坑。
得出的结果可见,跟用auth方式添加权限之后getAcl得到的密码结果是一样的。然后再把这个结果作为密码用digest的方式存进去
[zk: localhost:2181(CONNECTED) 18] setAcl /digest digest:zookeeper:4lvlzsipXVaEhXMd+2qMrLc0at8=:rwdca
cZxid = 0x1d
ctime = Mon May 30 11:12:54 CST 2016
mZxid = 0x1d
mtime = Mon May 30 11:12:54 CST 2016
pZxid = 0x1d
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
就可以通过addauth的方式访问了
[zk: localhost:2181(CONNECTED) 0] get /digest
Authentication is not valid : /digest
[zk: localhost:2181(CONNECTED) 1] addauth digest zookeeper:zookeeper
[zk: localhost:2181(CONNECTED) 2] get /digest
digest
cZxid = 0x1d
ctime = Mon May 30 11:12:54 CST 2016
mZxid = 0x1d
mtime = Mon May 30 11:12:54 CST 2016
pZxid = 0x1d
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
忘记了密码?
如果忘记了该子节点的授权用户名还有密码。这里是比较蛋疼的事情。比如
[zk: localhost:2181(CONNECTED) 10] setAcl /forget digest:because:because:rwacd
cZxid = 0x2f
ctime = Mon May 30 11:29:55 CST 2016
mZxid = 0x30
mtime = Mon May 30 11:30:16 CST 2016
pZxid = 0x2f
cversion = 0
dataVersion = 1
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 28
numChildren = 0
[zk: localhost:2181(CONNECTED) 11] get /forget
Authentication is not valid : /forget
由于我们基本上找不到because在base64反编码后再sha1反编码后的样子,所以基本上这个节点的控制权可以说是失去了。
解决方案
- 由于我们有
/
目录的权限,所以我们还是可以通过delete来删除这个子节点,重新设置。 - 如果我们是服务的维护者,通过设置配置文件
skipACL=yes
然后重启服务。所有操作就可以逃过ACL的检测。不过这种方式比较危险,最好是限制只有本地可以访问的时候进行。防止其他用户可以直接操作其他节点。不过设置好之后别忘了要重新开启ACL。