用法

oslo.privsep 允许你在代码中定义特定的函数,这些函数将在预定义的权限上下文中运行。这让你能够以比代码其余部分更多(或更少)的权限运行函数。Privsep 函数存在于特定的 privsep 子模块中(例如,nova 的 nova.privsep)。

定义上下文

上下文在 privsep/__init__.py 文件中定义。例如,这定义了一个 sys_admin_pctxt,具有 CAP_CHOWNCAP_DAC_OVERRIDECAP_DAC_READ_SEARCHCAP_FOWNERCAP_NET_ADMINCAP_SYS_ADMIN 权限(相当于 sudo 权限)

from oslo_privsep import capabilities
from oslo_privsep import priv_context

sys_admin_pctxt = priv_context.PrivContext(
    'nova',
    cfg_section='nova_sys_admin',
    pypath=__name__ + '.sys_admin_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],
)

定义带超时的上下文

可以初始化带有超时的 PrivContext

from oslo_privsep import capabilities
from oslo_privsep import priv_context

dhcp_release_cmd = priv_context.PrivContext(
    __name__,
    cfg_section='privsep_dhcp_release',
    pypath=__name__ + '.dhcp_release_cmd',
    capabilities=[caps.CAP_SYS_ADMIN,
                  caps.CAP_NET_ADMIN],
    timeout=5
)

PrivsepTimeout 在超时达到时会被引发。

警告

守护进程(根进程)任务在超时达到时不会停止。这意味着如果相关的线程永远无法完成,我们将拥有更少的可用线程。

定义特权函数

函数在 privsep/ 子目录下的文件中定义,例如在 privsep/motd.py 文件中,用于处理 MOTD 文件的函数。它们使用指向我们上面定义的上下文的装饰器

import nova.privsep

@nova.privsep.sys_admin_pctxt.entrypoint
def update_motd(message):
    with open('/etc/motd', 'w') as f:
        f.write(message)

特权函数必须尽可能简单、专门和狭窄,以防止进一步的权限提升。在这个例子中,update_motd(message) 是狭窄的:它只允许服务覆盖 MOTD 文件。如果创建了一个更通用的 update_file(filename, content),它可能会被用来覆盖文件系统中的任何文件,从而很容易提升到 root 权限。这将破坏 oslo.privsep 的整个目的。

定义带超时的特权函数

可以使用 entrypoint_with_timeout 装饰器

from oslo_privsep import daemon

from neutron import privileged

@privileged.default.entrypoint_with_timeout(timeout=5)
def get_link_devices(namespace, **kwargs):
    try:
        with get_iproute(namespace) as ip:
            return make_serializable(ip.get_links(**kwargs))
    except OSError as e:
        if e.errno == errno.ENOENT:
            raise NetworkNamespaceNotFound(netns_name=namespace)
        raise
    except daemon.FailedToDropPrivileges:
        raise
    except daemon.PrivsepTimeout:
        raise

PrivsepTimeout 在超时达到时会被引发。

警告

守护进程(根进程)任务在超时达到时不会停止。这意味着如果相关的线程永远无法完成,我们将拥有更少的可用线程。

使用特权函数

要在常规代码中使用特权函数,你可以直接调用它

import nova.privsep.motd
...

nova.privsep.motd.update_motd('This node is currently idle')

最好导入完整的路径(import nova.privsep.motd),而不是 motd 名称(from nova.privsep import motd),以便更容易地发现该函数在不同的特权上下文中运行。

有关更多详细信息,你可以阅读以下博客文章

从 rootwrap 转换为 privsep

oslo.rootwrap 是 oslo.privsep 的前身,允许代码在匹配预定义过滤器时以 sudo 身份运行命令。例如,你可以定义一个过滤器,允许你使用以下过滤器以 root 身份运行 chmod

chmod: CommandFilter, chmod, root

除了调用完整命令以完成简单任务的糟糕性能之外,rootwrap 还会导致糟糕的安全问题:很难以不会轻易允许权限提升的方式过滤命令。

用 privsep 函数替换 rootwrap 过滤器很容易。上面的 chmod 过滤器可以替换为一个调用 os.chmod() 的函数。但是,直接 1:1 的过滤器:函数替换通常会导致函数仍然过于宽泛,无法保证良好的安全性。最好将每个 chmod rootwrap 调用 替换为一个狭窄的 privsep 函数,该函数将将其限制在特定的文件上。

有时需要重构调用代码:rootwrap 设计 discouraged 创建新的过滤器,因此通常会导致创建过于宽泛的调用函数。

例如,这个 补丁系列 正在进行中,以将 Nova 从 rootwrap 转换为 privsep。

有关更多详细信息,你可以阅读以下博客文章