示例 2:使用 OPA 和 Rego 进行多租户访问控制和用户定义的 RBAC - AWS 规范性指导

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

示例 2:使用 OPA 和 Rego 进行多租户访问控制和用户定义的 RBAC

此示例使用 OPA 和 Rego 来演示如何通过租户用户定义的自定义角色在多租户应用程序的 API 上实现访问控制。它还演示了如何根据租户限制访问权限。此模型显示 OPA 如何根据高级角色中提供的信息做出精细的权限决策。

带有 OPA 和 Rego 的用户定义的 RBAC

租户的角色存储在用于为 OPA 做出访问决策的外部数据(RBAC 数据)中:

{ "roles": { "tenant_a": { "all_access_role": ["viewData", "updateData"] }, "tenant_b": { "update_data_role": ["updateData"], "view_data_role": ["viewData"] } } }

这些角色由租户用户定义时,应存储在外部数据源或身份提供者 (IdP) 中,在将租户定义的角色映射到权限和租户本身时,身份提供者可以充当真实来源。 

此示例使用 OPA 中的两个策略来做出授权决策,并研究这些策略如何强制执行租户隔离。这些策略使用前面定义的 RBAC 数据。

default allowViewData = false allowViewData = true { input.method == "GET" input.path = ["viewData", tenant_id] input.tenant_id == tenant_id role_permissions := data.roles[input.tenant_id][input.role][_] contains(role_permissions, "viewData") }

要显示此规则将如何运作,请考虑具有以下输入的 OPA 查询:

{ "tenant_id": "tenant_a", "role": "all_access_role", "path": ["viewData", "tenant_a"], "method": "GET" }

通过组合 RBAC 数据OPA 策略和 OPA 查询输入,可以按如下方式做出此 API 调用的授权决定:

  1. 来自的用户向Tenant A发出 API 调用/viewData/tenant_a

  2. 数据微服务接收调用并查询allowViewData规则,传递 OPA 查询输入示例中显示的输入。

  3. OPA 使用 OPA 策略中的查询规则来评估所提供的输入。OPA 还使用 RBAC 数据中的数据来评估输入。OPA 会执行以下操作:

    1. 验证用于进行 API 调用的方法是否为GET

    2. 验证请求的路径是否为。viewData

    3. 检查路径tenant_id中的是否等于与用户input.tenant_id关联的路径。这样可以确保保持租户隔离。另一个租户,即使角色相同,也无法获得进行此 API 调用的授权。

    4. 从角色的外部数据中提取角色权限列表并将其分配给变量。role_permissions此列表是通过使用与用户关联的租户定义角色来检索的 input.role.

    5. role_permissions检查它是否包含权限 viewData.

  4. OPA 将以下决定返回给数据微服务:

{ "allowViewData": true }

此过程显示了 RBAC 和租户意识如何有助于通过 OPA 做出授权决定。为了进一步说明这一点,可以考虑使用以下查询输入/viewData/tenant_b进行 API 调用:

{ "tenant_id": "tenant_b", "role": "view_data_role", "path": ["viewData", "tenant_b"], "method": "GET" }

此规则将返回与 OPA 查询输入相同的输出,尽管它是针对具有不同角色的不同租户的。这是因为此调用是针对的,/tenant_b而 RBAC view_data_role 中的数据仍具有与之关联的viewData权限。要对强制执行相同类型的访问控制/updateData,可以使用类似的 OPA 规则:

default allowUpdateData = false allowUpdateData = true { input.method == "POST" input.path = ["updateData", tenant_id] input.tenant_id == tenant_id role_permissions := data.roles[input.tenant_id][input.role][_] contains(role_permissions, "updateData") }

该规则在功能上与allowViewData规则相同,但它验证的是不同的路径和输入法。该规则仍可确保租户隔离,并检查租户定义的角色是否向 API 调用者授予权限。要了解如何强制执行此操作,请检查以下 API 调用的查询输入/updateData/tenant_b

{ "tenant_id": "tenant_b", "role": "view_data_role", "path": ["updateData", "tenant_b"], "method": "POST" }

使用allowUpdateData规则评估此查询输入时,会返回以下授权决定:

{ "allowUpdateData": false }

此呼叫将不会获得授权。尽管 API 调用者关联了正确的,tenant_id并且正在使用经批准的方法调用 API,但还是租户view_data_role定义input.role的。view_data_role没有updateData权限;因此,对的调用/updateData是未经授权的。对于拥有. 的tenant_b用户来说,此调用本来是成功的update_data_role