Nova 中 oslo-privsep 库的使用情况审查

https://blueprints.launchpad.net/nova/+spec/privsep-usage-review

Nova 对 privsep 库的使用过于宽泛。所有与 privsep 交互的功能都定义了一个包含所有所需权限的单个全局权限配置文件。虽然这可行,但并非库的最佳用法,因为功能获得了它们不需要的权限,因此不应获得这些权限。本规范旨在通过定义库的更专业化的使用来解决这种情况。

问题描述

Nova 计算服务使用 oslo-privsep 库在主机系统上获取提升的权限,目的是调用影响主机需要这些权限的区域的 Python 函数或 Linux 命令。

目前,Nova 使用 privsep 遵循了库首次创建时推荐的最佳实践

  1. 创建用于特权函数的专用模块。

  2. 创建一个单一的上下文,并将其使用限制在该模块中。

  3. 限制特权函数的范围,并将其操作作为非特权代码重用。

基于多年来对该库的使用情况,已经明确这种方法既不安全也不值得继续。在当前设计中,所有使用该库的功能共享一个配置文件。这会将代码中所有特权函数所需的全部功能聚合在一起。这意味着对于仅在文件系统上运行的单个函数,其他所有函数也会获得该功能,即使它们不需要。如果为每种情况使用更精确的配置文件,可以避免这种现象可能导致的意外行为。

用例

作为开发者,我希望拥有一个用于获取权限的微调方法。作为管理员,我希望 Nova 使用尽可能少的提升权限。

提议的变更

鉴于当前所有使用 privsep 库的函数都位于 nova.privsep 下,第一步是研究并映射每个函数所需的权限。接下来,可以为常见用例定义一组配置文件,例如网络或系统权限,并尽可能用它们覆盖。其余的需要分解为更小的函数,这些函数适合其中一种配置文件。如果不可能,则需要为它们保留当前的全部权限配置文件,直到找到更好的解决方案为止。

配置文件现在将在以下位置的 __init__.py 文件下定义:https://github.com/openstack/nova/blob/master/nova/__init__.py,而使用这些配置文件的函数将分布在其他包中。以下是文件可能最终呈现方式的示例

legacy_pctxt = priv_context.PrivContext(
    'nova',
    cfg_section='nova_sys_admin',
    pypath=__name__ + '.legacy_pctxt',
    capabilities=[capabilities.CAP_CHOWN,
                  capabilities.CAP_DAC_OVERRIDE,
                  capabilities.CAP_DAC_READ_SEARCH,
                  capabilities.CAP_FOWNER,
                  capabilities.CAP_NET_ADMIN,
                  capabilities.CAP_SYS_ADMIN],
)

sys_admin_pctxt = priv_context.PrivContext(
    'nova',
    cfg_section='privsep_sys_admin',
    pypath=__name__ + '.sys_admin_pctxt',
    capabilities=[capabilities.CAP_SYS_ADMIN],
)

net_admin_pctxt = priv_context.PrivContext(
    'nova',
    cfg_section='privsep_net_admin',
    pypath=__name__ + '.net_admin_pctxt',
    capabilities=[capabilities.CAP_NET_ADMIN],
)

file_admin_pctxt = priv_context.PrivContext(
    'nova',
    cfg_section='privsep_file_admin',
    pypath=__name__ + '.file_admin_pctxt',
    capabilities=[capabilities.CAP_CHOWN,
                  capabilities.CAP_DAC_OVERRIDE,
                  capabilities.CAP_DAC_READ_SEARCH,
                  capabilities.CAP_FOWNER],
)

每个新定义的配置文件都会生成一个消耗主机上资源的服务进程。因此,为了避免过度占用,一次最多可以定义 4 个配置文件。

为了提高可用性,在整个包的函数中找到的共享代码应提取到具有更广泛契约的其他非特权函数中。这些函数将负责执行更通用的操作,例如“chown”或“mkdir”,这些操作可能不需要用户的权限即可完成。但是,当需要提升权限时,将使用新的 privsep 上下文定义具有狭窄契约的专用一次性函数。这些函数将遵循以下条件创建

  • 其名称将包含 privileged_ 前缀。

  • 将在使用它们的同一个包中定义。

  • 除了单元测试外,将仅由单个模块导入。

以下是此实现方式的示例

# in nova/common/filesytem.py

def write_file(
  path: str,
  data: str = None,
  mode: str = 'w'
) -> ty.Optional[str]:
    try:
        with open(path, mode=mode) as fd:
            fd.write(data)
    except (OSError, ValueError) as e:
        LOG.debug(e)
        raise

def chown_file(
  path: str,
  usr: str = None,
  grp: str = None
) -> ty.Optional[str]:
    try:
        shutil.chown(path, user=usr, group=grp)
    except (OSError, ValueError) as e:
        LOG.debug(e)
        raise

# in nova/virt/libvirt/driver.py
import nova

from nova.common import filesystem as fs
...

@nova.file_admin_pctxt
def privileged_write_tpm_data(
  instance: uuid,
  tpm_data: str
) -> ty.Optional[str]:
    if not oslo_utils.uuidutils.is_uuid_like(instance):
        raise ValueError(f"instance: {instance} is not a valid uuid")
    path = os.path.join(CONF.instace_state_dir, instance)
    try:
        fs.write_file(path, data=tpm_data, mode='wb')
        fs.chown_file(path, "nova", "qemu")
    except (OSError, ValueError) as e:
        LOG.debug(e)

备选方案

我想到的没有。请提供有关本规范范围及其方法的任何反馈。

数据模型影响

REST API 影响

安全影响

需要使用提升的权限。

通知影响

其他最终用户影响

性能影响

其他部署者影响

如果租户的 OpenStack 发行版不使用提升权限配置的默认值,则本规范之后生成的所有 privsep 服务进程必须按照以下选项进行配置:https://docs.openstack.org/nova/latest/configuration/config.html#privsep

开发人员影响

开发者需要分析 nova.privsep 下的任何新函数所需的权限,并相应地应用正确的配置文件。

升级影响

实现

负责人

主要负责人

jsanemet

功能联络人

功能联络人

sylvainb

工作项

  • 研究已经使用 oslo-privsep 的函数,以确定每个函数需要的权限。

  • 为共享通用上下文的函数定义配置文件,例如:运行系统命令、修改网络设置…

依赖项

测试

Tempest 测试必须在无需任何修改的情况下继续通过,以验证在权限降低的情况下一切仍然正常工作。

文档影响

参考资料

首次讨论于:https://etherpad.opendev.org/p/nova-privsep-review

历史

修订版

发布名称

描述

2023.1 Antelope

引入