TripleO - 从容器安全地生成容器的模式

本文档描述了一种模式,可以用作 TripleO 当前方法的替代方案,以允许某些容器(Neutron 等)生成需要特殊权限(如网络命名空间)的并行进程。具体来说,它避免了暴露 Docker 套接字或使用最近进入 Stein 代码库的 Podman nsenter 技巧。

问题描述

在 Queens 中,TripleO 实现了一种容器化架构,旨在将所有 OpenStack 服务容器化。这种架构是成功的,但与裸机部署的等效版本相比,一些应用程序出现了回归。其中一个应用程序是 Neutron,它需要能够生成由 Neutron 代理自身直接启动的长期“并行”进程的能力。在最初的 Queens 架构中,Neutron 在代理容器内部启动这些并行进程,如果 Neutron 代理自身重新启动,则会导致服务中断。在裸机上,以前不会出现这种情况,因为这些进程将继续在代理重新启动/升级后运行。

Rocky 中的解决方法是为 Neutron 代理添加“包装器”脚本,并将 Docker 套接字暴露给每个代理容器。这些包装器脚本被绑定挂载到容器中,以便覆盖并行进程的正常位置。使用这种粗糙的机制,像 ‘dnsmasq’ 和 ‘haproxy’ 这样的二进制文件将启动一个 shell 脚本,而不是正常的二进制文件,这些自定义 shell 脚本依赖于从主机暴露的 Docker 套接字,以便能够使用提供给脚本的相同参数启动一个并行容器。

这种机制在功能上解决了容器化的问题,但暴露了一些安全问题,因为我们现在将启动任何容器的能力暴露给这些 Neutron 代理容器(具有访问 Docker 套接字的权限的特权容器)。

在 Stein 中,事情发生了变化,因为我们希望支持 Podman。与 Docker 不同,Podman 不在主机上包含守护进程。所有 Podman 命令都通过 CLI 执行,直接在主机上运行命令。我们发布了补丁,要求 Podman 命令使用 nsenter 进入主机的命名空间并在那里直接运行命令。同样,这种机制需要授予 Neutron 代理容器额外的权限,以便它们能够启动这些命令。此外,这种机制有点晦涩,难以在现场支持和调试。

提议的变更

概述

使用主机上的 systemd 直接启动并行进程容器,并支持 Neutron 代理所需的网络命名空间。这种方法的优点是,我们不再需要向 Neutron 容器授予启动它们不应该需要的容器的权限。

这种模式可以这样工作

  1. 一个 systemd.path 文件监视主机上已知位置的变化。示例 (neutron-dhcp-dnsmasq.path)

[Path]
PathModified=/var/lib/neutron/neutron-dnsmasq-processes-timestamp
PathChanged=/var/lib/neutron/neutron-dnsmasq-processes-timestamp

[Install]
WantedBy=multi-user.target
  1. 当 systemd.path 检测到变化时,它会触发此路径文件的服务:示例 (neutron-dhcp-dnsmasq.service)

[Unit]
Description=neutron dhcp dnsmasq sync service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/neutron-dhcp-dnsmasq-process-sync
User=root
  1. 我们使用今天使用的相同的“包装器”脚本来写入两个文件。第一个文件是用于在主机上启动进程的 CLI 参数的转储。该文件可以选择包含额外的元数据,例如某些 Neutron 并行进程所需的网络命名空间。第二个文件是一个时间戳,由主机上的 systemd.path 监视变化,并用作信号,指示它需要使用带有参数的文件来处理第一个文件。

# 当检测到变化时,上面的 systemd.service 在

主机上执行一个脚本,以干净地启动容器化的并行进程。当脚本完成启动进程后,它会截断该文件以从干净的状态开始。

# 包装器脚本和主机脚本都使用 flock 来消除竞争

条件,这些条件可能导致重新启动或丢失容器的问题。

替代方案

使用 Podman,像 varlink 这样的 API 是一个选项,但它可能仍然需要暴露主机上的套接字,这需要像今天一样额外的权限。这将避免 nsenter 技巧,但会避免 nsenter 技巧。

像 Kubernetes 这样的架构将为我们提供一个可以通过 COE 直接启动容器的 API。

此外,可以编写一个“容器感知”的 Neutron 外部进程管理器,以改进上述两种选项。Neutron 中当前的 Python 主要用于在裸机上启动进程,并假设它启动的一些进程应该在容器重新启动后存活。实现一个可以通过干净的接口启动并行进程,而不是覆盖二进制文件的类是可取的。可以支持支持通过 Kubernetes 和/或 Systemd 通过主机直接启动容器的类。

安全影响

这种机制应该允许我们删除过去用于执行容器的一些 Neutron 代理容器权限。它是一个更严格的粗糙接口,只允许容器启动特定类型的进程,而不是它选择的任何容器。

升级影响

并行进程容器应该与它们的启动方式无关,因此升级应该很小。

实现

负责人

主要负责人

dan-prince

其他贡献者

emilienm

工作项

# Ansible playbook 用于创建 systemd 文件、包装器

# TripleO Heat 模板更新以使用新的 playbook

# 从 puppet-tripleo 中删除/弃用旧的 docker.socket 和 nsenter 代码