天天看点

ZooKeeper系统模型之ACL。权限模式:Scheme授权对象:ID权限:PermissionACL管理

         ZooKeeper的ACL权限控制和Unix/Linux操作系统的ACL有一些区别,读者可以从三个方面来理解ACL机制,分别是:权限控制(Scheme)、授权对象(ID)和权限(Permission),通常使用“scheme:id:permission”来标识一个有效的ACL信息。

权限模式:Scheme

        权限模式用来确定权限验证中使用校验策略。在ZooKeeper中,开发人员使用最多的就是以下四种权限模式。

IP

IP模式通过IP地址粒度来进行权限控制,例如配置了“ip:192.168.0.110”,即表示权限控制都是针对这个IP地址的。同时,IP模式也支持按照网段的方式进行配置,例如“ip:192.168.0.1/24”表示针对192.168.0.*这个IP段进行权限控制。

Digest

Digest是最常用的权限控制模式,也更符合我们对于权限控制的认识,其以类似于“username:password”形式的权限标识来进行权限配置,便于区分不同应用来进行权限控制。

当我们通过“username:password”形式配置了权限标识后,ZooKeeper会对其先后进行两次编码处理,分别是SHA-1算法加密和BASE64编码,其具体实现由DigestAuthenticationProvider.generateDigest(String idPassword)函数进行封装,下面代码所示为使用该函数进行“username:password”编码的一个实例。

public class DigestAuthenticationProviderUsage {

    public static void main(String[] args) {

        try {

            System.out.println(DigestAuthenticationProvider.generateDigest("foo:zk-book"));

        } catch (NoSuchAlgorithmException e) {

            e.printStackTrace();

        }

    }

}

运行程序,输出结果如下:

ZooKeeper系统模型之ACL。权限模式:Scheme授权对象:ID权限:PermissionACL管理
从上面的运行结果中可以看出,“username:password”最终会被混淆为一个无法辨识的字符串。

World

World是一种最开放的权限控制模式,从其名字中也可以看出,事实上这种权限控制方式几乎没有任何作用,数据节点的访问权限对所有用户开放,即所有用户都可以在不进行任何权限校验的情况下操作ZooKeeper上的数据。另外,World模式也可以看作是一种特殊的Digest模式,他只有一个权限标识,即“world:anyone”。

Super

        Super模式,顾名思义就是超级用户的意思,也是一种特殊的Digest模式。在Super模式下,超级用户可以对任意ZooKeeper上的数据节点进行任何操作。

授权对象:ID

        授权对象指的是权限赋予的用户或一个指定实体,例如IP地址或是机器等。在不同的权限模式下,授权对象是不同的,下表中列出了各个权限模式和授权对象之间的对应关系。

权限模式 授权对象
IP 通常是一个IP地址或是IP段,例如“192.168.0.110”或“192.168.0.1/24”
Digest 自定义,通常是“username:BASE64(SHA-1(username:password))”,例如“foo:kWN6aNSbjcKWPqjiV7cg0N24raU=”
World 只有一个ID:"anyone"
Super 与Digest模式一致

权限:Permission

        权限就是指那些通过权限检查后可以被允许执行的操作。在ZooKeeper中,所有对数据的操作权限分为以下五大类:

  • CREATE(C):数据节点的创建权限,允许授权对象在该数据节点下创建子节点。
  • DELETE(D):子节点的删除权限,允许授权对象删除该数据节点的子节点。
  • READ(R):数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等。
  • WRITE(W):数据节点的更新权限,允许授权对象对该数据节点进行更新操作。
  • ADMIN(A):数据节点的管理权限,允许授权对象对该数据节点进行ACL相关的设置操作。

权限扩展体系

        在上文中,我们已经讲解了ZooKeeper默认提供的IP、Digest、World和Super这四种权限模式,在绝大部分的场景下,这四种权限模式已经能够很好的实现权限控制的目的。同时ZooKeeper提供了特殊的权限控制插件体系,允许开发人员通过指定方式对ZooKeeper的权限进行扩展。这些扩展的权限控制方式就像插件一样插入到ZooKeeper的权限体系中去,因此在ZooKeeper的官方文档中,也称该机制为“Pluggable ZooKeeper Authentication”。

实现自定义权限控制器

        要实现自定义权限控制器非常简单,ZooKeeper定义了一个标准权限控制器需要实现的接口:org.apache.zookeeper.server.auth.AuthenticationProvider,其接口定义如下图所示。

ZooKeeper系统模型之ACL。权限模式:Scheme授权对象:ID权限:PermissionACL管理
ZooKeeper系统模型之ACL。权限模式:Scheme授权对象:ID权限:PermissionACL管理

        用户可以基于该接口来进行自定义权限控制器的实现。事实上,在前面内容中提到的几个权限模式,对应的就是ZooKeeper自带的DigestAuthenticationProvider和IPAuthenticationProvider两个权限控制器。

注册自定义权限控制器

        完成自定义权限控制器的开发后,接下来就需要将该权限控制器注册到ZooKeeper服务器中去了。ZooKeeper支持通过系统属性和配置文件两种方式来注册自定义的权限控制器。

  • 系统属性 -Dzookeeper.authProvider.X

在ZooKeeper启动参数中配置类似于如下的系统属性:

-Dzookeeper.authProvider.1=com.zkbook.CustomAuthenticationProvider

  • 配置文件方式

在zoo.cfg配置文件中配置类似于如下的配置项:

authProvider.1=com.zkbook.CustomAuthenticationProvider

        对于权限控制器的注册,ZooKeeper采用了延迟加载的策略,即只有在第一次处理包含权限控制的客户端请求时,才会进行权限控制器的初始化。同时,ZooKeeper还会将所有的权限控制器都注册到ProviderRegistry中去。在具体的实现中,ZooKeeper首先会将DigestAuthenticationProvider和IPAuthenticationProvider这两个默认的控制器初始化,然后通过扫描zookeeper.authProvider.这一系统属性,获取到所有用户配置的自定义权限控制器,并完成其初始化。

ACL管理

        讲解完ZooKeeper的ACL及其扩展机制后,我们来看看如何进行ACL管理。

设置ACL

        通过zkCli脚本登录ZooKeeper服务器后,可以通过两种方式进行ACL的设置。一种是在数据节点创建的同时进行ACL权限的设置,命名格式如下:

  • create [-s] [-e] path data acl

        具体使用如下所示。

ZooKeeper系统模型之ACL。权限模式:Scheme授权对象:ID权限:PermissionACL管理

        另一种方式则是使用setAcl命名单独对已经存在的数据节点进行ACL设置:

  • setAcl path acl

         具体使用如下所示。

ZooKeeper系统模型之ACL。权限模式:Scheme授权对象:ID权限:PermissionACL管理

Super模式的用法

        根据ACL权限控制的原理,一旦对一个数据节点设置了ACL权限控制,那么其他没有被授权的ZooKeeper客户端将无法访问该数据节点,这的确很好的保证了ZooKeeper的数据安全。但同时,ACL权限控制也给ZooKeeper的运维人员带来了一个困扰:如果一个持久数据节点包含了ACL权限控制,而其创建者客户端已经退出或已不再使用,那么这些数据节点该如何清理呢?这个时候,就需要在ACL的Super模式下,使用超级管理员权限来进行处理了。要使用超级管理员权限,首先需要在ZooKeeper服务器上开启Super模式,方法是在ZooKeeper服务器启动的时候,添加如下系统属性:

  • -Dzookeeper.DigestAuthenticationProvider.superDigest=foo:kWN6aNSbjcKWPqjiV7cg0N24raU=

        其中,“foo”代表了一个超级管理员的用户名;“kWN6aNSbjcKWPqjiV7cg0N24raU=”是可变的,由ZooKeeper的系统管理员来进行自主配置,此例中使用的是“foo:zk-book”的编码。完成对ZooKeeper服务器的Super模式的开启后,就可以在应用程序中使用了,下面是一个使用超级管理员权限操作ZooKeeper数据节点的示例程序。

public class AuthSample_Super {

    final static String PATH = "/zk-book";

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

        ZooKeeper zooKeeper1 = new ZooKeeper("127.0.0.1:2181", 5000, null);

        zooKeeper1.addAuthInfo("digest", "foo:true".getBytes());

        // 判断是否为空

        if (zooKeeper1.exists(PATH, false) == null) {

            zooKeeper1.create(PATH, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);

        }

        // 用管理员权限

        ZooKeeper zooKeeper2 = new ZooKeeper("127.0.0.1:2181", 500000, null);

        zooKeeper2.addAuthInfo("digest", "foo:zk-book".getBytes());

        System.out.println(zooKeeper2.getData(PATH, false, null));

        // 用其他用户访问

        ZooKeeper zooKeeper3 = new ZooKeeper("127.0.0.1:2181", 50000, null);

        zooKeeper3.addAuthInfo("digest", "foo:false".getBytes());

        System.out.println(zooKeeper3.getData(PATH, false, null));

    }

}

        从上面的输出结果中,我们可以看出,由于“foo:zk-book”是一个超级管理员账户,因此能够针对一个受权限控制的数据节点zk-book随意进行操作,但是对于“foo:false”这个普通用户,就无法通过权限校验了。

继续阅读