如何在 ElasticSearch-based 系统上实现 ACL?

How to implement ACL on an ElasticSearch-based system?

我有一个使用 NodeJS 和 Elasticsearch 的系统 (RESTful),它实现了 RBAC 授权策略。 RBAC 授权与其他 API 前面的授权服务器一起工作,根据授权给用户角色的路由测试每个请求(使用承载令牌对用户进行身份验证)。

我喜欢这种设计,因为其他 API 不需要了解 authorization/authentication 服务。而且速度非常非常快,因为它使用 in-memory 缓存策略,而不是每次收到新请求以测试身份验证时都向 Elasticsearch 发出请求。

但现在我需要实施 ACL 以提供更精细的授权控制。从 REST 的角度来看,该策略将在资源级别应用。示例:"POST:/user/123" 仅授权给 A 用户。

我对客户做过调查,85% 的客户只会使用 ACL 的 allow 策略,默认情况下 ACL 控制将拒绝一切。好吧,现在我有了开发这个控件的所有信息。但是我没有看到实现这个的最佳方法。

我的第一个想法是:

  1. 系统最重要的品质是可扩展性;

  2. 好吧,这在内存缓存中是不可能的,我用10万用户和100万资源(这可能是真实场景)做了一些模拟,内存量很大,如果缓存此功能,成本会很高;

  3. 在这种情况下,身份验证服务无法处理 ACL,因为它无法过滤搜索。身份验证服务不拦截结果,仅验证 headers 和针对角色的路由;

  4. 所以,有了所有这些要点,如果在 Elasticsearch 的每个文档中我都有一个名为 "acl_allow_method_user" 的新字段,它是一个方法数组 + 用户 ID 的授权使用该资源怎么办?最终会得到这样的结果:

"acl_allow_method_user":["POST:123434"]

我还必须创建一个供所有 API 使用的通用包,以便在与 Elasticsearch 的每次交互中验证此策略,但我认为这没有任何问题。

  1. 哪位有过ACL的经验,这个设计好吗?

  2. Elasticsearch 对数组字段的大小有限制吗?

  3. 性能呢?这种方法会有影响吗?

我建议为 ACL 设置一个单独的 Elasticsearch 索引,它应该比您的主文档索引小得多。这将允许您适当地调整 ACL 索引设置,例如(1) 分片数量低于主文档索引,(2) auto_expand_replicas 设置为 0-all 以防您想使用术语查询(例如:加载用户拥有的所有文档), (3) 执行不同的 retention/GDPR 政策。

ACL 索引可以包含每个 ACL 规则的文档,例如userId=1,docId=123,opType=POST。请注意,此方法将允许您在将来为其他类型的委托人和资源定义 ACL 规则。此外,这可以支持可以动态匹配新文档的 ACL,例如userId=1,opType=POST,pattern="*" 将允许 userId=1 的用户访问 post 任何文档,实际上是系统管理员。从 documents/users 解耦 ACL 将允许您更新 ACL 而无需更新相应的文档,这将在 Elasticsearch 中执行得更好,Elasticsearch 不执行就地更新而是删除并重新创建文档。此外,您可以替换 (PUT) 整个文档,而不必担心保留关联的 ACL。但是,您可能希望在删除文档或用户时清理 ACL,这可以在删除过程中完成,也可以作为单独的计划清理过程完成。

现在 ACL 与文档本身是分开的,它们可以缓存在 memcached 或 Redis 集群中,而不需要太多内存。在典型的 OLTP 系统中,在任何时间点只有一小部分用户处于活动状态,因此您可以适当配置 LRU 缓存以提高命中率。如果不知道您的系统具有哪种访问模式特征,就很难提供进一步的建议。

最后要考虑的一点是什么 生成ACL。如果某些 ACL 是自动生成的,例如基于某种模式,那么也许你可以在你的系统中使用这种模式来避免每个用户每个文档都有一个 ACL 规则。例如,如果某些 ACL 是从目录服务生成的,那么您可以在 ACL 管理系统中缓存(并定期刷新)LDAP 规则。

对于在这里遇到同样问题的任何人,我们根据案例得出的结论是:作为微服务中的 ACL REST,细化到资源点代表着类似于多租户系统的挑战。

它们是业务逻辑,每个服务都知道 "how" 某人拥有资源(以及可能的特权)。将这些规则上的数据如何存储标准化,恰恰与每个服务的逻辑知识背道而驰。

我们可以标准化的一点是每个微服务的 ACL 的端点(采用相同合同和签名的路由)。如果你真的想在 APIs(服务)的私有环境中隔离 ACL,因为我们有一个负责用户控制和权限的微服务,所以整个架构可以转向事件溯源。

没有 ACL 的私有 API 隔离的示例:

  1. 我们有 3 个服务:"S (A)" 负责用户和权限的控制,"S (B)" 和 "S (C)" 执行任何普通任务。

  2. 前端应用程序必须了解 S (A)、S (B) 和 S (C) 的端点,并发出单独的请求来控制每个服务的 ACL 策略。

具有私有 API 隔离和事件源的示例:

  1. 存在相同的微服务。

  2. 前端应用程序向 S (A) 发出请求,将一些 ACL 策略应用于 S (B) 和 S (C)。

  3. S(A)记录策略变更请求,在broker中触发事件通知策略变更。

  4. S (B) 和 S (C) 捕获事件并在其逻辑中应用策略。

  5. S (B) 和 S (C) 发布政策执行结果(授予或撤销)。

  6. S (A) 捕获应用策略的结果事件并记录此结果。

我会选择@alecswan 的答案作为正确答案,因为 "starting point" 得出了那个结论。

也感谢@xeye,它提醒我们注意业务逻辑部分。