新的位置 API

https://blueprints.launchpad.net/glance/+spec/new-location-apis

问题描述

目前,show_multiple_locations 配置选项存在两个安全漏洞,OSSN-0065 [1] 和 OSSN-0090 [2]。如果启用 show_multiple_locations,并且添加/更新 (set_image_location)、获取 (get_image_location) 和删除 (delete_image_location) 位置的策略设置为非管理员,则非管理员用户可以修改位置数据,从而破坏他们拥有的镜像。请注意,添加、获取和删除位置的策略默认设置为非管理员,否则非管理员用户无法将数据与镜像记录关联,或检索镜像数据,或删除镜像数据。

当 show_multiple_locations 为 False 时,即使用户具有 {get,set,delete}_image_location 权限,用户也无法通过 image-update API 调用修改镜像位置。但是,有一些流行的用例,其他服务可以通过写入或读取镜像位置来绕过 Glance,直接在后端存储或访问镜像数据,并使用镜像所有者的凭据,这就是操作员希望将 show_multiple_locations 设置为 True 的原因。然而,操作员想要做的是启用优化的镜像数据访问;将镜像位置暴露给非管理员用户是一种副作用,而不是目标。我们目前建议希望使用优化数据访问的操作员为服务使用专门的 Glance 实例,并且仅将 glance-api 暴露给终端用户,并将 show_multiple_locations 设置为 False。这对某些用户来说不方便。

提议的变更

这项工作将分为 3 个阶段,具体如下

  1. 引入 2 个新的 API 调用,允许对镜像位置进行操作,这些操作在 REST API 影响 部分中详细描述。这些调用将取代 cinder 和 nova 等消费者的 image-update 机制。

  2. 修改消费者 (cinder/nova) 代码以使用新的位置 API。同时修改 HTTP store 以使用新的位置 API。

  3. 当其他服务 (cinder/nova) 执行位置操作不再需要 show_multiple_locations 配置选项时,将其删除。这将在消费者适应新的位置 API 处理升级情况后的 1 或 2 个周期内完成。

自 Newton 以来,配置选项 show_multiple_locations 已被弃用,但我们将保留该配置选项,直到 Glance 位置的消费者 (nova、cinder、http store 等) 开始使用新的位置 API。由于这是一项跨多个服务 (nova、cinder、glance) 的重大工作,我们将分不同的周期实施工作项,以便为开发人员 (实施此项工作) 和操作员 (迁移远离该配置选项) 提供足够的时间。

我们将引入 2 个新的策略,每个 API 执行不同的操作,例如添加和获取,具体如下

  1. add policy 可以默认为 project memberservice 角色 (在实现时)。

  2. get policy 将默认为 service 角色进行授权。

除了新的 add policy 之外,我们将在位置添加 API 代码中添加一个检查,以检查镜像的状态,并且仅当镜像处于 QUEUED 状态时才添加位置,并且当镜像处于其他状态时将不允许添加位置。这是为了防止恶意用户反复修改镜像位置,因为从 Glance 的角度来看,第一次添加的位置是正确的。

我们还将在 Glance 端引入一个新的配置参数 do_secure_hash,它将告诉 API 我们是否希望执行哈希计算。这在 nova、cinder 等添加 Glance 中的位置时很有用,因为 Glance 在这些情况下不会自动计算哈希和校验和。 do_secure_hash 的值默认为 True

在 nova 或 cinder 发送请求以添加 VM 快照或上传卷的镜像位置后,并且 do_secure_hashTrue,Glance 将启动一个后台进程来计算镜像的哈希值。除非我们在请求体中具有要验证的 validation_data,否则在注册位置后,即使哈希计算仍在后台进行,镜像也将设置为 active 状态。这是为了确保在注册位置后,镜像可以立即用于创建实例和可引导卷,而无需等待哈希计算,因为这是一个长期运行的任务。哈希计算完成后,镜像属性将使用 checksumos_hash_algoos_hash_value 值进行更新。

以下是不同 do_secure_hashvalidation_data 值下镜像转换的情况

  • do_secure_hashTruevalidation_data 不为 None

    镜像转换:(queued, importing, active)

    在这种情况下,消费者提供用于验证的哈希值,并且 Glance 计算哈希值。此案例的一个示例是为 HTTP store 提供 validation_data。在这里,镜像哈希将被计算和验证,然后才将镜像设置为 active 状态。

  • do_secure_hashTruevalidation_data 为 None

    镜像转换:(queued, active)

    在这种情况下,消费者不会提供验证数据,但哈希值由 Glance 计算。此案例的示例是 nova 拍摄实例快照或 cinder 上传卷到镜像时。在这里,镜像哈希计算将在设置镜像为 active 状态后完成并更新。这是一个棘手的情况,因为消费者不知道 active 镜像是否会拥有哈希值,以及是否应该等待哈希值填充到镜像中。为了处理这种情况,我们将 os_hash_algo 值设置在镜像属性中,以便消费者知道哈希计算正在进行中,并且哈希值将在这里填充。以下是以下情况:

    • active 镜像且没有 os_hash_algo:此镜像将不会填充哈希值。

    • active 镜像且具有 os_hash_algo:轮询 active 镜像状态和 os_hash_algo,直到获得 os_hash_value。由于镜像在没有提供 validation_data 且哈希计算在后台进行时获得 active 状态,因此轮询 active 镜像状态是可选的。如果哈希计算失败,将弹出 os_hash_algo 值。

  • do_secure_hashFalsevalidation_data 不为 None

    镜像转换:(queued, active)

    在这种情况下,消费者将提供验证数据,并且 Glance 不会计算哈希值。此案例的一个示例是为 HTTP store 提供 validation_data。在这里,镜像哈希将不会被计算和验证,而是直接使用用户提供的值设置到镜像中。

  • do_secure_hashFalsevalidation_data 为 None

    镜像转换:(queued, active)

    在这种情况下,消费者不会提供验证数据,并且 Glance 不会计算哈希值。这可能发生在所有情况下。在这里,哈希值不会设置在镜像中。

如果哈希计算失败,我们将添加一个重试机制,该机制将重新启动该任务。我们将添加一个新的配置选项 http_retries,其默认值为 3,即如果第一次和第二次尝试失败,哈希计算将默认最多执行 3 次。如果所有重试后哈希计算仍然失败,我们将不会更新哈希和校验和值,并且镜像将保持在 active 状态。

通过 Image API 访问镜像位置的终端用户访问不再是必需的。自 Train 以来,Glance 具有多个存储支持,并且我们添加了允许用户使用 API 操作数据位置的 API 调用。此外,存储是一个不透明的标识符,而镜像位置暴露了用户不需要知道的后端详细信息。

以下是直接操作镜像位置的当前用例,以及如何通过新的位置 API 处理它们。

  1. 在使用 Nova 和 Glance 共用的基于写时复制 (COW) 的后端时,Nova 可以在 Glance 中创建一个镜像记录,直接在后端拍摄服务器镜像的快照,并在镜像记录上设置位置。此用例涵盖了新的 add-location 调用,并且将其默认策略设置为项目成员 (镜像所有者) 或服务。

  2. 用户希望拥有单个镜像记录,但将镜像数据存储在多个位置以实现位置 (即,使镜像数据尽可能靠近其被消耗的位置)。此用例由 Glance 多个存储功能加上镜像导入处理,自 API v2.8 以来,允许一个“stores”参数指定应存储镜像数据的位置。这适用于新创建的镜像和现有镜像 (通过 copy-image 导入方法)。在此工作流程中,Glance 本身操作镜像位置;用户无需直接与位置交互。

  3. 操作员希望引入新的存储后端并停用当前的后端,同时保持相同的镜像目录。与 #2 类似,这可以通过使用 copy-image 导入方法和在 v2.10 中引入的 delete-image-from-store API 调用来处理。请注意,存在一些例外情况:

    1. HTTP store 是只读的,因此我们无法在此情况下使用 copy-image。

    2. 对于 RBD store,如果我们从它启动 VM 或创建可引导卷,我们将创建一个依赖链,因此我们无法删除源镜像,直到其所有子镜像都被展平。

    3. 对于 cinder store,如果 cinder 后端使用 COW 克隆,则类似于 b) 中提到的 RBD 情况,否则镜像删除将成功。

以下 API 尚未实现

Update:对于服务到服务的交互,更新位置的元数据没有价值。如果计划从 image-update 调用中删除现有的位置代码并支持操作员/终端用户执行位置操作,这将是有益的。

Delete:我们已经有了 删除镜像存储 API 用于此目的。我们不需要 删除镜像存储 API 调用用于当前用例,但如果我们计划在未来扩展位置 API,我们可以通过更新 删除镜像存储 操作的策略,从默认的 role:admin 修改为 role:admin or role:service 来实现。

备选方案

  • 我们可以删除 show_multiple_locations 配置选项,并过滤具有 admin_or_service 角色的镜像。这将要求消费者在添加或获取镜像时提供管理员凭据以获取位置。这是最初的提议,但由于这里的不同意见 [3],我们将设计更改为当前提议。

  • 另一种选择是在导入工作流程中添加此功能。我们可以添加一个新的导入方法 direct-location,它将允许终端用户指定 locationmetadata 参数,并基于给定的参数创建一个新的镜像。我们还可以使用 locationmetadata 值更新现有镜像,但需要镜像处于 queued 状态。

    为此,我们需要添加一个新的导入方法 direct-location,并向以下命令添加 --metadata--location 参数

    • glance image-create-via-import --import-method direct-location --location <location> --metadata <key1=value1, key2=value2 ...>

    • glance image-import --import-method direct-location --location <location> --metadata <key1=value1, key2=value2 ...>

数据模型影响

REST API 影响

我们将添加 2 个新的 location API

  • 添加 Location

    这将向现有镜像添加一个新的 location。请求体将包含 location URL 和 validation_data [4] (可选)。在请求体中包含 validation_data 的目的是,当消费者想要验证镜像哈希值,或者只是直接想要将哈希值添加到镜像时。 validation_datado_secure_hash 的用例在 Proposed change 部分中描述。一个提供 validation_data 的示例是 HTTP 存储用例,用户将为 HTTP 镜像提供哈希值。

    与旧的 location API 不同,我们将不支持在特定索引上添加 location。如果想要利用索引的优势,可以使用旧的 location API,或者将 location 策略设置为 store_type [5]。 提出了一种新的 location 策略 store_identifier [6],当配置了多个存储时,它应该能够从特定的存储中下载镜像。

    POST /v2/images/{image_id}/locations

    • JSON 请求体

      {
          "url": "cinder://lvmdriver-1/1a304872-b0ca-4992-b2c2-6874c6d5d5f9",
          "validation_data": {
              "os_hash_algo": "sha512",
              "os_hash_value": "6b813aa46bb90b4da216a4d19376593fa3f4fc7e617f03a92b7fe11e9a3981cbe8f0959dbebe36225e5f53dc4492341a4863cac4ed1ee0909f3fc78ef9c3e869",
          }
      }
      
    • JSON 响应体

      • 成功 - 200

      {
          "url": "cinder://lvmdriver-1/1a304872-b0ca-4992-b2c2-6874c6d5d5f9",
          "metadata": "{'store': 'lvmdriver-1'}"
          "validation_data": {
              "os_hash_algo": "sha512",
              "os_hash_value": "6b813aa46bb90b4da216a4d19376593fa3f4fc7e617f03a92b7fe11e9a3981cbe8f0959dbebe36225e5f53dc4492341a4863cac4ed1ee0909f3fc78ef9c3e869",
          }
      }
      
      • 错误 - 409 (Location 已经存在,或者如果镜像不在 QUEUED 状态),403 (禁止非所有者用户),400 (BadRequest 如果哈希值验证失败)

  • 获取 Location(s)

    这将显示与现有镜像关联的所有 location。如果镜像不包含任何 location,则返回一个空列表。

    GET /v2/images/{image_id}/locations

    • JSON 响应体

      [
          {
              "url": "cinder://lvmdriver-1/0f031ed1-5872-43d5-a638-4b0d07c10ab5",
              "metadata": "{'store': 'lvmdriver-1'}"
          },
          {
              "url": "cinder://cephdriver-1/11b4fa9f-a44b-46c9-950c-0026c467252c",
              "metadata": "{'store': 'cephdriver-1'}"
          }
      ]
      
      • 错误 - 404 (Image ID 不存在),403 (禁止普通用户)

镜像创建操作期间的镜像状态转换如下。镜像上传 (PUT)、镜像暂存 (PUT) 和 location 添加 (POST) 将使镜像从 queued 状态转换为下一个状态,该状态可以是以下任一状态

  1. saving

  2. uploading

  3. importing

  4. active

以下是镜像从 queued 状态的有效转换。

‘queued’: (‘saving’, ‘uploading’, ‘importing’, ‘active’, ‘deleted’)

安全影响

不会比现在更差,甚至可能更好。

  1. get-locations 策略限制为 ‘service’ 角色,因此用户将无法查看镜像 location。因此,即使将 ‘show_multiple_locations’ 和 ‘show_direct_url’ 设置为 False,新的 get-locations API 也不会向用户暴露 location 信息。

  2. add-location 策略默认情况下由 image-owner 限制。这将允许最终用户将 location 添加到镜像,以解决我们不了解的此功能的当前用法。即使允许这样做,由于 API 调用仅允许镜像处于 ‘queued’ 状态,因此数据替换攻击也被阻止。add-location API 不能用于将 location 添加到处于其他状态的镜像,然后删除原始 location,因此在此场景下 OSSN-0065 攻击是不可能的。此外,add-locations 调用(与当前通过 PATCH 更新 location 的方法不同)不需要 location 可见才能成功。因此,操作员可以配置 Glance,将 ‘show_multiple_locations’ 和 ‘show_direct_url’ 设置为 False,即使其他服务正在与 Glance 共享 COW 后端,并且操作员想要优化的工作流程。

通知影响

其他最终用户影响

由于新的 API 主要用于服务到服务的交互(HTTP 存储用例除外),我们将仅通过 CLI 暴露 location 添加 API。但是,我们需要在 openstacksdk 中为所有 API 添加方法(这将调用新的 location API),这些方法将由其他消费者服务(如 cinder 和 nova)使用。最终用户仍然可以使用现有的命令(内部调用 image-update API)来对 location 执行操作

  • glance location-add: 添加 location(以及相关元数据)到镜像。

  • glance location-delete: 从镜像中删除 location(以及相关元数据)。

  • glance location-update: 更新镜像 location 的元数据。

我们还将向 glanceclient 和 OSC 添加一个新的命令,该命令将允许最终用户添加 HTTP 存储用例的 location urlvalidation-data

  • glance add-location-properties --url <location> --validation-data <os_hash_algo=value1, os_hash_value=value2>

  • openstack image add location properties --url <location> --validation-data <os_hash_algo=value1, os_hash_value=value2>

性能影响

在旧的 location API 中,消费者(nova、cinder)在 glance 中注册了 location,并且未计算校验和、哈希等值。在消费者适应新的 location API 之后,并且 do_secure_hash 配置参数为 True(默认),glance 将读取镜像并在后台计算哈希值。哈希值计算将是一项长期运行的任务,因此会消耗资源,但是,这不会影响 nova 或 cinder 请求的操作,因为镜像将转换为 active 状态,即使哈希值计算仍在进行中。

性能方面的缺点将导致创建更安全的镜像,并且需要通过文档和发布说明向操作员/最终用户传达影响。由于 do_secure_hash 将是 glance 侧的可配置参数,我们将添加合适的帮助文本来传达启用/禁用此选项的性能和安全性影响。

其他部署者影响

开发人员影响

像 Cinder、Nova 和 HTTP 存储这样的消费者需要修改代码以调用新的客户端函数来访问 API。在实现客户端更改时,需要考虑的关键事项是

  • 我们将使用 SDK 来进行 API 调用。对调用新 location API 的更改将在 SDK 以及 OSC/glanceclient 中进行,以在 HTTP 存储的情况下进行 location ADD。

  • 保持与旧行为的向后兼容性。Glance 应该支持旧行为以及添加/获取 location 的新方法。这在升级用例中很有用,其中一个计算节点运行 2023.1 (Antelope) 代码,而另一个计算节点已升级到 2024.1 (CC) 版本。

  • 应该进行测试,以查看使用旧 location API 支持的现有功能是否在使用新的 API 时按预期工作。

实现

负责人

主要负责人

pdeore

其他贡献者

whoami-rajat

工作项

  • 添加 2 个新的 Location API 用于 add 和 get 操作。

  • 修改 cinder 和 nova 以及 http 存储等消费者以使用新的 location API。

  • 在 glance 中添加一个新的配置参数 do_secure_hash 并记录其影响。

  • 在 glance 中添加一个新的配置参数 http_retries 并记录其用法。

  • 添加 SDK 支持以调用新的 API。

  • 添加一个发布说明,说明我们将删除配置选项 show_multiple_locations,当消费者(nova/cinder/http 存储)切换到使用新的 location API 时。

  • 针对新的 add-location 和 get-location API 的 Tempest 测试。

依赖项

测试

  • 单元测试

  • 功能测试

  • 集成测试

  • Tempest 测试

文档影响

需要记录新的 location API。

参考资料