5.2 ACL 权限控制


文档摘要

5.2 ACL 权限控制 第五章:Zookeeper 高级特性与优化 5.2 ACL 权限控制 在分布式系统中,数据的安全性至关重要。ZooKeeper 作为分布式协调服务,存储着重要的元数据和配置信息,因此,对其进行有效的权限控制显得尤为重要。ZooKeeper 提供了强大的 ACL (Access Control List,访问控制列表) 机制,用于保护 ZooKeeper 集群中数据的安全性,防止未经授权的访问和操作。 本章节将深入探讨 ZooKeeper 的 ACL 权限控制机制,包括其基本概念、组成部分、权限类型、应用场景、代码实践以及最佳实践等方面,帮助您全面理解和掌握 ZooKeeper 的权限控制,构建更加安全可靠的分布式系统。 5.2.1 ACL 概述 什么是 ACL?

5.2 ACL 权限控制

第五章:Zookeeper 高级特性与优化

5.2 ACL 权限控制

在分布式系统中,数据的安全性至关重要。ZooKeeper 作为分布式协调服务,存储着重要的元数据和配置信息,因此,对其进行有效的权限控制显得尤为重要。ZooKeeper 提供了强大的 ACL (Access Control List,访问控制列表) 机制,用于保护 ZooKeeper 集群中数据的安全性,防止未经授权的访问和操作。

本章节将深入探讨 ZooKeeper 的 ACL 权限控制机制,包括其基本概念、组成部分、权限类型、应用场景、代码实践以及最佳实践等方面,帮助您全面理解和掌握 ZooKeeper 的权限控制,构建更加安全可靠的分布式系统。

5.2.1 ACL 概述

什么是 ACL?

ACL,即访问控制列表,是一种用于控制用户或进程对特定资源访问权限的机制。在 ZooKeeper 中,ACL 用于控制客户端对 ZooKeeper 节点(ZNode)的访问权限,例如创建节点、读取节点数据、修改节点数据、删除节点以及管理子节点等。

为什么需要 ACL?

默认情况下,ZooKeeper 集群中的节点对所有客户端都是开放的,这意味着任何连接到 ZooKeeper 集群的客户端都可以对所有节点进行操作,这显然是不安全的。在实际应用中,我们通常需要根据不同的业务场景和安全需求,对不同的节点设置不同的访问权限,例如:

  • 数据隔离: 不同的应用或服务可能需要使用 ZooKeeper 存储各自的数据,为了防止数据互相干扰或泄露,需要通过 ACL 将不同应用的数据隔离开来,只允许特定的应用访问其自身的数据节点。

  • 权限分级: 对于同一个应用,不同的用户或角色可能需要拥有不同的权限。例如,管理员可以拥有所有节点的完全控制权限,而普通用户可能只能读取部分节点的数据。

  • 安全审计: 通过合理的 ACL 配置,可以有效地记录和审计用户的操作行为,追踪潜在的安全风险。

  • 防止恶意操作: ACL 可以有效地防止未经授权的客户端恶意修改或删除 ZooKeeper 中的重要数据,保障系统的稳定性。

ACL 的优势:

  • 细粒度权限控制: ZooKeeper ACL 可以精确到每个节点,甚至可以针对不同的操作类型(读、写、创建、删除、管理)进行细粒度的权限控制。

  • 灵活性和可扩展性: ZooKeeper 支持多种 ACL 策略(Scheme),可以根据不同的认证方式和安全需求进行灵活配置。

  • 易于管理: ZooKeeper 提供了丰富的 API 和命令行工具,方便管理员进行 ACL 的管理和维护。

5.2.2 ACL 组成

ZooKeeper 的 ACL 由三个核心部分组成,通常表示为 <scheme>:<id>:<permission> 三元组:

  • Scheme (认证模式): 用于确定身份验证的方式。ZooKeeper 提供了多种 Scheme,常见的包括:

    • world: 最开放的 Scheme,表示任何客户端都可以访问,相当于完全开放权限。通常用于测试环境或不需要权限控制的场景。

    • auth: 已认证的用户才能访问。客户端必须先通过 addAuthInfo 接口进行身份认证,才能访问带有 auth 权限的节点。

    • digest: 使用 "username:password" 形式的用户名密码认证。客户端需要提供正确的用户名和密码才能访问。

    • ip: 基于客户端 IP 地址进行认证。只有来自指定 IP 地址或 IP 段的客户端才能访问。

    • sasl: 使用 Kerberos 或其他 SASL 认证机制进行认证,通常用于企业级安全环境。

  • ID (身份标识): 用于指定被授权的用户或实体。ID 的格式取决于 Scheme:

    • world: ID 只有一个值 anyone,表示任何人。

    • auth: ID 是已认证的用户名(由客户端通过 addAuthInfo 提供)。

    • digest: ID 是 "username:password" 字符串。

    • ip: ID 是 IP 地址或 IP 段,例如 192.168.1.1192.168.1.0/24

    • sasl: ID 是 Kerberos principal 或其他 SASL 认证机制的身份标识。

  • Permission (权限类型): 用于指定被授权的操作类型。ZooKeeper 定义了以下五种基本权限:

    • CREATE (c): 创建子节点的权限。拥有该权限的用户可以在该节点下创建子节点。

    • READ (r): 读取节点数据的权限。拥有该权限的用户可以读取节点的数据和子节点列表。

    • WRITE (w): 修改节点数据的权限。拥有该权限的用户可以修改节点的数据。

    • DELETE (d): 删除子节点的权限。拥有该权限的用户可以删除该节点下的子节点。注意,删除节点本身的权限由父节点的 WRITE 权限控制。

    • ADMIN (a): 管理节点 ACL 的权限。拥有该权限的用户可以设置和修改该节点的 ACL。

Mermaid 图示 ACL 组成:

权限组合:

可以将多个权限组合在一起,例如 crwda 表示同时拥有创建、读取、写入、删除和管理权限。

默认 ACL:

默认情况下,如果创建节点时没有指定 ACL,ZooKeeper 会使用 OPEN_ACL_UNSAFE,它等同于 world:anyone:cdrwa,即任何人拥有所有权限,这在生产环境中通常是不安全的。

5.2.3 ACL 权限类型详解

ZooKeeper 定义了五种权限类型,它们分别控制着对 ZNode 的不同操作:

  1. CREATE (c): 创建子节点的权限。

    • 拥有 CREATE 权限的用户可以在目标节点下创建新的子节点。

    • 如果用户没有 CREATE 权限,则无法在目标节点下创建子节点,创建操作会失败。

  2. READ (r): 读取节点数据和子节点列表的权限。

    • 拥有 READ 权限的用户可以:

      • 获取目标节点的数据 (getData)。

      • 列出目标节点的子节点 (getChildren)。

      • 监听目标节点的数据变化 (getData with watcher)。

      • 监听目标节点的子节点变化 (getChildren with watcher)。

    • 如果用户没有 READ 权限,则无法读取节点数据和子节点列表,读取操作会失败。

  3. WRITE (w): 修改节点数据的权限。

    • 拥有 WRITE 权限的用户可以修改目标节点的数据 (setData)。

    • 如果用户没有 WRITE 权限,则无法修改节点数据,修改操作会失败。

  4. DELETE (d): 删除子节点的权限。

    • 拥有 DELETE 权限的用户可以删除目标节点下的 子节点 (delete on child node)。

    • 注意: 删除节点本身的权限并不是由 DELETE 权限控制,而是由 父节点的 WRITE 权限 控制。也就是说,要删除一个节点,用户必须拥有 父节点的 WRITE 权限目标节点本身的 DELETE 权限

    • 如果用户没有 DELETE 权限,则无法删除子节点,删除操作会失败。

  5. ADMIN (a): 管理节点 ACL 的权限。

    • 拥有 ADMIN 权限的用户可以:

      • 获取目标节点的 ACL 信息 (getACL)。

      • 设置目标节点的 ACL 信息 (setACL)。

    • 如果用户没有 ADMIN 权限,则无法管理节点 ACL,管理操作会失败。

权限类型总结表格:

权限类型 缩写 描述 适用操作
CREATE c 创建子节点的权限 create (在目标节点下创建子节点)
READ r 读取节点数据和子节点列表的权限 getData, getChildren, exists (读取数据), watchers
WRITE w 修改节点数据的权限 setData
DELETE d 删除子节点的权限 delete (删除目标节点的子节点)
ADMIN a 管理节点 ACL 的权限 (获取和设置 ACL) getACL, setACL

权限检查流程:

当客户端尝试对 ZooKeeper 节点执行操作时,ZooKeeper 服务器会进行权限检查。权限检查流程大致如下:

  1. 获取客户端身份信息: 根据客户端连接时使用的 Scheme 和 ID,获取客户端的身份信息。

  2. 获取目标节点的 ACL: 从 ZooKeeper 存储中获取目标节点的 ACL 信息。

  3. 匹配 ACL 规则: 遍历目标节点的 ACL 列表,查找是否有与客户端身份信息匹配的 ACL 规则。

  4. 检查权限: 如果找到匹配的 ACL 规则,检查该规则中是否包含所需的操作权限。

  5. 授权或拒绝: 如果找到匹配的 ACL 规则且包含所需权限,则授权操作;否则,拒绝操作并返回 NoAuth 异常。

5.2.4 ACL 应用场景

ACL 权限控制在 ZooKeeper 中有着广泛的应用场景,以下是一些常见的例子:

  1. 服务注册与发现: 在服务注册与发现场景中,服务提供者需要将自己的服务信息注册到 ZooKeeper 中,服务消费者需要从 ZooKeeper 中获取服务列表。可以使用 ACL 来控制:

    • 服务注册节点: 只允许服务提供者创建和修改服务注册节点,服务消费者只能读取。

    • 服务消费节点: 只允许服务消费者读取服务列表,服务提供者不能修改。

  2. 配置中心: 在配置中心场景中,配置数据存储在 ZooKeeper 中,应用需要从 ZooKeeper 中读取配置数据。可以使用 ACL 来控制:

    • 配置节点: 只允许配置管理平台或管理员修改配置数据,应用只能读取配置数据。
  3. 分布式锁: 在分布式锁场景中,可以使用 ZooKeeper 节点来实现互斥锁。可以使用 ACL 来控制:

    • 锁节点: 只允许持有锁的客户端修改锁节点的数据,其他客户端只能读取或监听锁节点的变化。
  4. 队列和消息系统: 在队列和消息系统场景中,可以使用 ZooKeeper 节点来管理队列和消息。可以使用 ACL 来控制:

    • 队列节点: 生产者只能创建消息节点,消费者只能读取和删除消息节点。
  5. 命名空间隔离: 对于多租户或多团队共享 ZooKeeper 集群的场景,可以使用 ACL 来实现命名空间隔离,防止不同租户或团队的数据互相干扰。可以为每个租户或团队创建一个根节点,并设置 ACL,只允许该租户或团队的客户端访问该根节点下的数据。

  6. 敏感数据保护: 对于存储在 ZooKeeper 中的敏感数据,例如数据库连接信息、密钥等,必须使用 ACL 进行严格的权限控制,只允许授权的用户或服务访问。

5.2.5 ACL 操作实践

本节将通过代码示例和命令行操作,演示如何在 ZooKeeper 中进行 ACL 权限控制。

5.2.5.1 使用 Java API 操作 ACL

以下 Java 代码示例演示了如何使用 ZooKeeper Java API 进行 ACL 操作:

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; public class ACLDemo { private static final String ZK_ADDRESS = "localhost:2181"; private static final int SESSION_TIMEOUT = 5000; public static void main(String[] args) throws IOException, InterruptedException, KeeperException, NoSuchAlgorithmException { ZooKeeper zk = new ZooKeeper(ZK_ADDRESS, SESSION_TIMEOUT, new Watcher() { @Override public void process(WatchedEvent event) { System.out.println("事件类型:" + event.getType() + ", 路径:" + event.getPath()); } }); // 1. 创建开放权限节点 (world:anyone:cdrwa) String openNodePath = "/open_node"; createNodeWithOpenACL(zk, openNodePath); System.out.println("创建开放权限节点: " + openNodePath); // 2. 创建 Digest 认证节点 (digest:user1:password@cdrwa) String digestNodePath = "/digest_node"; String digestUser = "user1"; String digestPassword = "password"; createNodeWithDigestACL(zk, digestNodePath, digestUser, digestPassword); System.out.println("创建 Digest 认证节点: " + digestNodePath); // 3. 获取节点的 ACL 信息 getACLInfo(zk, openNodePath); getACLInfo(zk, digestNodePath); // 4. 使用 Digest 认证客户端访问 Digest 节点 ZooKeeper zkDigestClient = createDigestClient(digestUser, digestPassword); getDataWithAuth(zkDigestClient, digestNodePath); // 应该成功 getDataWithoutAuth(zk, digestNodePath); // 应该失败 zk.close(); zkDigestClient.close(); } // 创建节点并设置 OPEN_ACL_UNSAFE (world:anyone:cdrwa) private static void createNodeWithOpenACL(ZooKeeper zk, String path) throws KeeperException, InterruptedException { zk.create(path, "open data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // 创建节点并设置 Digest ACL (digest:username:password@permissions) private static void createNodeWithDigestACL(ZooKeeper zk, String path, String username, String password) throws KeeperException, InterruptedException, NoSuchAlgorithmException { List<ACL> acls = new ArrayList<>(); // 创建 Digest 认证的 ACL Id id = new Id("digest", DigestAuthenticationProvider.generateDigest(username + ":" + password)); acls.add(new ACL(ZooDefs.Perms.ALL, id)); // 设置所有权限 (cdrwa) acls.add(ZooDefs.Ids.READ_ACL_UNSAFE.get(0)); // 添加 world:anyone:r 默认权限,方便查看 zk.create(path, "digest data".getBytes(), acls, CreateMode.PERSISTENT); } // 获取节点的 ACL 信息 private static void getACLInfo(ZooKeeper zk, String path) throws KeeperException, InterruptedException { List<ACL> aclList = zk.getACL(path, new Stat()); System.out.println("节点 " + path + " 的 ACL 信息: " + aclList); } // 创建 Digest 认证的 ZooKeeper 客户端 private static ZooKeeper createDigestClient(String username, String password) throws IOException, InterruptedException { ZooKeeper zkClient = new ZooKeeper(ZK_ADDRESS, SESSION_TIMEOUT, new Watcher() { @Override public void process(WatchedEvent event) { System.out.println("Digest 客户端事件类型:" + event.getType() + ", 路径:" + event.getPath()); } }); zkClient.addAuthInfo("digest", (username + ":" + password).getBytes()); // 添加认证信息 return zkClient; } // 使用认证客户端获取数据 (应该成功) private static void getDataWithAuth(ZooKeeper zk, String path) throws KeeperException, InterruptedException { byte[] data = zk.getData(path, false, new Stat()); System.out.println("使用认证客户端获取节点 " + path + " 数据成功: " + new String(data)); } // 使用未认证客户端获取数据 (应该失败) private static void getDataWithoutAuth(ZooKeeper zk, String path) { try { zk.getData(path, false, new Stat()); System.out.println("使用未认证客户端获取节点 " + path + " 数据成功 (不应该发生)"); } catch (KeeperException.NoAuthException e) { System.out.println("使用未认证客户端获取节点 " + path + " 数据失败 (预期结果): " + e.getClass().getSimpleName() + ": " + e.getMessage()); } catch (KeeperException | InterruptedException e) { System.out.println("使用未认证客户端获取节点 " + path + " 数据失败 (其他异常): " + e.getClass().getSimpleName() + ": " + e.getMessage()); } } }

代码解释:

  1. 创建 ZooKeeper 连接: 使用 new ZooKeeper(ZK_ADDRESS, SESSION_TIMEOUT, watcher) 创建 ZooKeeper 客户端连接。

  2. 创建开放权限节点: 使用 ZooDefs.Ids.OPEN_ACL_UNSAFE 创建 ACL,表示任何人拥有所有权限。

  3. 创建 Digest 认证节点:

    • 创建 ArrayList<ACL> 列表用于存储 ACL 规则。

    • 使用 DigestAuthenticationProvider.generateDigest(username + ":" + password) 生成密码的摘要信息。

    • 创建 Id 对象,指定 Scheme 为 "digest",ID 为密码摘要。

    • 创建 ACL 对象,指定权限为 ZooDefs.Perms.ALL (cdrwa),ID 为上面创建的 Id 对象。

    • ACL 对象添加到 ACL 列表中。

    • 使用 zk.create(path, data, acls, CreateMode.PERSISTENT) 创建节点,并设置 ACL 列表。

    • 注意: 为了方便查看节点信息,代码示例中还添加了 ZooDefs.Ids.READ_ACL_UNSAFE.get(0),即 world:anyone:r,允许任何人读取节点数据和子节点列表。在实际生产环境中,需要根据安全需求进行更严格的权限控制。

  4. 获取 ACL 信息: 使用 zk.getACL(path, new Stat()) 获取节点的 ACL 信息。

  5. 创建 Digest 认证客户端:

    • 创建新的 ZooKeeper 客户端连接。

    • 使用 zkClient.addAuthInfo("digest", (username + ":" + password).getBytes()) 添加 Digest 认证信息。

  6. 使用认证客户端和未认证客户端访问 Digest 节点:

    • 使用认证客户端 zkDigestClient 获取 Digest 节点数据,应该成功。

    • 使用未认证客户端 zk 获取 Digest 节点数据,应该失败,并抛出 KeeperException.NoAuthException 异常。

运行代码示例:

  1. 确保 ZooKeeper 集群已启动并运行。

  2. 编译并运行 ACLDemo.java 代码。

  3. 观察控制台输出,验证 ACL 权限控制的效果。

5.2.5.2 使用 Zookeeper CLI 操作 ACL

ZooKeeper CLI 也提供了丰富的命令用于操作 ACL。

常用 ACL 相关 CLI 命令:

  • getAcl <path>: 获取指定节点的 ACL 信息。

    [zk: localhost:2181(CONNECTED) 0] getAcl /digest_node 'digest,'user1:JOMuy/ISkyiOoQJ4h85oV202i5Q= : cdrwa 'world,'anyone : r

    输出结果显示了 /digest_node 节点的 ACL 信息,包括 Digest 认证和 World 认证规则。

  • setAcl <path> <acl>: 设置指定节点的 ACL 信息。

    [zk: localhost:2181(CONNECTED) 0] setAcl /digest_node digest:user1:JOMuy/ISkyiOoQJ4h85oV202i5Q=:crwda cZxid = 0x400000009 ctime = Thu Nov 30 15:30:30 CST 2023 mZxid = 0x400000009 mtime = Thu Nov 30 15:30:30 CST 2023 pZxid = 0x400000009 cversion = 0 dataVersion = 0 aclVersion = 1 ephemeralOwner = 0x0 dataLength = 11 numChildren = 0

    该命令将 /digest_node 节点的 ACL 设置为只允许 digest:user1:password 用户拥有所有权限。

  • addauth <scheme> <auth>: 添加认证信息。

    [zk: localhost:2181(CONNECTED) 0] addauth digest user1:password [zk: localhost:2181(CONNECTED) 1] get /digest_node digest data cZxid = 0x400000009 ctime = Thu Nov 30 15:30:30 CST 2023 mZxid = 0x400000009 mtime = Thu Nov 30 15:30:30 CST 2023 pZxid = 0x400000009 cversion = 0 dataVersion = 0 aclVersion = 1 ephemeralOwner = 0x0 dataLength = 11 numChildren = 0

    先使用 addauth digest user1:password 添加 Digest 认证信息,然后就可以成功访问 /digest_node 节点。

  • create <path> <data> <acl>: 创建节点并设置 ACL。

    [zk: localhost:2181(CONNECTED) 1] create /cli_acl_node cli_data digest:user2:password2:cdrwa Created /cli_acl_node [zk: localhost:2181(CONNECTED) 2] getAcl /cli_acl_node 'digest,'user2:n/sH7rE8+yPE9W7p/r4L6Qz6YpQ= : cdrwa

    该命令创建 /cli_acl_node 节点,并设置 ACL 为 digest:user2:password2:cdrwa

CLI 操作步骤:

  1. 启动 ZooKeeper CLI 客户端 (./zkCli.sh)。

  2. 使用 getAcl 命令查看节点的 ACL 信息。

  3. 使用 setAcl 命令修改节点的 ACL 信息。

  4. 使用 addauth 命令添加认证信息。

  5. 使用 get, set, delete, create 等命令验证 ACL 权限控制的效果。

5.2.6 ACL 注意事项与最佳实践

在使用 ZooKeeper ACL 进行权限控制时,需要注意以下事项并遵循一些最佳实践:

  1. 最小权限原则: 只授予用户或服务必要的最小权限,避免过度授权。例如,如果一个应用只需要读取配置数据,则只授予 READ 权限,不要授予 WRITE 或 ADMIN 权限。

  2. 默认 ACL 配置: 在创建节点时,务必显式设置 ACL,不要依赖默认的 OPEN_ACL_UNSAFE。根据实际安全需求选择合适的 ACL 策略。

  3. ACL 继承性: 子节点默认不继承父节点的 ACL。如果需要子节点继承父节点的 ACL,需要在创建子节点时显式设置 ACL 与父节点相同。

  4. 权限组合: 合理组合权限类型,例如 cr 表示创建和读取权限,cw 表示创建和写入权限,cdrwa 表示所有权限。

  5. 认证方式选择: 根据安全需求选择合适的认证方式 (Scheme)。

    • world: 适用于测试环境或不需要权限控制的场景。

    • auth: 适用于简单的认证场景,但安全性较低。

    • digest: 适用于用户名密码认证场景,安全性较高。

    • ip: 适用于基于 IP 地址的认证场景,安全性一般。

    • sasl: 适用于企业级安全环境,安全性最高,但配置复杂。

  6. 密码管理: 对于 Digest 认证,密码需要安全存储和管理,避免泄露。可以使用环境变量、配置文件或专门的密钥管理工具来管理密码。

  7. ACL 管理工具: 对于复杂的 ACL 管理,可以考虑使用 ZooKeeper 管理工具或开发自定义的管理平台,方便 ACL 的配置、查看和维护。

  8. 监控与审计: 监控 ZooKeeper 集群的 ACL 配置和访问日志,及时发现和处理潜在的安全风险。

  9. 测试与验证: 在生产环境部署 ACL 配置之前,务必在测试环境中进行充分的测试和验证,确保 ACL 配置符合安全需求,并且不会影响应用的正常运行。

  10. 文档记录: 详细记录 ACL 配置策略、权限分配方案、认证方式等信息,方便后续的维护和管理。

5.2.7 总结

ZooKeeper ACL 权限控制是保障 ZooKeeper 集群数据安全性的重要机制。通过深入理解 ACL 的组成、权限类型、应用场景和操作实践,并遵循最佳实践,可以有效地保护 ZooKeeper 中的数据安全,构建更加安全可靠的分布式系统。合理地使用 ACL,可以实现细粒度的权限控制、数据隔离、安全审计等功能,满足不同场景下的安全需求。在实际应用中,需要根据具体的业务场景和安全需求,选择合适的 ACL 策略和认证方式,并进行充分的测试和验证,确保 ACL 配置的有效性和安全性。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U