ZooKeeper 笔记(5) ACL(Access Control List)访问控制列表

zk做为分布式架构中的重要中间件,通常会在上面以节点的方式存储一些关键信息,默认情况下,所有应用都可以读写任何节点,在复杂的应用中,这不太安全,ZK通过ACL机制来解决访问权限问题,详见官网文档:http://zookeeper.apache.org/doc/r3.4.6/zookeeperProgrammers.html#sc_ZooKeeperAccessControl

总体来说,ZK的节点有5种操作权限:

CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda(即:每个单词的首字符缩写)

注:这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限

 

身份的认证有4种方式:

world:默认方式,相当于全世界都能访问
auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
ip:使用Ip地址认证

 

Cli命令行下可以这样测试:

通过getAcl命令可以发现,刚创建的节点,默认是 world,anyone的认证方式,具有cdrwa所有权限

 

继续捣鼓:

先给/test增加了user1:+owfoSBn/am19roBPzR1/MfCblE的只读(r)权限控制,

说明:setAcl /test digest:用户名:密码:权限 给节点设置ACL访问权限时,密码必须是加密后的内容,这里的+owfoSBn/am19roBPzR1/MfCblE=,对应的原文是12345 (至于这个密文怎么得来的,后面会讲到,这里先不管这个),设置完Acl后,可以通过

getAcl /节点路径 查看Acl设置

然后get /test时,提示认证无效,说明访问控制起作用了,接下来:

addauth digest user1:12345 给"上下文"增加了一个认证用户,即对应刚才setAcl的设置

然后再 get /test 就能取到数据了

最后 delete /test 成功了!原因是:根节点/默认是world:anyone:crdwa(即:全世界都能随便折腾),所以也就是说任何人,都能对根节点/进行读、写、创建子节点、管理acl、以及删除子节点(再次映证了ACL中的delete权限应该理解为对子节点的delete权限)

 

刚才也提到了,setAcl /path digest这种方式,必须输入密码加密后的值,这在cli控制台上很不方便,所以下面这种方式更常用:

注意加框的部分,先用addauth digest user1:12345 增加一个认证用户,然后用 setAcl /test auth:user1:12345:r 设置权限,跟刚才的效果一样,但是密码这里输入的是明文,控制台模式下手动输入更方便。

 

好了,揭开加密规则:

    static public String generateDigest(String idPassword)
            throws NoSuchAlgorithmException {
        String parts[] = idPassword.split(":", 2);
        byte digest[] = MessageDigest.getInstance("SHA1").digest(
                idPassword.getBytes());
        return parts[0] + ":" + base64Encode(digest);
    }

就是SHA1加密,然后base64编码  

 

代码使用:

zookeeper有一个很好用的客户端开源项目zkclient,官网地址为:http://github.com/zkclient ,其最新片0.7-dev已经支持ACL了(旧0.1版无此功能,所以推荐使用最新版),使用方法:

git clone https://github.com/sgroschupf/zkclient (把代码拉到本地)

修改

build.gradle 找到92行

uploadArchives {

    repositories.mavenDeployer {
        //repository(url: "file:///tmp/mavenRepo")
        repository(url: "http://172.21.129.56:8081/nexus/content/repositories/thirdparty/") {
            authentication(userName: admin, password: admin123)
        }
        beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
        pom.project {
            name 'ZkClient'
            packaging 'jar'
            description 'A zookeeper client, that makes life a little easier.'
            url 'https://github.com/sgroschupf/zkclient'
            licenses {
                license {
                    name 'The Apache Software License, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    distribution 'repo'
                }
            }
            scm {
                url 'https://github.com/sgroschupf/zkclient'
                connection 'scm:git:git://github.com/sgroschupf/zkclient.git'
                developerConnection 'scm:git:https://github.com/sgroschupf/zkclient.git'
            }
            developers {
                developer {
                    id 'sgroschupf'
                    name 'Stefan Groshupf'
                }
                developer {
                    id 'pvoss'
                    name 'Peter Voss'
                }
                developer {
                    id 'jzillmann'
                    name 'Johannes Zillmann'
                }
            }
        }
    }
}

把这一段干掉,否则编译时会出错

然后(windows环境,把./gradew 换成gradlew)

./gradlew test (测试)

./gradlew jars (编译生成jar包)

./gradlew install (安装到本机maven仓库)  

 

新建一个maven项目,pom.xml参考下面设置:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6
 7     <groupId>yjmyzz</groupId>
 8     <artifactId>zkclient-demo</artifactId>
 9     <version>1.0</version>
10
11     <dependencies>
12         <dependency>
13             <groupId>org.apache.zookeeper</groupId>
14             <artifactId>zookeeper</artifactId>
15             <version>3.4.6</version>
16         </dependency>
17
18         <dependency>
19             <groupId>com.101te</groupId>
20             <artifactId>zkclient</artifactId>
21             <version>0.7</version>
22             <classifier>dev</classifier>
23         </dependency>
24
25         <dependency>
26             <groupId>log4j</groupId>
27             <artifactId>log4j</artifactId>
28             <version>1.2.17</version>
29         </dependency>
30
31     </dependencies>
32
33
34 </project>

View Code

 

然后写一段代码测试一下:

package yjmyzz.zk;

import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Main {

    private static final String zkAddress = "localhost:2181";
    private static final String testNode = "/test";
    private static final String readAuth = "read-user:123456";
    private static final String writeAuth = "write-user:123456";
    private static final String deleteAuth = "delete-user:123456";
    private static final String allAuth = "super-user:123456";
    private static final String adminAuth = "admin-user:123456";
    private static final String digest = "digest";

    private static void initNode() throws NoSuchAlgorithmException {
        ZkClient zkClient = new ZkClient(zkAddress);
        zkClient.addAuthInfo(digest, allAuth.getBytes());

        if (zkClient.exists(testNode)) {
            zkClient.delete(testNode);
            System.out.println("节点删除成功!");
        }

        List<ACL> acls = new ArrayList<ACL>();
        acls.add(new ACL(ZooDefs.Perms.ALL, new Id(digest, DigestAuthenticationProvider.generateDigest(allAuth))));
        acls.add(new ACL(ZooDefs.Perms.READ, new Id(digest, DigestAuthenticationProvider.generateDigest(readAuth))));
        acls.add(new ACL(ZooDefs.Perms.WRITE, new Id(digest, DigestAuthenticationProvider.generateDigest(writeAuth))));
        acls.add(new ACL(ZooDefs.Perms.DELETE, new Id(digest, DigestAuthenticationProvider.generateDigest(deleteAuth))));
        acls.add(new ACL(ZooDefs.Perms.ADMIN, new Id(digest, DigestAuthenticationProvider.generateDigest(adminAuth))));
        zkClient.createPersistent(testNode, "test-data", acls);

        System.out.println(zkClient.readData(testNode));
        System.out.println("节点创建成功!");
        zkClient.close();
    }

    private static void readTest() {
        ZkClient zkClient = new ZkClient(zkAddress);

        try {
            System.out.println(zkClient.readData(testNode));//没有认证信息,读取会出错
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        try {
            zkClient.addAuthInfo(digest, adminAuth.getBytes());
            System.out.println(zkClient.readData(testNode));//admin权限与read权限不匹配,读取也会出错
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        try {
            zkClient.addAuthInfo(digest, readAuth.getBytes());
            System.out.println(zkClient.readData(testNode));//只有read权限的认证信息,才能正常读取
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        zkClient.close();
    }

    private static void writeTest() {
        ZkClient zkClient = new ZkClient(zkAddress);

        try {
            zkClient.writeData(testNode, "new-data");//没有认证信息,写入会失败
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        try {
            zkClient.addAuthInfo(digest, writeAuth.getBytes());
            zkClient.writeData(testNode, "new-data");//加入认证信息后,写入正常
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        try {
            zkClient.addAuthInfo(digest, readAuth.getBytes());
            System.out.println(zkClient.readData(testNode));//读取新值验证
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }

        zkClient.close();
    }

    private static void deleteTest() {
        ZkClient zkClient = new ZkClient(zkAddress);
        //zkClient.addAuthInfo(digest, deleteAuth.getBytes());
        try {
            //System.out.println(zkClient.readData(testNode));
            zkClient.delete(testNode);
            System.out.println("节点删除成功!");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        zkClient.close();
    }

//    private static void deleteTest2() throws IOException, InterruptedException, KeeperException {
//        //使用zookeeper原生的API进行删除(注:delete权限指有没有权限删除子节点)
//        ZooKeeper zk = new ZooKeeper(zkAddress, 300000, new DemoWatcher());
//        zk.delete(testNode, -1);
//        System.out.println("节点删除成功");
//        zk.close();
//    }
//
//    static class DemoWatcher implements Watcher {
//        @Override
//        public void process(WatchedEvent event) {
//            System.out.println("----------->");
//            System.out.println("path:" + event.getPath());
//            System.out.println("type:" + event.getType());
//            System.out.println("stat:" + event.getState());
//            System.out.println("<-----------");
//        }
//    }

    private static void changeACLTest() {
        ZkClient zkClient = new ZkClient(zkAddress);
        //注:zkClient.setAcl方法查看源码可以发现,调用了readData、setAcl二个方法
        //所以要修改节点的ACL属性,必须同时具备read、admin二种权限
        zkClient.addAuthInfo(digest, adminAuth.getBytes());
        zkClient.addAuthInfo(digest, readAuth.getBytes());
        try {
            List<ACL> acls = new ArrayList<ACL>();
            acls.add(new ACL(ZooDefs.Perms.ALL, new Id(digest, DigestAuthenticationProvider.generateDigest(adminAuth))));
            zkClient.setAcl(testNode, acls);
            Map.Entry<List<ACL>, Stat> aclResult = zkClient.getAcl(testNode);
            System.out.println(aclResult.getKey());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        zkClient.close();
    }

    public static void main(String[] args) throws Exception {

        initNode();

        System.out.println("---------------------");

        readTest();

        System.out.println("---------------------");

        writeTest();

        System.out.println("---------------------");

        changeACLTest();

        System.out.println("---------------------");

        deleteTest();

        //deleteTest2();

    }
}

 

输出结果:

test-data
节点创建成功!
---------------------
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test
test-data
---------------------
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test
new-data
---------------------
[31,s{'digest,'admin-user:mAlW21Phn07yOvWnKJYq2sCMoZw=}
]
---------------------
节点删除成功!

从zkclient的使用结果看,与cli操作效果一样。  

 

最后:关于多级节点之间的ACL,并非继承关系,但是也有些一联系,这是初次接触ACL中比较难理解的地方:

从这张图上可以发现,子节点/a/b的控制权限范围(全世界都能做任何事)可以超出父节点的范围(仅限:user-a:pwd:a具有read/admin权限)

继续,看上面的这4条红线标注的地方,从上向下一个个解释:

红线1:因为/a只有user-a:pwd-a有ra权限,即:没用户具有c(create)权限,所以不能创建子节点

红线2:因为/a/b为world:anyone:cdrwa权限,即无限制,所以在/a/b下创建子节点b1,地球人已经无法阻止,创建成功

红线3:给/a/b/b1指定了user-b1:pwd-b1的da权限(即:delete+admin)

(注:重温下前面提到的setAcl 二种模式,

一种是setAcl /path digest:username:encrypedpwd:crwda 用这种方式时,encrypedpwd用户必须是密文,

另一种方式是先addauth digest:usrname:password 先把授权信息加入上下文,这里password用的是明文,然后再setAcl /path auth:username:password:crdwa

所以如果在cli控制台测试,强烈建议用第二种方式,否则象上图中的方式用错了方式,pwd-b1在zk中被认为是密文,要解密出来几乎不可能,所以设置后,相当于这个节点就废了,因为你不知道密码,要操作该节点时,提供不了正确的认证信息)

红线4:还是刚才的理由,因为/a/b为world:anyone:cdrwa,没有限制,所以删除其下的子节点不受阻挡。

从上图可以看出,无法get父节点的内容,但是可以get子节点的内容,再次说明父、子节点的权限没直接关系,但是做delete时,上面的例子却遇到了麻烦:

想删除/a/b时,由于父节点/a的ACL列表里,只有ra权限,没有d权限,所以无法删除子节点。想删除/a时,发现下面还有子节点b,节点非空无法删除,所以这个示例就无解了(因为根据前面的操作,密码也还原不出来,也就无法修改ACL属性),而根节点/也无法删除,解决办法,只能到data目录里清空所有数据,再重启zk,但是这样就相当于所有数据全扔了,所以在设计ACL时,对于delete权限,要谨慎规划,在测试zk集群上做好测试,再转到生产环境操作。

 

最后给一些权限组合的测试结果:

要修改某个节点的ACL属性,必须具有read、admin二种权限

要删除某个节点下的子节点,必须具有对父节点的read权限,以及父节点的delete权限

 

参考文章:
https://ihong5.wordpress.com/2014/07/10/apache-zookeeper-acl-access-control-list-getting-permission-sets/

https://ihong5.wordpress.com/2014/07/24/apache-zookeeper-setting-acl-in-zookeeper-client/

https://ihong5.wordpress.com/2014/06/24/znode-types-and-how-to-create-read-delete-and-write-in-zookeeper-via-zkclient/

http://zookeeper.apache.org/doc/r3.4.6/zookeeperProgrammers.html#sc_ZooKeeperAccessControl

 

时间: 2024-11-05 22:00:25

ZooKeeper 笔记(5) ACL(Access Control List)访问控制列表的相关文章

18.9. ACL - Access Control List

$ sudo modprobe loop $ dd if=/dev/zero of=file bs=1k count=100 $ sudo losetup /dev/loop0 file $ sudo mkfs.ext3 /dev/loop0 $ sudo mkdir /mnt/loop $ sudo mount -o rw,acl /dev/loop0 /mnt/loop/ $ sudo chown neo.neo -R /mnt/loop $ cd /mnt/loop/ 18.9.1. ge

ACM(访问控制模型),Security Identifiers(SID),Security Descriptors(安全描述符),ACL(访问控制列表),Access Tokens(访问令牌)

对于<windows核心编程>中的只言片语无法驱散心中的疑惑.就让MSDN中的解释给我们一盏明灯吧.如果要很详细的介绍,还是到MSDN仔细的看吧,我只是大体用容易理解的语言描述一下.   windows的安全访问控制(ACM,access control mode)是由两部分组成的.一个是访问令牌(access tokens),另一个是安全描述符(security identifiers).   访问令牌是欲进行访问的进程使用的表明自己身份和特权的信息数据. 安全描述符是欲被访问的安全对象的相

RHCSA 系列(七): 使用 ACL(访问控制列表) 和挂载 Samba/NFS 共享

在上一篇文章(RHCSA 系列(六))中,我们解释了如何使用 parted 和 ssm 来设置和配置本地系统存储. RHCSA 系列: 配置 ACL 及挂载 NFS/Samba 共享 – Part 7 我们也讨论了如何创建和在系统启动时使用一个密码来挂载加密的卷.另外,我们告诫过你要避免在挂载的文件系统上执行危险的存储管理操作.记住了这点后,现在,我们将回顾在 RHEL 7 中最常使用的文件系统格式,然后将涵盖有关手动或自动挂载.使用和卸载网络文件系统(CIFS 和 NFS)的话题以及在你的操作

编程实现遍历ACL访问控制列表检查进程访问权限

阅读本文的朋友需要对Windows访问控制模型有初步的了解,了解Token(访问令牌),ACL(访问控制列表),DACL(选择访问控制列表),ACE(访问控制列表项)等与访问控制模型相关的名词含义及之间的关系,当然我也会在文中简要科普一下ACM. 写这篇文章的目的主要是最近在写一个Win下本地提权的东西,涉及到了对ACL的操作,以前对ACL总是避而远之,Windows访问控制模型很复杂很头疼一个API会牵出一大把初始化要用的API.毕竟涉及到用户访问的安全,肯定不能让编程人员随意更改这些机制,复

新的RBAC:基于资源的权限管理(Resource-Based Access Control)

本文讨论以角色概念进行的权限管理策略及主要以基于角色的机制进行权限管理是远远不够的.同时将讨论一种更好的权限管理方式. What is a Role? 什么是角色 当说到程序的权限管理时,人们往往想到角色这一概念.角色是代表一系列行为或责任的实体,用于限定你在软件系统中能做什么.不能做什么.用户帐号往往与角色相关联,因此,一个用户在软件系统中能"做"什么取决于与之关联的各个角色. 例如,一个用户以关联了"项目管理员"角色的帐号登录系统,那这个用户就可以做项目管理员能

Declarations and Access Control (1)

access 1) Declarations and Access Control Objective 1Write code that declares, constructs and initializes arrays of any base type using any of the permitted forms, both for declaration and for initialization. 1.    Arrays are Java objects. (An object

网络题:ACL访问控制列表的实现

题目需求: 首先给4台PC 配上ip地址 这里基本都会 不多讲解 列表的实现-"> 然后做vlan划分 0和1 划分到vlan2 里 pc2和3 划分到vlan3里 直接上图 我就不写步骤了.然后我们做单臂路由,有的同学不会,我前面的文档有专门的 单臂路由和vlan'的文章,今天主要acl访问控制列表.

Swift中的Access Control权限控制介绍

  这篇文章主要介绍了Swift中的Access Control权限控制介绍,本文讲解了private.internal.public三个关键字的使用,需要的朋友可以参考下 如果您之前没有接触过权限控制,先来听一个小故事: 小明是五道口工业学院的一个大一新生,最近他有点烦恼,因为同屋经常用他的热水壶,好像那是自己家的一样,可是碍于同学情面,又不好意思说.直到有一天,他和学姐小K吐槽. 学姐听了之后,说:大学集体生活里面,大部分东西都是默认室友可以共用的.如果你不想别人拿,我可以帮你做封印,只要打

【原创】RabbitMQ 之 Access Control(翻译)

Access Control  When the server first starts running, and detects that its database is uninitialised or has been deleted, it initialises a fresh database with the following resources: 当服务器启动运行后,检测到所使用的数据库未进行过初始化,或者被删除了,则会使用如下资源初始化一个新的数据库:  a virtual