策略配置 HowTo

您可以使用 Cinder 策略来控制您的用户和管理员与块存储服务交互的方式。在本 HowTo 中,我们将讨论 Cinder 采用的用户模型以及如何通过调整策略对其进行修改。

  • 与大多数 OpenStack 服务一样,Cinder 使用 OpenStack oslo.policy 库作为其策略相关代码的基础。有关“规则”和“角色”、其他词汇以及 OpenStack 策略和策略配置文件的一般信息,请参阅 使用 oslo.policy 的应用程序管理

  • 请参阅 策略配置,了解 Cinder 识别的策略目标列表。

  • 从 Queens 版本开始,运行 Cinder 的默认方式是不使用策略文件。这是因为代码中定义了合理默认值。但是,要使用自定义策略配置运行 Cinder,您需要将您的更改写入策略文件。

  • 在本文档的其他地方,您可以找到 示例策略文件 的副本,其中包含所有默认设置。

  • 有关直接从 Cinder 源代码生成示例 policy.yaml 文件的说明,请参阅 Cinder 源代码仓库(或其 github 镜像)中 etc/cinder 目录中的文件 README-policy.generate.md

  • OpenStack 已弃用自 Wallaby 版本(Cinder 18.0.0)以来使用 JSON 策略文件。如果您仍在使用的 JSON 格式,有一个 oslopolicy-convert-json-to-yaml 工具可以将您现有的 JSON 格式策略文件以向后兼容的方式迁移到 YAML。

词汇说明

我们需要澄清一些我们将在下面使用的术语。

项目

这是将用户分组为可以拥有云资源的单位的行政分组。(这曾经被称为“租户”。)

服务

这是用户通过其提供的 API 与之交互的 OpenStack 组件。例如,“Cinder”是 OpenStack 代码名称,用于提供块存储 API 版本 2 和 3。Cinder 也被称为 OpenStack 块存储服务。

做出这种区分的目的是,‘项目’一词还有另一种相关的用法,但我们会使用它。每个 OpenStack 服务都是由一个“项目团队”生产和维护的。我们不会在此文档中使用“项目”一词。我们将始终使用“服务”一词。(如果您是 OpenStack 的新手,这不会有问题。但是,如果您与在 OpenStack 方面经验丰富的人讨论此内容,您需要明确这一点,以免彼此无意中谈论不同的内容。)

用户模型

Cinder 代码的编写预期存在两种类型的用户。

最终用户

这些用户是消耗资源并(可能)支付账单的用户。终端用户被限制在特定项目内操作,并且无法对不属于他们所处项目拥有的资源执行操作。

管理用户(“管理员”)

这些用户负责维持系统的正常运行。他们能够查看 Cinder 控制的所有资源,并且可以对它们执行大多数操作。他们还可以访问其他操作(例如,设置配额),这些操作不能由终端用户执行。

此外,管理员可以查看终端用户无法看到的资源属性(例如,卷的迁移状态)。从技术上讲,当在管理上下文中发出 volume-show 调用时,它将包含比管理上下文调用时更多的属性。类似地,当在管理上下文发出 volume-list 调用时,响应可能包含不属于发出调用的人的项目拥有的卷;这在调用管理上下文时永远不会发生。

策略

广义地说,操作员可以使用策略完成两件事

  1. 策略文件可以定义授予用户在管理上下文中行事的权限的标准。

  2. 策略文件可以为特定的操作(或策略目标)指定哪些用户可以执行这些操作。

通常,虽然操作员可以定义可以在管理上下文中进行调用,但操作员无法影响可以在管理上下文中做什么(因为这已经在代码实现时决定了)。例如,项目之间的边界在 Cinder 中得到严格执行,只有管理员才能查看跨项目的资源。没有办法授予用户查看另一个项目的权限(至少不能通过策略配置–可以通过使用身份服务将用户添加到其他项目来完成,但请注意,此时,用户不再是拥有现在可见资源的项目的成员。)

预定义的策略规则

默认的 Cinder 策略文件包含三个用作策略文件配置基础的规则。

“context_is_admin”

这定义了 Cinder 中的管理上下文。您会注意到它在示例策略文件的开头定义了一次,并且在文件的其他任何地方都没有引用。要理解这是做什么,了解一些 API 实现很有帮助。

用户的 API 请求必须附带来自身份服务的身份验证令牌。(如果您正在使用客户端软件,例如,python-cinderclient 或 python-openstack 客户端,令牌将在幕后为您请求。)块存储 API 确认令牌未过期,并获取有关请求者的其他信息,例如身份服务识别用户拥有的角色。Cinder 使用此信息创建一个内部上下文对象,该对象将在各种函数和服务被调用以满足用户请求时在代码中传递。

当创建请求上下文对象时,Cinder 使用“context_is_admin”规则来确定此上下文对象是否将被识别为提供管理上下文。它通过在上下文对象上将“is_admin”属性设置为 True 来执行此操作。Cinder 代码在调用链中稍后会简单地检查上下文对象上的“is_admin”属性是否为 true,以确定调用是否发生在管理上下文中。类似地,策略将引用“is_admin:True”(直接或间接)以需要管理上下文。

所有这些都是冗长地说,在 Cinder 策略文件中,您只会看到“context_is_admin”在顶部;之后,每当您想引用管理上下文时,您将看到“is_admin:True”。

“admin_or_owner”

这是大多数非管理员 API 调用的默认规则。顾名思义,它允许管理员或所有者进行调用。

“admin_api”

这是默认规则,用于仅允许管理员发起的 API 调用。

注意

对于某些 API 调用,代码中存在检查,以确保在允许请求成功之前正在进行管理上下文调用。因此,并非总是可以通过将值设置为“rule:admin_api”的策略目标更改为“rule:admin_or_owner”(或“rule:admin_api or role:some-special-role”)来授予非管理员用户成功进行调用的权限。不幸的是,您无法通过实验策略文件(或查看源代码)来确定这些调用是什么。但是,一个好的经验法则是,默认策略配置中标记为“rule:admin_api”的策略控制的 API 调用属于此类。

示例:配置只读管理员

一个相当常见的配置请求是创建一个具有仅观察者(“看但不要碰”)功能的特殊管理员类别。出于安全和稳定性的原因,允许所有用户(包括管理员)拥有成功执行其工作所需的最低权限是一个好主意。某人的工作是审核有关 Cinder 的信息(例如,查看当前的配额设置),他们不需要更改这些设置。在本节中,我们将讨论一种配置 Cinder 策略文件以实现此目的的方法。

注意

为了使讨论集中,此示例假定您正在从默认策略文件工作。希望一般的策略将足够清晰,可以应用于已经使用非默认配置的云。此外,还有其他逻辑上等效的方法来配置策略文件以引入只读管理员;这绝不是唯一的方法。

鉴于工作要求,观察员管理员(我们简称为“观察员-管理员”)需要以管理上下文运行。因此,我们将不得不调整策略文件中的“context_is_admin”定义以包含此人。请注意,如果我们不进行其他更改,这将使此人成为完全管理员。因此,我们将采用的策略是首先使观察员-管理员成为完全管理员,然后阻止观察员-管理员访问那些不是只读的 API 调用。

警告

从比喻上讲,我们正在打开水闸然后逐个堵住漏洞。这听起来令人担忧,应该如此。我们不能强调足够的策略文件更改应该是良好控制的(也就是说,您确切知道谁拥有新的角色或角色)并且是经过测试的(您应该有一种测试方法来确定您的更改是否只有您预期的效果)。

这可能是您被提醒以下内容的好地方:以下建议不提供任何形式的保证,无论是明示的还是暗示的。与 OpenStack 源代码一样,它们受 Apache 许可协议 2.0 版 的约束。特别是,我们建议您关注第 7-9 条。

步骤 0:测试

我们首先提到测试(即使您尚未进行任何更改),因为如果我们等到进行配置更改后才提到它,您可能会认为这是最后要做的事情(或最不重要的)。如果您在开始修改策略配置之前制定如何测试这些更改的计划,这将使您的生活更轻松。

我们建议设置自动化测试,因为块存储 API 有很多 API 调用,您需要针对管理员用户、观察员-管理员用户和“普通”终端用户测试每个 API 调用。此外,如果您预计可能需要比本示例中概述的更细粒度的访问权限(例如,您想要一个可以创建和读取但不能删除的“创建者”角色),您的配置将更加复杂,因此需要比手动测试更广泛的测试。

步骤 1:创建一个新角色

在身份服务中,创建一个新角色。创建一个以前从未分配过的角色是一个好主意,这样您就可以轻松跟踪它已分配给谁。如您所回忆,此人将拥有任何在“堵住漏洞”阶段遗漏的功能的完全管理权限

对于此示例,我们将使用名为 cinder:reader-admin 的角色。此角色名称没有什么特别之处;您可以使用对将分配角色和配置策略的管理员有意义的任何名称。(‘cinder:’部分是为了提醒您此角色适用于块存储服务,‘reader’部分来自 OpenStack 已经收敛的此类型观察员角色名称,‘-admin’部分是为了提醒您拥有此角色的任何人将能够观察管理员类型的内容。)

注意

从 Rocky 版本开始,身份服务(Keystone)在服务启动时创建三个角色:memberreaderadmin。默认情况下,reader 角色未分配给任何用户。在 Stein 周期内的工作正在进行中,以便身份 API 将识别具有 reader 角色的用户,并授予他们对身份 API 的只读访问权限。有关更多信息,请参阅 Keystone 规范 基本默认角色

我们提到这一点是为了让您知道,如果您在执行本文档中描述的策略配置时使用名为 reader 的角色,那么在某个时候,分配了 reader 角色的用户可能对块存储服务以外的其他服务具有只读访问权限。这种结果的期望取决于您的特定用例。

步骤 2:打开水闸

如果您的安装没有 /etc/cinder/policy.yaml 文件,您可以从源代码生成一个(请参阅本文档的介绍部分)。

注意

默认文件是完全注释掉的。为了使您下面的任何更改生效,请不要忘记取消注释它们所在的行。

要将管理上下文扩展为包含新角色,请更改

"context_is_admin": "role:admin"

"context_is_admin": "role:admin or role:cinder:reader-admin"

步骤 3:修补 Admin API 中的漏洞

现在我们对策略配置进行调整,以便观察者管理员实际上只能以只读方式访问 Cinder 资源。

3A:新的策略规则

首先,我们为 Admin API 访问创建一个新的策略规则,该规则明确排除新角色。在策略文件中找到左侧带有 "admin_api" 的行。紧随其后,引入一个新的规则

"strict_admin_api": "not role:cinder:reader-admin and rule:admin_api"

3B:修补漏洞

现在,使用此新规则修补我们在 Admin API 中打开的漏洞。在策略文件的其余部分中找到如下所示的每一行

"target": "rule:admin_api"

对于每一行,确定观察者管理员是否需要访问此操作。例如,目标 "volume_extension:services:index" 指定一个只读操作,因此观察者管理员可以执行它。我们将将其保留在默认配置中,即

"volume_extension:services:index": "rule:admin_api"

另一方面,如果目标允许修改,我们很可能不希望允许观察者管理员执行它。对于此类操作,我们需要使用“严格”形式的管理员规则。例如,考虑操作 "volume_extension:quotas:delete"。要排除观察者管理员执行它,请更改默认设置

"volume_extension:quotas:delete": "rule:admin_api"

"volume_extension:quotas:delete": "rule:strict_admin_api"

针对默认由 rule:admin_api 管理的其他策略目标,逐个案例进行操作。

3C:其他更改

您可能已经想到了,但上述说明中可能暗示了一些其他更改,但并未明确提及。例如,您会在示例文件中找到以下策略

"volume_extension:volume_type_encryption": "rule:admin_api"
"volume_extension:volume_type_encryption:create": "rule:volume_extension:volume_type_encryption"
"volume_extension:volume_type_encryption:get": "rule:volume_extension:volume_type_encryption"
"volume_extension:volume_type_encryption:update": "rule:volume_extension:volume_type_encryption"
"volume_extension:volume_type_encryption:delete": "rule:volume_extension:volume_type_encryption"

第一个策略涵盖所有创建/读取/更新/删除操作(并且在 Stein 开发周期中已弃用以供删除)。但是,如果您将其设置为 "rule:strict_admin_api",观察者管理员将无法读取卷类型加密。因此,应将其保留在 "rule:admin_api",并将创建/更新/删除策略更改为 "rule:strict_admin_api"。此外,为了准备删除弃用的策略目标,更改 get 策略的值为 "rule:admin_api" 也是一个好主意。

步骤 4:修补“常规”API 中的漏洞

如前所述,具有 cinder:reader-admin 角色的用户将被提升到完全管理权限。这意味着这样的用户可以对最终用户资源执行管理功能。因此,我们还有另一组漏洞需要修补。

4A:新的策略规则

正如我们为 Admin API 所做的那样,我们将创建“admin_or_owner”规则的严格版本,以便我们可以专门排除观察者管理员执行该操作。在策略文件中找到 "admin_or_owner" 出现在左侧的行。它可能如下所示

"admin_or_owner": "is_admin:True or (role:admin and is_admin_project:True) or project_id:%(project_id)s"

紧随其后,引入一个新的规则

"strict_admin_or_owner": "(not role:cinder:reader-admin and (is_admin:True or (role:admin and is_admin_project:True))) or project_id:%(project_id)s"

注意

要理解此更改的作用,请注意“admin_or_owner”规则定义的通用结构是

<admin-stuff> or <project-stuff>

要构建严格版本,我们需要确保 not cinder:reader-admin 部分仅应用于左侧(<admin-stuff>)。最简单的方法是以以下方式构建新规则

(not role:cinder:reader-admin and (<admin-stuff>)) or <project-stuff>

注意

如果您不需要具有 cinder:reader-admin 角色的用户来管理其自己项目中的资源,则可以将其简化为

"strict_admin_or_owner": "not role:cinder:reader-admin and rule:admin_or_owner"

4B:修补漏洞

在策略文件中找到如下所示的每一行

"target": "rule:admin_or_owner"

并确定它是否代表观察者管理员需要执行的操作。对于您不希望观察者管理员执行的操作,请将策略更改为

"target": "rule:strict_admin_or_owner"

4C:不受限制的策略

默认文件中有一些策略如下所示

"target": ""

这些称为不受限制的策略,因为要求为空,因此可以由任何经过身份验证的用户满足。(但是,回想一下之前关于 用户模型 的讨论,但这并不意味着任何用户可以看到其他用户的资源。)

不受限制的策略可能出现在没有特定资源引用的 GET 调用(例如,获取所有卷的调用)或创建完全新资源的 POST 调用(例如,创建卷的调用)上。您不会在 Cinder 策略文件中看到很多这样的策略,因为实现 Block Storage API v2 和 v3 的代码始终确保存在包含至少 project_iduser_id 的目标对象,这些对象可用于评估是否应允许该策略允许该操作。

因此,可以保留明显的只读目标(例如,volume_extension:type_get)不受限制。非只读的策略目标(例如,volume:accept_transfer),可以更改为 rule:strict_admin_or_owner

步骤 5:测试

我们上面强调过,由于此更改的性质,仔细测试它非常重要。需要注意的一点是:因为我们使用诸如 not role:cinder:reader-admin 之类的子句,所以角色名称中的拼写错误会导致问题。(例如,如果您将其输入文件为 not role:cinder_reader-admin,它将不会排除我们担心的用户,该用户具有 cinder:reader-admin 角色。)

如前所述,我们建议设置自动化测试,以便您可以防止在修改策略文件时出现回归。