跳转到: 导航, 搜索

Swift/ContainerACLWithKeystoneV3

ARCHIVE

状态

完成。合并的补丁 https://review.openstack.org/#/c/86430/ 解决了此 wiki 页面上讨论的“优选方案”。此 wiki 页面是为补丁提供信息的背景思考的存档,但 *代码是规范性的* - 此 wiki 页面不应被解释为合并代码的文档。

此页面旨在捕获关于 swift 如何在 keystone v3 域的上下文中处理基于名称的 ACL 的形成性想法。

问题陈述

Swift keystoneauth 中间件允许容器 ACL 以 tenant:user 的形式指定“跨租户”访问,其中 tenant 和 user 可以是 UUID、名称或通配符 *。随着 keystone v3 API 的引入,名称(租户和用户)不再全局唯一,而仅在域内唯一。因此,使用不合格名称指定的跨租户 ACL 将会产生歧义。

总结 / 优选方案

(基于亚特兰大峰会讨论。在此处实现 https://review.openstack.org/#/c/86430/)

  • 未来,容器跨租户 ACL 应该只使用 id 或通配符来表示租户和用户 - 无法明确表达名称,并且名称是可变的。
  • 在验证 ACL 时,keystoneauth 不应匹配名称,除非 (a) 请求用户位于默认域中,并且 (b) 租户位于默认域中
  • 当使用 v3 token 创建帐户时,将租户域 id 记录为帐户 sysmeta,以便在验证 ACL 时确定租户域。

对未来选项的讨论

1. (优选) 仅允许在 keystone v3 中使用 UUID(或通配符)在 X-Container-[Read|Write] ACL 中。此要求由补丁 https://review.openstack.org/#/c/86430/ 强制执行

2. 要求在 X-Container-[Read|Write] ACL 中使用域限定名称。这方面的障碍是 keystone 当前在名称中没有保留字符,因此没有可以用于域限定名称中分隔符的字符。(如果 keystone 采用并强制执行一种表达域限定名称(或分层名称)的规范方法,则情况可能会发生变化。例如,如果 keystone 预留 '@@' 字符串,则可以用作用户/租户名称和域名称之间的分隔符,那么 swift 可以接受 tenant_name@@domain_name:user_name@@domain_name 形式的 ACL。)此外,keystone 中的名称不保证是不可变的,这意味着使用名称表达的任何 ACL 可能会变得陈旧(甚至不安全,如果名称被重新分配给另一个用户或租户)。

3. 如果需要基于名称的 ACL 支持,则提供一个新的 JSON 编码的结构化 ACL 规范,其中明确指出名称和域。这可能需要新的 header key(s) 来与旧的 X-Container-[Read|Write] ACL 格式区分开。这解决了使用当前 tenant:user 形式的 ACL 无法表达域限定名称的问题,但没有解决名称可变带来的风险。持久化的 ACL 应该只使用 ID 来表达 - 用户指定的 ACL 可以安全地使用名称,如果这些名称在 ACL 进入 swift 时被解析为 ID,即 swift keystoneauth 每次收到带有名称的 ACL header 时都会查询 keystone 服务,用 keystone 返回的 ID 填充 ACL,然后持久化填充完整的 ACL。为了完整起见,keystoneauth 应该在响应 GET/HEAD 请求时返回容器 header 时执行相反的过程,即查询 keystone 以获取当前映射到 ID 的名称,将名称插入 ACL 并返回给用户。这样,如果名称已更改,则返回给用户的 ACL 仍然是最新的。(这显然会增加对 keystone 的请求开销)。

对向后兼容性问题的讨论

现有系统在 ACL 中具有不合格名称。我们需要继续尊重这些 ACL,因为遗留用户和租户迁移到 keystone v3。

为了简单起见,我们假设所有遗留用户和租户都迁移到一个名为“遗留域”的单个 v3 域。(keystone 有默认域的概念,它可能名为“Default”,并且可能具有 id “default”,但默认域名称和 id 在迁移期间是可配置的。使用“遗留域”一词可以避免在此讨论中的混淆。)

我们假设 keystoneauth 中间件可以通过配置获得遗留域 id。

(稍后我们将回到讨论将遗留用户和租户迁移到多个域的影响 - 更新:显然这不可能发生,所有遗留用户都迁移到一个默认域。)。

我们可以陈述几个目标

目标1:继续为迁移到遗留域的用户和租户尊重现有的不合格名称 ACL。

目标2:遗留域中的用户可以继续使用不合格名称指定 ACL,但仅授予也位于遗留域中的用户。

目标3:非遗留域中的用户可以使用不合格名称指定 ACL,但仅授予也位于同一域中的用户。


为了实现目标1,我们需要验证 (a) 用户位于遗留域,并且 (b) 正在访问的租户帐户也位于遗留域。

实现 (a) 很容易,因为用户的域 id 将包含在已验证请求 token 的 token 信息中。需要注意的是,keystone 的 authtoken 中间件在没有域信息时(例如,处理 v2 token 时)会将用户域 id 设置为“default”,并且如上所述,这实际上可能不是遗留域的 id。

实现 (b) 比较困难,因为 swift 没有关于租户/帐户域成员资格的先验知识,并且确定租户域成员资格并不简单……

考虑的确定租户域成员资格的选项

1. 从 keystone 获取

在需要应用遗留 ACL 时,按需从 keystone 请求帐户/租户域 id。请注意,swift 的 keystoneauth 中间件当前不会向 keystone 发送任何请求(由 keystone authtoken 中间件处理)。检索后,可以将租户域 id 缓存或持久化为帐户 sysmeta。

2. 从 ACL 格式推断

我们可以从遗留 ACL 格式的存在推断租户位于遗留域。只有在强制要求所有在非遗留域中创建的 ACL 都是域限定的(假设这样做是可能的)时,这才是安全的。为了强制执行此操作,我们需要在设置 ACL 时了解租户的域成员资格(即,我们将问题转移到其他地方),或者强制所有新的 ACL 都必须使用域限定名称,包括遗留域中的新 ACL,这会阻止我们实现目标2 和目标3。

设置 ACL 时区分允许的 ACL 格式

[经过反思,我不确定我们是否可以在设置 ACL 时强制使用域限定名称,因为 ACL 值实际上可能以 ID 的形式表达,而且我不确定我们是否可以区分 ID 和名称。因此,本节可能无关紧要。]

我们是否可以继续允许在遗留域中设置遗留 ACL,方法是在处理带有 X-Container-[Read|Write] 的容器 POST 请求时进行区分?

考虑的选项

A. 仅当请求限定在遗留租户上时,才允许设置遗留 ACL 格式。当用户具有 admin 角色时,token 将限定在租户上,因此 keystoneauth 可以轻松确定域是否为遗留域。不幸的是,当具有 reseller-admin 角色用户设置 ACL 时并非如此 - 这些 token 不需要限定在租户上。拒绝。

B. 仅当用户(授权者)位于遗留域中时,才允许设置遗留 ACL。用户的域 id 包含在请求的 token 信息中,包括具有 reseller-admin 角色的用户。但是,如果向非遗留租户授予 admin 角色,这将允许遗留用户设置遗留 ACL。拒绝。

3. 从 ACL POST 请求中发现

当收到带有 X-Container-[Read|Write] header 的容器 POST 请求时,检查 token 信息以获取租户域信息。

  • v3 token,admin 用户角色:token 限定在租户上,因此可以获得租户域 id。将其存储为容器上的 sysmeta。
  • v3 token,reseller admin 角色:token 可能未限定在租户上。将租户域 sysmeta 存储为 unknown
  • v2 token - 租户必须位于遗留域,不存储租户域 sysmeta

验证 ACL 时

  • 如果租户域 sysmeta 不存在 -> ACL 是使用 v2 token 设置的 -> 租户是遗留的 -> 允许 ACL 中的不合格名称
  • 如果租户域 sysmeta = id_x -> 租户域 id 已知 -> 仅当用户域 == 租户域时才允许不合格的 ACL
  • 如果租户域 sysmeta = unknown -> 租户可能位于非遗留域 -> 不允许 ACL 中的不合格名称

这种方法看起来很有希望……最终的情况是难点,即具有 v3 token 的 reseller admin 设置使用不合格名称的 ACL,keystoneauth 无法在设置 ACL 时了解租户域 id,因此唯一的安全选择是拒绝 ACL。要求 reseller admin 了解更好吗?

4. (优选) 在创建帐户时发现

这类似于 (3)。创建帐户时,检查 token 信息以获取租户域信息。

  • v3 token,admin 用户角色:token 限定在租户上,因此可以获得租户域 id。将其存储为帐户上的 sysmeta。
  • v3 token,reseller admin 角色:token 可能未限定在租户上。将租户域 sysmeta 存储为 unknown
  • v2 token - 租户必须位于遗留域,不存储租户域 sysmeta(注意:我们可能不知道默认域的 id)

验证 ACL 时

  • 如果租户域 sysmeta 不存在 -> 帐户是使用 v2 token 创建的 -> 租户是遗留的 -> 允许 ACL 中的不合格名称
  • 如果租户域 sysmeta = id_x -> 租户域 id 已知 -> 仅当用户域 == 租户域时才允许不合格的 ACL
  • 如果租户域 sysmeta = unknown -> 租户可能位于非遗留域 -> 不允许 ACL 中的不合格名称

这种方法具有与 (3) 类似的难点 - 如果使用 reseller admin 和 v3 token 创建帐户,我们不知道其域 id。我们可以使帐户创建失败,或者假设即使在默认域中,如果帐户是使用 v3 创建的,其用户也应该了解名称基于 ACL 的弃用。

对目标的替代解释

如果我们允许在任何域中设置遗留 ACL,但仅当授权者和访问请求用户位于同一域中时才授予遗留 ACL,该怎么办?

为了实现这一点,我们需要在设置 ACL 时持久化授权者的域 id,然后将其与访问请求用户的域 id 进行比较(两者始终包含在 token 信息中)。授权者请求 id 可以作为容器元数据项持久化,例如 X-Container-Sysmeta-[Read|Write]-Granter。

这将导致略有不同的语义:位于 domainX 中的用户可以在 domainY 中的租户中设置遗留 ACL,但 ACL 仅授予位于 domainX 中的其他用户。也许可以接受?

一个潜在的令人困惑的后果是,位于 domainZ 中的用户无法更新 ACL,而不会无意中撤销对 domainX 中用户的访问权限,因为授权者的域将从 domainX 切换到 domainZ。

其他

1. 在某些情况下,全局名称唯一性将独立于 keystone 强制执行。在这种情况下,可以使用覆盖配置标志来允许继续在 ACL 中使用不合格名称。当前补丁提供了这一点。

2. 我们可以将所有现有的 ACL 重写为新的域限定格式。不太可能是一个受欢迎的选择。