如何在基于ElasticSearch的系统中实现ACL?

7
我有一个使用NodeJS和Elasticsearch实现RBAC授权策略的系统(RESTful)。RBAC授权与授权服务器一起工作,该服务器在其他API之前测试每个请求是否符合用户角色授权的路由(使用Bearer令牌对用户进行身份验证)。
我喜欢这个设计,因为其他API不需要知道授权/身份验证服务。而且它非常非常快,因为它使用内存缓存策略,而不是每次收到新请求时都向Elasticsearch发出请求来测试授权。
但现在我需要实现ACL以提供更细粒度的授权控制。从REST的角度来看,策略将应用于资源级别。例如:"POST:/user/123"只授权给A用户。
我已经与客户进行了调查,85%的人只会使用ACL的“允许”策略,默认情况下ACL控制将拒绝所有内容。所以好吧,现在我有所有开发此控件所需的信息。但我不知道最佳实现方式。
我的第一个想法是:
  1. 系统最重要的质量是可扩展性;
  2. 好吧,无法在内存中缓存,我已经模拟了10万个用户和100万个资源(可能是真实场景),内存量非常大,如果进行缓存,这个功能将具有高成本;
  3. 在这种情况下,认证服务无法处理ACL,因为它无法过滤搜索。身份验证服务不会拦截结果,只会验证标头和路由与角色是否匹配;
  4. 所以,根据所有这些要点,如果在Elasticsearch的每个文档中都有一个名为“acl_allow_method_user”的新字段,该字段是一个方法+用户ID授权使用此资源的数组,会怎样呢?将最终得到像这样的东西:

"acl_allow_method_user":["POST:123434"]

我还需要创建一个通用包,供所有API使用,以验证每次与Elasticsearch的交互中的此策略,但我认为这没有任何问题。
  1. 有任何ACL经验的人,这是一个好的设计吗?
  2. Elasticsearch对数组字段的大小有限制吗?
  3. 性能如何?这种方法会有影响吗?

你真的需要“每个文档”访问控制吗? - xeye
1
不幸的是,是一位相关客户的要求。 - Victor França
你有没有考虑过 XACML? - David Brossard
2个回答

4
我建议为ACLs(访问控制列表)建立单独的Elasticsearch索引,该索引应该比主文档索引小得多。这将允许您适当地调整ACL索引设置,例如:(1)使用比主文档索引更少的分片数,(2)将auto_expand_replicas设置为0-all,以防您想使用terms查询(示例:加载用户拥有的所有文档),(3)强制执行不同的保留/GDPR策略。
然后,ACL索引可以包含每个ACL规则的文档,例如:userId=1,docId=123,opType=POST。注意,这种方法将允许您为未来的其他类型的负责人和资源定义ACL规则。此外,这可以支持能够动态匹配新文档的ACLs,例如:userId=1,opType= POST,pattern="*"将允许具有userId=1的用户发布任何文档,实际上是sysadmin。将ACLs与文档/用户分离将允许您更新ACLs而无需更新相应的文档,在Elasticsearch中表现更好,它不执行原地更新,而是删除并重新创建文档。此外,您将能够在不担心保留相关ACL的情况下替换(PUT)整个文档。但是,您可能需要在删除文档或用户时清除ACLs,这可以在删除期间或作为单独的定期清理过程中完成。
现在ACLs已与文档本身分开,它们可以在memcached或Redis集群中缓存,而不需要太多内存。在典型的OLTP系统中,任何时候只有小部分用户处于活动状态,因此您可以适当配置LRU缓存以增加命中率。如果不知道您的系统具有哪种类型的访问模式,则很难提供进一步的建议。
最后需要考虑的一个问题是ACL由谁生成。如果一些ACL是自动生成的,例如:基于某种模式,则您可能可以在系统中使用此模式,以避免每个用户每个文档一个ACL规则。例如,如果一些ACL是从目录服务生成的,则您可以在ACL管理系统中缓存(并定期刷新)LDAP规则。

谢谢你的回答!这真的很有道理,我可以使用认证服务来管理RBAC已经实现的缓存。我们正在使用进程缓存来缓存RBAC策略,它运行得非常好。但是还有一个问题,一个大问题。那么调查怎么办?搜索必须使用ACL进行过滤。以这种方式,我不知道如何解决这个问题。 - Victor França
然后,您需要执行搜索,然后根据内存中的ACL过滤结果。 - xeye
@xeye抱歉,但你必须先理解我的问题...在搜索后过滤结果?这完全是错误的。考虑一个简单的“计数”操作。您无法检索100万个文档并将这些文档与ACL策略进行过滤。 - Victor França
@VictorFrança,每个文档的ACL非常受限制,你在那里没有太多选择。而且,当您将ACL存储在单独的索引中时,您将无法轻松地在ES内部加入它以过滤查询。ES的整个概念是关于搜索的,不要将业务逻辑(如ACL)推到那里。 - xeye
@xeye我同意你的观点,但ACL怎么办呢?我们计划在每个文档中创建ACL字段,这似乎不是一个坏主意。最终,如果我为此创建另一个索引,其大小几乎与每个文档中的ACL字段相同。有些文档将是公共的,也就是说,它们将没有ACL策略,在这些情况下,因为会出现巨大的用户权限数组,我们将简单地让文档所有者禁用ACL。这看起来是一个优雅的解决方案,我们只对Elasticsearch支持的数组大小存有疑虑。 - Victor França
1
@VictorFrança,为了避免在客户端内存中进行过滤,您可以使用terms query,它将允许您基于ACLs限制您正在搜索的文档集。但是,您必须适当地构建提供基于ACLs的术语的索引,例如userId:{opType:docIds}。然后,在搜索时,您可以使用带有"path":"user1/READ"的terms query,其中user1是需要对文档具有读取权限的用户ID。但是,您可能需要增加index.max_terms_count - alecswan

-1

对于那些遇到相同问题的人,以下是我们针对此案例得出的结论:在微服务REST中,将ACL(访问控制列表)粒度细化到资源点会带来类似于多租户系统的挑战。

它们属于业务逻辑,每个服务都知道“如何”拥有资源(以及可能的权限)。规范这些规则数据存储方式与每个服务逻辑的知识恰好相反。

我们可以规范的是每个微服务的ACL终端点(假设具有相同的契约和签名的路由)。如果您真的想在API(服务)的私有环境中隔离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) 记录策略更改请求并触发经纪人中的事件,通知策略更改。

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

  5. S (B) 和 S (C) 发布策略实施结果(授予或撤销)。

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

我会选择 @alecswan 的答案作为正确答案,因为它是达成这个结论的“起点”。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接