acl(access control list)权限控制
- zk的权限控制,可以控制节点的读写操作,保证数据的安全性
- 权限的permission可以指定不同的权限范围及角色
命令:
- getAcl path 获取节点权限信息
- setAcl path acl 设置节点权限信息
- addauth scheme auth 输入注册用户(认证授权信息),输入时使用明文密码,在zk中密码是以密文加密形式存在
acl构成
[scheme:id:permissions]构成权限列表
- scheme 采取某种权限机制(只能使用内置的几种)
- world 只有一个用户,就是anyone,任何人都可以访问,形式为
world:anyone:[permissions]
- auth 需要注册认证的用户(即通过
addauth
的用户),形式为auth:user:password:[permissions]
- digest 同auth一样需要注册认证的用户,但是密码形式不同,密码为加密后密码,形式为
digest:user:BASE64(SHA1(password)):permissions
- ip 可以添加ip权限机制,形式为
ip:162.0.0.1:permissions
- super 代表超级管理员拥有所有权限
- world 只有一个用户,就是anyone,任何人都可以访问,形式为
- id 代表访问用户
- permissions 权限组合字符串(crdwa)
- c -> create
- r -> read
- d -> delete(针对子节点权限,比如设置
>> setAcl /names world:anyone:crwa
,删除>> delete /names
是可以成功的,但是删除其子节点>> delete /names/zxc
返回Authentication is not valid : /names/zxc
,说明delete权限针对的是子节点操作权限) - w -> write
- a -> admin (具有此节点修改/设置权限的权限)
digest与auth区别:本质上来讲,更加安全.因为有时候不想给出密码因为密码可以干很多事情,所以有一个权限是通过密文就可以直接登录.
命令演示
>> getAcl /names/zxc
>> 'world,'anyone
>> : cdrwa
scheme讲解
world-scheme解读
新建节点默认是world scheme且全部权限
>> [zk: localhost:2181(CONNECTED) 20] getAcl /names/zxc
>> 'world,'anyone
>> : cdrwa
修改权限
>> [zk: localhost:2181(CONNECTED) 21] setAcl /names world:anyone:cra //有创建/读取/和设置权限的权限
尝试读取
[zk: localhost:2181(CONNECTED) 22] get /names //可以读取
111
cZxid = 0x10
ctime = Sat Dec 22 15:43:45 CST 2018
mZxid = 0x10
mtime = Sat Dec 22 15:43:45 CST 2018
pZxid = 0x15
cversion = 3
dataVersion = 0
aclVersion = 2
ephemeralOwner = 0x0
dataLength = 3
numChildren = 1
修改节点信息
[zk: localhost:2181(CONNECTED) 23] set /names 111 //因为没有修改权限失败
Authentication is not valid : /names
删除节点
[zk: localhost:2181(CONNECTED) 30] delete /names //没有报错,直接删除成功??这是这么回事,其实d->delete权限是针对子节点的权限
创建一个字节
[zk: localhost:2181(CONNECTED) 33] create /names 111 //重新创建names节点(因为刚刚删除了)
Created /names
[zk: localhost:2181(CONNECTED) 34] create /names/zxc 1111 //创建/names/zxc
Created /names/zxc
[zk: localhost:2181(CONNECTED) 35] setAcl /names world:anyone:cra //重新设置权限
cZxid = 0x21
ctime = Sat Dec 22 16:21:44 CST 2018
mZxid = 0x21
mtime = Sat Dec 22 16:21:44 CST 2018
pZxid = 0x22
cversion = 1
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 3
numChildren = 1
[zk: localhost:2181(CONNECTED) 36] delete /names/zxc //此时删除zxc发现delete权限生效
Authentication is not valid : /names/zxc
auth-scheme解读
设置auth权限
setAcl /auth auth:cdz:cdz:cra
Acl is not valid : /auth
//发现会报错,原因是我们还没有添加auth要使用addAuth命令来添加一个用户
[zk: localhost:2181(CONNECTED) 0] addauth digest cdz:123456 //直接添加成功没有返回
[zk: localhost:2181(CONNECTED) 1] setAcl /auth auth:cdz:123456:cra //使用auth scheme添加权限
cZxid = 0x25
ctime = Sat Dec 22 16:27:24 CST 2018
mZxid = 0x25
mtime = Sat Dec 22 16:27:24 CST 2018
pZxid = 0x25
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 2
numChildren = 0
[zk: localhost:2181(CONNECTED) 2] getAcl /auth
'digest,'cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8= //可以看到类型为digest ,且帐户名与密码密文都有展示
: cra
[zk: localhost:2181(CONNECTED) 3]
此时新建窗口2
[zk: localhost:2181(CONNECTED) 0] get /auth //因为没有登录cdz这个用户,所以不能读取
Authentication is not valid : /auth
[zk: localhost:2181(CONNECTED) 1] addauth digest cdz:123456 //在登录cdz帐户之后才可以获取auth信息
[zk: localhost:2181(CONNECTED) 2] get /auth
11
cZxid = 0x25
ctime = Sat Dec 22 16:27:24 CST 2018
mZxid = 0x25
mtime = Sat Dec 22 16:27:24 CST 2018
pZxid = 0x25
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 2
numChildren = 0
[zk: localhost:2181(CONNECTED) 3] setAcl /auth auth:jack:jack:crwda //重设帐户, 发现返回节点信息,感觉是成功了,其实我们想要的是报错的,原因很简单,我们根本还没有创建jack这个帐户
cZxid = 0x25
ctime = Sat Dec 22 16:27:24 CST 2018
mZxid = 0x25
mtime = Sat Dec 22 16:27:24 CST 2018
pZxid = 0x25
cversion = 0
dataVersion = 0
aclVersion = 2
ephemeralOwner = 0x0
dataLength = 2
numChildren = 0
[zk: localhost:2181(CONNECTED) 4] getAcl /auth //但是查看信息发现根本没有生效,这是为什么?
//其实设置节点属于那个访问,
//这里因为已经通过addauth digest cdz:123456登录了cdz这个帐户,
//这个session已经记住并且有一个登录列表,
//设置auth用户时,只会取出第一个登录的auth帐户.
'digest,'cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=
: cdrwa
[zk: localhost:2181(CONNECTED) 5] setAcl /auth auth:::ca //故我们设置时其实直接使用默认形式
cZxid = 0x25
ctime = Sat Dec 22 16:27:24 CST 2018
mZxid = 0x25
mtime = Sat Dec 22 16:27:24 CST 2018
pZxid = 0x25
cversion = 0
dataVersion = 0
aclVersion = 3
ephemeralOwner = 0x0
dataLength = 2
numChildren = 0
[zk: localhost:2181(CONNECTED) 6] getAcl /auth
'digest,'cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=
: ca //看到权限是设置成功的
[zk: localhost:2181(CONNECTED) 7] addauth digest jack:jack //我们添加一个jack用户
[zk: localhost:2181(CONNECTED) 8] setAcl /auth auth:jack:jack:cra //设置`/auth` 用户为jack
cZxid = 0x25
ctime = Sat Dec 22 16:27:24 CST 2018
mZxid = 0x25
mtime = Sat Dec 22 16:27:24 CST 2018
pZxid = 0x25
cversion = 0
dataVersion = 0
aclVersion = 4
ephemeralOwner = 0x0
dataLength = 2
numChildren = 0
[zk: localhost:2181(CONNECTED) 9] getAcl /auth //获取`/auth`的权限信息 ,其权限有增加了一个jack帐户,原来的cdz没有改变,意味着此时cdz,jack帐户都可以对其做操作
'digest,'cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=
: cra
'digest,'jack:p4FVWKzcf0HsYG6jAmAOvoHGCt8=
: cra
digest-scheme解读
digest-scheme与auth-shceme的最大区别就是,在设置用户的时候一个auth密码输入是明文形式,而digest是密文形式.
[zk: localhost:2181(CONNECTED) 11] create /digest 222 //创建一个digest节点
Created /digest
[zk: localhost:2181(CONNECTED) 12] getAcl /digest //获取节点权限信息 是默认world-scheme
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 13] setAcl /digest digest:cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=:ca //设置digest-scheme权限,形式上输入的是密文
cZxid = 0x32
ctime = Mon Dec 24 11:07:14 CST 2018
mZxid = 0x32
mtime = Mon Dec 24 11:07:14 CST 2018
pZxid = 0x32
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 14] getAcl /digest //获取节点权限信息 设置成功 其他的功能和auth一样,必须登录者才可以对其游对应的权限操作
'digest,'cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=
: ca
ip-scheme解读
填写ip作为限制,指定ip才可以进行访问节点,此在客户端限制经常被用到,但是有些真实环境下有可能有一些机器的IP会经常变化,所以这个限制需要根据线上机器的情况而定.
[zk: localhost:2181(CONNECTED) 37] create /ip 123 //创建一个节点
Created /ip
[zk: localhost:2181(CONNECTED) 38] setAcl /ip ip:127.0.0.1:ra //设置权限 通过 ip-scheme
cZxid = 0x43
ctime = Mon Dec 24 14:29:32 CST 2018
mZxid = 0x43
mtime = Mon Dec 24 14:29:32 CST 2018
pZxid = 0x43
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 39] getAcl /ip //获取权限信息, 设置成功
'ip,'127.0.0.1
: ra
[zk: localhost:2181(CONNECTED) 40] get /ip //因为本机就是 127.0.0.1 所以是可以访问的
123
cZxid = 0x43
ctime = Mon Dec 24 14:29:32 CST 2018
mZxid = 0x43
mtime = Mon Dec 24 14:29:32 CST 2018
pZxid = 0x43
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
super-scheme解读
super管理员,类似于Linux中root权限用户,可以对任何节点做任何操作,一般在公司中运维人员来掌控.
设置super管理员需要去修改zkServer.sh文件,通过关键字nohup
找到此行
nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}"
在其后面添加"-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.DigestAuthenticationProvider.superDigest=用户名:加密后密码"
完成之后就是这个样子:
nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}"
"-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs=" \
-cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
修改完成之后,进行保存然后重启zookeeper服务器就可以生效设置的用户super权限.
而-Dzookeeper.DigestAuthenticationProvider.superDigest
这个配置项的意思是什么呢,可以看得出,其实是在启动时,设置zookeeper的属性,下载源码搜索DigestAuthenticationProvider
这个类可以发现,其中有一个读取本地配置文件的静态常量private static final String superDigest = System.getProperty("zookeeper.DigestAuthenticationProvider.superDigest");
public 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(this.getScheme(), digest));
return Code.OK;
} catch (NoSuchAlgorithmException var5) {
LOG.error("Missing algorithm", var5);
return Code.AUTHFAILED;
}
}
总结ACL
实验下来,其实zk的权限是一个比较不完备的,因为你会发现一个坑,在的权限并不是一个继承关系.
[zk: localhost:2181(CONNECTED) 43] create /test 1111 //创建一个测试节点
Created /test
[zk: localhost:2181(CONNECTED) 44] getAcl /test //获取测试节点权限信息
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 45] setAcl /test digest:cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=:cra //通过digest获取设置节点权限
cZxid = 0x47
ctime = Mon Dec 24 15:08:54 CST 2018
mZxid = 0x47
mtime = Mon Dec 24 15:08:54 CST 2018
pZxid = 0x47
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 46] getAcl /test //获取测试节点权限信息查看设置是否成功
'digest,'cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=
: cra
[zk: localhost:2181(CONNECTED) 0] addauth digest cdz:123456 //登录帐户cdz
[zk: localhost:2181(CONNECTED) 1] get /test //查看节点信息 成功说明登录成功
1111
cZxid = 0x47
ctime = Mon Dec 24 15:08:54 CST 2018
mZxid = 0x47
mtime = Mon Dec 24 15:08:54 CST 2018
pZxid = 0x47
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 3] create /test/a 111 //在测试节点下创建 /test/a节点
Created /test/a
[zk: localhost:2181(CONNECTED) 4] getAcl /test/a //插件节点/test/a节点权限信息 发现其权限为'world,'anyone 说明并没有进行权限的继承.
'world,'anyone
: cdrwa
新开窗口2
[zk: localhost:2181(CONNECTED) 0] get /test/a //不登录直接获取节点信息 是可以的
111
cZxid = 0x4f
ctime = Mon Dec 24 15:50:34 CST 2018
mZxid = 0x4f
mtime = Mon Dec 24 15:50:34 CST 2018
pZxid = 0x4f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 1] create /test/a/b 1111 //创建节点也可以成功
Created /test/a/b
[zk: localhost:2181(CONNECTED) 2] delete /test/a/b //删除/test/a/b节点也可以成功
[zk: localhost:2181(CONNECTED) 3] ls /test/a //查看a下确实没有b节点
[]
[zk: localhost:2181(CONNECTED) 4] delete /test/a //但是想要删除a节点却不行,因为删除子节点需要看上一个父节点的权限
[zk: localhost:2181(CONNECTED) 5] addauth digest cdz:123456 //登录cdz用户
[zk: localhost:2181(CONNECTED) 6] delete /test/a //删除一样不能成功
Authentication is not valid : /test/a
[zk: localhost:2181(CONNECTED) 7] getAcl /test //查看权限,因为cdz这个用户并没有delete权限
'digest,'cdz:hGq0vr1Ww1PViRKdAf5fa9ua7q8=
: cra
[zk: localhost:2181(CONNECTED) 8] delete /test //此时直接删除test节点,一样不可以,因为test节点不为空 不能删除,所以就进入了一个死循环中,
//因为此时我们是知道cdz这个用户密码的,但是很多时候可能我们并不知道,但是我们却可以操作test/a节点,
//但是却不能删除他,这就照成了假象,而且有时候即使我们不知道密码情况下,如果/test下没有节点且/test的父节点没有设置其他权限
//是可以直接删除/test节点的,但此时/test节点下有子节点,这时如果密码忘记就会很难办,并且就算有密码情况下,也需要重新设置权限信息
//故在节点权限方面,对于r->read,d->delete的权限,需要在测试节点上测试完成后再将权限放在正式环境中
Node not empty: /test