zk学习笔记二

acl(access control list)权限控制

Posted by CDz on December 21, 2018

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 代表超级管理员拥有所有权限
  • 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