ZooKeeper 作为一个分布式协调框架,内部存储的都是一些分布式系统运行时状态的元数据,尤其是一些涉及到分布式锁master选举分布式协调等应用场景的数据,数据的访问权限涉及到 ZooKeeper 运行时安全。为了保障 ZooKeeper 的数据安全,提供了一套完整的ACL(Access Control List)权限控制机制来保障数据的安全。

权限 ACL 介绍

ACL 的实现和 UNIX 的实现非常相似:它采用权限位来控制哪些操作被允许,哪些操作被禁止。但是和标准的 UNIX 权限不同的是,Znode 没有限制用户(user,即文件的所有者),组(group)和其他(world)。Zookeepr 是没有所有者的概念的。

每个 ZNode 的ACL是独立的,且子节点不会继承父节点的ACL。例如:Znode /app对于 ip 为172.16.16.1只有只读权限,而/app/status是world可读,那么任何人都可以获取 /app/status。所以在 Zookeeper 中权限是没有继承和传递关系的,每个Znode 的权限都是独立存在的

Zookeeper 支持可插拔的权限认证方案,分为三个维度:scheme,user,permission。通常表示为[scheme:id:permissions],其中 Scheme 表示使用何种方式来进行访问控制(使用何种权限验证机制),Id代表用户,Permission表示有什么权限(权限组合字符串)。下面分别说说这三个维度:

一个 ZooKeeper 的节点( znode )存储两部分内容:数据和状态,状态中包含 ACL 信息。创建一个 znode 会产生一个 ACL 列表,列表中每个 ACL 包括以上三个维度。

ACL 的构成-scheme(权限模式)

scheme 有以下几种类型:

  • world:world下只有一个id,即只有一个用户,也就是anyone,那么组合的写法就是 world:anyone:[permissions]
  • ip:当设置为ip指定的ip地址,此时限制ip进行访问,比如 ip:192.168.77.130:[permissions]。也可以通过配置IP网段的方式,例如 “ip:192.168.28.1/24:[permissions]”,表示针对192.168.28.*整个IP段进行权限控制。
  • auth:代表认证登录,需要注册用户获取权限后才可以登录访问,形式为 auth:userpassword:[permissions]
  • digest:需要对密码加密才能访问,组合形式为:digest:username:BASE64(SHA1(password)):[permissions]。具体的实现是由DigestAuthenticationProvider.generateDigest()方法来保证的。
  • auth 与 digest 的区别就是,前者使用明文密码进行登录,后者使用密文密码进行登录。setAcl /path auth:lee:lee:cdrwasetAcl /path digest:lee:BASE64(SHA1(password)):cdrwa是等价的,在通过 addauth digest lee:lee 后都能操作指定节点的权限。在实际情况中,digest要更为常用一些。
  • super:代表超级管理员,拥有所有的权限,可以操作ZooKeeper上的任意节点数据。

注意的是, exists 操作和 getAcl 操作并不受 ACL 许可控制,因此任何客户端可以查询节点的状态和节点的 ACL 。

ACL 的构成-id(授权对象)

对于不同的scheme,授权的对象是不同的:

  • IP模式:授权对象通常是一个ip地址或者是一个ip段;
  • Digest模式:授权对象通常是自定义中的username;
  • World模式:授权对象是anyone;
  • Super模式:授权对象和Digest中的相同;
ACL 的构成-permissions(权限)

权限字符串缩写 crdwa

  • CREATE:创建子节点权限(允许对子节点 Create 操作);
  • READ:访问节点/子节点权限(允许对本节点 GetChildren 和 GetData 操作);
  • WRITE:设置节点数据权限(允许对本节点 SetData 操作);
  • DELETE:删除子节点权限(允许对子节点 Delete 操作);
  • ADMIN:管理员权限(允许对本节点 setAcl 操作);
ACL 命令
  • getAcl:获取某个节点的ACL权限信息;
  • setAcl:设置某个节点的ACL权限信息;
  • addauth:输入认证授权信息,注册时输入明文密码(登录),但是在zk的系统里,密码是以加密后的形式存在的;
ACL 命令行操作

ACL 命令行 world

使用 getAcl 命令获取某个节点的acl权限信息,示例:

[zk: localhost:2181(CONNECTED) 0] create /testDir/testAcl test-data     # 创建一个子节点
Created /testDir/testAcl
[zk: localhost:2181(CONNECTED) 1] getAcl /testDir/testAcl       # 获取该节点的acl权限信息
'world,'anyone      # 默认为world
: cdrwa

使用 setAcl 命令设置某个节点的acl权限信息,示例:

[zk: localhost:2181(CONNECTED) 2] setAcl /testDir/testAcl world:anyone:crwa     # 设置该节点的acl权限
cZxid = 0x67
ctime = Tue Aug 28 13:12:29 CST 2018
mZxid = 0x67
mtime = Tue Aug 28 13:12:29 CST 2018
pZxid = 0x67
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
[zk: localhost:2181(CONNECTED) 3] getAcl /testDir/testAcl
'world,'anyone
: crwa      # 设置成功后,该节点就少了d权限
[zk: localhost:2181(CONNECTED) 4] create /testDir/testAcl/xyz xyz-data      # 创建子节点
Created /testDir/testAcl/xyz
[zk: localhost:2181(CONNECTED) 5] delete /testDir/testAcl/xyz       # 删除该子节点
Authentication is not valid : /testDir/testAcl/xyz      # 由于没有d权限,所以提示无法删除

设置其他的权限也是如此,在此就不做演示了。

ACL 命令行 auth

使用 auth 来设置权限的时候,需要在 zk 里注册一个用户才可以。示例:

[zk: localhost:2181(CONNECTED) 6] addauth digest user1:123456       # 需要先添加一个用户
[zk: localhost:2181(CONNECTED) 7] setAcl /testDir/testAcl auth:user1:123456:crwa    # 然后才可以拿着这个用户去设置权限
cZxid = 0x67
ctime = Tue Aug 28 13:12:29 CST 2018
mZxid = 0x67
mtime = Tue Aug 28 13:12:29 CST 2018
pZxid = 0x69
cversion = 1
dataVersion = 0
aclVersion = 2
ephemeralOwner = 0x0
dataLength = 9
numChildren = 1
[zk: localhost:2181(CONNECTED) 8] getAcl /testDir/testAcl
'digest,'user1:HYGa7IZRm2PUBFiFFu8xY2pPP/s=     # 密码是以密文的形式存储的
: crwa

如果设置了权限后,再次进行设置,就可以不用加上用户名和密码了。而且就算是使用其他用户进行设置,也只会根据第一次设置的用户来进行配置:

[zk: localhost:2181(CONNECTED) 9] setAcl /testDir/testAcl auth::crw
[zk: localhost:2181(CONNECTED) 10] setAcl /testDir/testAcl auth:test:test:crw
[zk: localhost:2181(CONNECTED) 11] getAcl /testDir/testAcl
'digest,'user1:HYGa7IZRm2PUBFiFFu8xY2pPP/s=     # 依旧是第一次设置的用户
: crw
ACL 命令行 digest

由于之前使用 auth 做实验的时候已经登录了用户 ,所以在使用 digest 做实验前,需要先退出一下客户端,再重新连接,这样之前登录的用户就会自动退出。退出用户后才能使用 digest 设置权限,示例:

[zk: localhost:2181(CONNECTED) 2] create /names/testDigest digest-data      # 创建子节点
Created /names/testDigest
[zk: localhost:2181(CONNECTED) 3] addauth digest user1:123456       # 添加用户
[zk: localhost:2181(CONNECTED) 4] getAcl /names/testDigest
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 5] setAcl /names/testDigest digest:user1:HYGa7IZRm2PUBFiFFu8xY2pPP/s=:crwa   # 使用digest来设置权限
cZxid = 0x72
ctime = Tue Aug 28 14:37:05 CST 2018
mZxid = 0x72
mtime = Tue Aug 28 14:37:05 CST 2018
pZxid = 0x72
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 11
numChildren = 0
[zk: localhost:2181(CONNECTED) 6] getAcl /names/testDigest
'digest,'user1:HYGa7IZRm2PUBFiFFu8xY2pPP/s=
: crwa

可以看到,digest 和 auth 除了一个使用明文一个使用密文之外,其他都是一致的,所以它俩的区别就只是密文和明文的区别。在线上环境中,一般使用 digest 比较多,因为密文安全一些。

ACL 命令行 ip

这种方式和限制 ip 是一样的,就是设置只允许某一个 ip 有权限操作。示例:

[zk: localhost:2181(CONNECTED) 7] create /names/testip ip-data      # 创建子节点
Created /names/testip
[zk: localhost:2181(CONNECTED) 8] getAcl /names/testip
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 9] setAcl /names/testip ip:192.168.190.129:cdrwa     # 使用ip来设置权限
cZxid = 0x74
ctime = Tue Aug 28 15:03:56 CST 2018
mZxid = 0x74
mtime = Tue Aug 28 15:03:56 CST 2018
pZxid = 0x74
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
[zk: localhost:2181(CONNECTED) 10] getAcl /names/testip
'ip,'192.168.190.129        # 只允许这个ip拥有该节点的cdrwa权限
: cdrwa
[zk: localhost:2181(CONNECTED) 11] get /names/testip
Authentication is not valid : /names/testip     # 由于本机的ip不是192.168.190.129,所以获取失败
ACL 命令行 super超级管理员

基本所有的系统都会拥有一个超级管理员用户,zk也不例外。当我们给一些节点设置了权限,但是却发现设置错误了,导致节点无法正常访问,那么这时候普通用户是无法解决这种问题的,只能使用超级管理员用户来重新设置权限或删除节点。所以本节简单演示一下,如何配置管理员用户:

1、修改 zkServer.sh 增加super管理员
[root@study-01 /usr/local/zookeeper-3.4.11/bin]# vim zkServer.sh  # 找到nohup那一行,加入以下内容
"-Dzookeeper.DigestAuthenticationProvider.superDigest=user1:HYGa7IZRm2PUBFiFFu8xY2pPP/s="

注:user1是用户名,后面那一大串是加密后的密文密码

如下图:

ZooKeeper管理者使用用户系统权限管理安全基线 zookeeper权限控制_数据

2、修改完之后,需要重启zookeeper服务才能生效
[root@study-01 /usr/local/zookeeper-3.4.11/bin]# ./zkServer.sh restart
3、使用超级管理员用户
[root@study-01 ~]# zkCli.sh
[zk: localhost:2181(CONNECTED) 9] ls /names/ip  # ls一个无权限的节点
Authentication is not valid : /names/ip   # 可以看到,权限不够
[zk: localhost:2181(CONNECTED) 10] addauth digest user1:123456  # 登录超级管理员用户,这里登录用的是明文密码
[zk: localhost:2181(CONNECTED) 11] ls /names/ip  # 然后再次ls
[]   # 这次就可以ls到了
[zk: localhost:2181(CONNECTED) 12] get /names/ip   # 也可以get信息
ip-data
cZxid = 0x51
ctime = Mon Apr 23 21:02:42 CST 2018
mZxid = 0x51
mtime = Mon Apr 23 21:02:42 CST 2018
pZxid = 0x51
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
[zk: localhost:2181(CONNECTED) 13] delete /names/testip  # 删除节点也可以
ACL 的常用使用场景
  • 开发/测试环境分离,开发者无权操作测试库的节点,只能读取
  • 生产环境上控制指定ip的服务可以访问相关的节点,防止混乱