Podman、KVM 和容器:安全虚拟化的实用指南

最后更新: 五月9 ,2026
  • 容器与主机共享内核,因此它们的隔离性取决于命名空间、cgroups 和系统加固。
  • 与传统的 Docker 相比,Podman 通过避免使用中央 root 守护进程并实现无 root 执行,提供了额外的安全性。
  • Podman 中的桥接网络、主机网络和无根网络(slirp4netns、paste)允许您调整连接性和隔离性之间的平衡。
  • 结合 KVM、容器和最佳实践(无根管理、MAC 地址管理、安全计算、镜像扫描),为生产环境提供了一个强大的平台。

Podman KVM 安全虚拟化

当你在 Linux 上开始搭建一个正规的容器环境时,会立即面临同样的问题: 完全依赖容器的安全性有多高?何时应该升级到 KVM 或微型虚拟机? 在企业环境中,Docker、Podman、LXC、KVM 虚拟机和各种虚拟机管理程序混杂在一起,充分理解隔离模型是避免犯错的关键。

此外,一些较为复杂的情况也并不少见,例如: 尝试在 VirtualBox 或 KVM 的虚拟机中使用 Docker Desktop 或 Podman。这涉及到多个级别的虚拟化(嵌套或非嵌套),很容易遇到诸如“主机上未启用 KVM”之类的错误。让我们把所有这些组件——容器、虚拟机、Podman、KVM、网络和安全——梳理清楚,看看如何将它们结合起来,实现真正安全实用的虚拟化。

使用 KVM 和虚拟机的经典虚拟化

整个体系的基础仍然是 操作系统虚拟化 使用诸如 KVM、Xen 或 ESXi 之类的虚拟机管理程序完成在这里,物理硬件运行着一个虚拟机管理程序,该程序负责将机器“分割”成多个独立的虚拟机。

在这个模型中,每个虚拟机都启动 它自己的内核和它自己的客户操作系统与宿主机完全分离。虚拟机管理程序(例如集成到 Linux 内核中的 KVM)充当硬件和虚拟机之间的守护者,负责实现它们之间的严格隔离。

我们通常将虚拟机管理程序分为两种类型:第一种类型或裸机虚拟机管理程序,直接运行在硬件上;第二种类型虚拟机管理程序,运行在现有的操作系​​统上。 VirtualBox 或 VMware Workstation 属于第二类。而由 libvirt 在 Linux 服务器上管理的 KVM 则更接近于第 1 类场景,即使它与主机本身共享空间。

这种方法的最大好处是: 每个虚拟机都有自己完整的堆栈:内核、用户空间、服务、网络、存储这提供了很大的灵活性,并允许在同一台物理机上运行不同的操作系统和内核版本。

在 GNU/Linux 中管理这些虚拟化平台,几乎总是使用 libvirt 作为编排 API此外,还支持图形和命令行工具,或者像 OpenStack 这样的大型项目,它通过结合虚拟机、裸机和容器来构建整个云。

KVM的硬件要求和常见问题

在运行 KVM 或使用依赖于 KVM 的解决方案(例如某些 Docker Desktop 或微型虚拟机配置)之前,您需要确保: 该处理器支持硬件虚拟化(Intel 平台的 VT-x 或 AMD 平台的 SVM/AMD-V)。 是什么 已在 BIOS/UEFI 中启用.

在 Linux 系统中,我们有一个快速检查方法:如果命令 grep -E 'svm|vmx' /proc/cpuinfo 如果返回空值,或者固件中该选项保持禁用状态,KVM 将无法工作。此外,在需要将虚拟机运行在另一个虚拟机内部(嵌套虚拟化)的情况下,顶层虚拟机管理程序必须显式地将这些扩展暴露给客户机。

当出现问题时,会显示一些典型的消息,例如在 VirtualBox 中 RHEL 9 里著名的 Docker Desktop 弹出窗口: “主机上未启用KVM”问题不在于 Docker 或 RHEL 本身,而在于 VirtualBox 虚拟机没有获得运行 KVM 所需的虚拟化扩展。

类似命令 modprobe kvm o modprobe kvm_amd / kvm_intel 内核模块应该能够正常加载,不会出现错误信息。如果 dmesg 中没有任何相关信息,或者 lsmod | grep kvm 它只显示了“裸机”KVM和IRQ旁路,缺少具体的CPU模块;很可能是 硬件虚拟化尚未暴露出来 对客户系统或在固件中禁用。

在这些情况下,即使主机内所有可能的功能都已激活, 如果 VirtualBox 不提供嵌套虚拟化功能或配置错误,KVM 将无法使用。解决方案包括检查主机虚拟机管理程序(VirtualBox、VMware 等)的配置,并在可用时启用嵌套虚拟化。

容器:操作系统级别的轻量级虚拟化

与传统虚拟机相比,容器提供了一种替代方案: 并非所有硬件都被模拟;而是重用主机内核。 使用 Linux 内核函数隔离进程和资源。

一个容器仍然 一组使用命名空间和 cgroups 的封装进程在这个“环境”中,应用程序认为它运行在自己的机器上,拥有自己的文件系统、网络接口和进程 ID,但实际上它与主机共享内核。

容器能够提供接近原生应用的性能,因为 没有第二个内核,也没有完整的硬件模拟。开销很小:仅足以设置命名空间、使用 cgroups 限制资源和应用安全策略(seccomp、AppArmor、SELinux 等)。

然而,这种设计带来了一个重要的后果: 所有有效载荷共享同一个物理内核内核中的一个严重漏洞可能突然成为该节点上运行的所有容器的潜在逃逸途径。

这就是为什么人们经常谈论集装箱隔热的原因。 它不像虚拟机那样是“绝对的”。如果有人设法利用了严重的内核漏洞,命名空间将不再是有效的屏障,容器和主机之间的边界将被打破。

命名空间和 cgroups:容器隔离的基础

在 Linux 系统中,容器隔离建立在两大支柱之上: 命名空间用于分隔系统视图 y cgroups 用于限制和核算资源.

  MS-DOS 的历史:彻底改变 PC 的操作系统

命名空间用于隔离诸如此类的内容 进程 ID、网络、挂载点、进程间通信 (IPC)、主机名 (UTS) 和用户这样一来,一个进程就只能“看到”它自己命名空间内的进程、网络接口、文件系统和主机名,从而感觉自己像是在另一个系统上一样。

就它们而言,cgroups 允许 指定容器可以消耗多少 CPU、内存、磁盘 I/O、进程数等资源。这样可以防止不受控制的负载使整个主机饱和,这在团队或项目之间共享节点时至关重要。

然而,必须非常明确一点: 仅靠命名空间和 cgroups 并不能构成完整的安全系统。它们无法阻止内核漏洞的利用,无法过滤系统调用,也无法管理细粒度的访问策略。它们是基础,但需要加强。

改善环境的明智之举是将以下因素结合起来: 使用 seccomp 的系统调用过滤器、MAC 策略(AppArmor 或 SELinux)以及大幅降低的功能 理想情况下,这些进程应该与用户命名空间和无根模式一起使用,以最大限度地减少如果有人设法离开容器而造成的损害。

容器平台的安全威胁

在实际的容器平台中,攻击面分布在多个层级: 内核和运行时环境(runc、containerd、CRI-O)、镜像供应链、容器/编排器配置以及底层基础设施 (云、存储、身份与访问管理、网络)。

典型的攻击途径包括 软件漏洞 在内核或容器运行时,使用不安全或未维护的镜像,在 Kubernetes 或 Docker 中出现配置错误(特权容器、危险的主机挂载、过度开放的网络模式),以及 CI/CD 注册表或管道中的安全漏洞。

在许多现实生活中发生的事件中,并不存在孤立的“神奇漏洞”,而是…… 已知漏洞加上宽松的配置:以 root 用户身份运行的容器、使用 –privileged 标志、将主机文件系统挂载到容器内,以及缺少 seccomp 或 MAC。

除此之外,还有供应链问题: 公共基础镜像通常包含 CVE 漏洞、过时的软件包和控制不善的依赖项。恶意软件很容易潜入系统,或者被入侵的库可以保持休眠状态,直到执行条件出现。

因此,在生产中,它并非可有可无: 需要使用 Trivy、Clair 或 Grype 等工具扫描图像是否存在漏洞。控制使用哪些注册表(Harbor、Quay、GHCR,并制定相应的策略),并在将镜像部署到集群之前对其进行签名/验证。

Docker 与 Podman:对威胁模型的影响

在应用容器领域,Docker 和 Podman 的功能类似,但是 其架构有很大不同,这改变了安全模型。 我们用它来运行主机。

Docker 在其经典的 rootful 模式下依赖于 一个以 root 用户身份运行的中央守护进程 (dockerd)。CLI 通过 /var/run/docker.sock 或 TCP 与此守护进程通信,而正是这个特权进程创建和销毁容器、管理镜像、网络和卷,并与注册表通信。

这意味着 任何能够访问 Docker 套接字的人实际上都拥有对主机的 root 权限。由于该守护进程可以启动特权容器、挂载任意文件系统和修改关键配置,因此它实际上成为了一个单点故障。

另一方面,Podman 的诞生源于这样一种想法: 成为一款无需守护进程且尽可能“原生”运行于 Linux 系统的容器引擎没有永久的中央进程;容器依赖于用户或 systemd,与 systemd 的集成允许通过服务单元控制容器。

这种方法更符合Unix“每个进程都属于启动它的用户”的模式。 避免依赖高特权恶魔此外,Podman 还兼容 Docker 命令,简化了迁移(您甚至可以在不想安装 Docker CE 的环境中将 docker 别名设为 podman)。

无根模式和用户命名空间

降低潜在容器泄漏影响的主要实际进展之一是: 无根模式结合用户命名空间这里要讨论的问题是:“如果容器最终逃逸会发生什么?”

用户命名空间允许 将容器 UID 0 重新映射到主机上的非特权 UID换句话说,在容器内部,应用程序认为自己是 root 用户,但从主机的角度来看,该进程实际上是一个具有已分配 subuid/subgid 范围的普通用户。

这样一来,即使攻击者获得了容器内的 root 权限,并设法将内核或运行时漏洞利用程序串联起来, 返回主机时,它以无权限用户的身份进行操作。您不能覆盖根二进制文件、挂载敏感文件系统或操作您没有必要权限的设备。

Podman从一开始就按照这种模式进行设计: 尽可能默认使用无根容器化即使没有 root 权限,也使用 SELinux/AppArmor 和 cgroups,并着重强调最大限度地减少结构特权。

Docker 也具有 “真正的”无根模式在这种环境下,Dockerd 及其容器位于用户命名空间中,这与旧版 `userns-remap` 系统中守护进程始终以 root 用户身份运行的情况不同。从安全角度来看,这可以防止针对 Dockerd 的漏洞利用自动授予主机 root 权限。

使用 Podman 的容器网络:桥接、主机、macvlan 和无根接口

安全性和实际操作的另一个重要因素是 容器网络层Podman 提供多种连接机制,尤其关注无根场景。

  提升游戏性能的关键BIOS设置

Podman 4 已推出 Netavark 是一款遵循 CNI 模型的网络驱动程序 为容器提供桥接网络、macvlan 等的 IP 地址。使用 NetAvark,rootful 容器通常默认连接到名为“podman”的网络,该网络与主机上的 Linux 桥接器 (podman0) 相关联,地址为 10.88.0.0/16。

此默认桥接网络提供 通过 SNAT 规则实现基本互联网连接 它允许您使用 `-po --publish` 将容器端口暴露给主机,该参数会在 iptables/nftables 中生成 DNAT 规则。为了与 Docker 兼容,此网络不会激活内部 DNS 服务器。

我们也可以创建 用户自定义桥接网络这些解决方案将容器组彼此隔离,并提供内部 DNS。这使得容器能够在私有网络内按名称解析,更容易分离不应相互访问的服务,并提供对 MTU、防火墙和其他设置的更多控制。

在更高级的环境中,可以使用 使用 macvlan 或 ipvlan 网络将容器直接连接到物理网络 Macvlan 允许容器之间相互通信,而 ipvlan 则倾向于将它们隔离得更彻底;在部署中,如果每个容器都需要被网络的其余部分视为真正的主机,那么这些选项就很有用。

无根网络:slirp4netns 和 pasta

当用户不是root用户时,情况就会变得复杂: 没有权限的用户无法随意修改全局网络命名空间。 Podman 既不能创建任意接口,也不能使用用户空间解决方案来提供连接。

这就是它发挥作用的地方 slirp4netns 是 Podman 无根模式使用的经典机制。该项目在容器内创建了一个隔离的网络环境,并使用内核的 slirp 模块来转换地址(NAT),使容器能够通过主机的网络访问互联网。

实际上,slirp4netns 在容器的网络命名空间内创建一个 TAP 接口 (例如,tap0 的 IP 地址为 10.0.2.100,网关为 10.0.2.2),并通过用户空间实现的 TCP/IP 协议栈路由流量。这种方法虽然可行,但会增加一些开销并存在某些限制。

其中一个局限性是: 非特权用户无法使用 1024 以下的端口参数 sysctl net.ipv4.ip_unprivileged_port_start 标记哪个端口被视为“非特权”(默认情况下为 1024),但如果安全模型允许,可以对其进行仔细调整。

在 Podman 5 中,slirp4netns 被替换为 默认情况下,粘贴操作是一种无需root权限的机制。Pasta 也完全在用户空间中运行,但由于其 tap 接口和零拷贝技术,它实现了接近原生网络的性能,与 slirp4netns 相比,降低了对延迟和吞吐量的影响。

在无根容器中桥接和托管网络

即使在非root模式下,我们也可以 将无根容器连接到默认的“podman”桥接网络 使用 `--network=podman` 参数。这会利用 NetAvark 的功能,并使用 `-p` 参数将端口映射到主机,就像在 root 模式下一样。

也可以创建 无根环境中的用户自定义桥接网络在这种情况下,网桥和相关设备不是在主机的全局网络命名空间中创建的,而是在用户的命名空间中创建的,因此从主机上我们无法使用简单的 sudo ip a 命令看到这些接口。

为了检查这些无根桥,Podman 提供了以下命令。 podman unshare –rootless-netns这将打开用户网络命名空间中的一个 shell。通过该 shell,可以观察内部网桥,并验证与容器的直接连接。

这种隔离产生了一种奇特的效应: 主机与这些无根容器的 IP 地址之间可能没有直接连接。但它确实可以与映射到主机自身 IP 地址的端口一起使用,这些端口充当服务的入口点。
对于容器之间的通信,集成到用户自定义桥接网络中的 DNS 服务器允许它们按名称进行通信,这大大简化了分布式应用程序的配置。

另一端是 主机网络(–network=host)在这种模式下,容器与主机共享网络协议栈,但没有自己的 IP 地址。如果一个无根容器在 8080/tcp 端口启动了一个服务器,该服务将直接在主机的 8080 端口上可用,这样可以省去端口映射,但会降低隔离性。

Podman桌面、映像工具和本地编排

对于那些更喜欢视觉效果的人来说, Podman Desktop 提供了一个图形环境 用于管理开发机器上的容器、Pod、镜像和网络配置,作为 Docker Desktop 的直接替代方案,但依赖于无守护进程引擎。

图像采集是通过以下方式完成的: `podman pull` 命令从 `/etc/containers/registries.conf` 中定义的寄存器下载数据。如果我们指定镜像时没有指定记录名称,Podman 会尝试按照配置的记录顺序查找镜像,默认情况下使用最新的标签。

除了 Podman 的 CLI 之外,我们还可以使用 Skopeo 是一款专门用于管理、检查和复制图像的工具。 在本地注册表和存储库之间进行同步。诸如 copy、delete 或 sync 之类的命令允许您自动执行存储库之间的同步、将镜像标记为垃圾回收或在环境之间移动工件。

下载图像后, podman run 允许通过指定传输方式和路由来启动容器。 (默认情况下,Docker 会在配置的寄存器中进行传输和搜索)。常用的选项(-d、-p、--name、--pod 等)既适用于实验室环境,也适用于更正式的部署。

日常管理工作已完成。 `podman ps` 列出容器,`podman stop / start` 停止或启动现有实例,`podman rm` 删除已停止的容器。如果容器已被修改,并且我们想将其转换为新镜像,podman commit 会将这些更改保存为新名称。

  Linux 认证:如何成为 Linux 专家

使用 Podman 的 Pod 和高级执行模型

受 Kubernetes 的启发,Podman 允许 将容器分组到 Pod 中,这些 Pod 共享相同的网络堆栈和某些资源。对于数据库及其客户端,或者需要低延迟通信的多个微服务来说,这是一个完美的模式。

命令 `podman pod create` 创建一个新的 pod 并返回其 ID,但默认情况下,pod 会保持停止状态,直到关联的容器启动或使用 podman pod start 显式启动它。

要查看系统中有哪些 pod,您可以使用 podman pod ls这还会列出与每个 Pod 关联的 INFRA 容器。这个特殊的容器负责维护 Pod 的网络和其他共享资源,因此容器的数量永远不会为零。

Podman 提供特定的命令 启动、停止或重启整个 pod(podman pod start/stop/restart)但是我们也可以只对 pod 内的特定容器进行操作,而不修改其余部分,从而在应用程序级别保持进程隔离。

要向现有 pod 添加更多容器,您可以使用 podman run –pod Pod_NAME语法与单个容器相同。容器无法在不销毁它的情况下从 Pod 中“移除”,因为 Pod 成员资格与其生命周期绑定。

监督工作已完成。 使用 `podman ps --pod` 或其他特定命令来监控所有 pod 中所有容器的进程。快速识别哪些内容属于哪个组以及服务是如何分配的。

容器、LXC 和应用程序虚拟化

尽管 Docker 和 Podman 占据了讨论的主导地位, 这些并非 GNU/Linux 中唯一的容器化选项。还有一些解决方案,例如 LXC/LXD、systemd-nspawn 或 Kata Containers,它们从不同的角度解决了这个问题。

LXC 提供 非常轻量级的系统容器,拥有自己的主机名、IP、文件系统和完整的初始化功能。结合 LXD(它充当容器和虚拟机的管理程序),您可以获得非常接近拥有多台轻量级机器的体验,其性能几乎与裸机相当。

Docker/Podman 专注于 兼容 OCI 的应用容器当需要持久环境,支持多个服务和进程,并且行为类似于虚拟机,但开销更小,并且与 Linux 内核的集成非常自然时,LXC 的优势就显现出来了。

从性能角度来看, LXC 可以运行数据密集型企业应用程序,几乎没有任何性能损失。这使其成为对延迟敏感且不需要传统虚拟机强隔离性的工作负载的强大选择。

与此同时,他们的力量也得到了增强。 面向微型虚拟机和强化隔离的技术,例如 Kata Containers这些方案会在每个 pod 中启动一个拥有独立内核的小型虚拟机,而 gVisor 则会在应用程序和 Linux 之间插入一个用 Go 语言编写的“内核沙箱”。两者都能减少对共享内核的依赖,并接近虚拟机的安全模型,同时保持容器的易用性。

强化 Docker 和 Podman 的最佳实践

综上所述,构建一个稳健的容器平台,合理的方法是采用分层架构: 使用命名空间/cgroups进行分段,使用seccomp和MAC进行过滤策略,以及使用无根模式进行结构化权限限制。.

在实践中,建议采用以下标准: 容器应尽可能以非root模式运行。尤其是在多租户环境或共享云环境中。Podman 开箱即用地实现了这一点,而 Docker rootless 在需要兼容性时也遵循同样的理念。

主机内核必须 及时安装安全补丁Dirty COW、Dirty Pipe 等漏洞需要通过更新来解决,如果我们不想让容器逃逸的大门敞开,就必须相对快速地应用这些更新。

另一种非常有效的措施是使用 不可变的面向容器的发行版,例如 openSUSE MicroOS 或 Flatcar Linux其只读根目录和原子更新系统大大减少了攻击面,并且如果在补丁后出现问题,可以方便地进行回滚。

关于配置: 除高度受控的情况外,生产环境中不应使用特权容器。不要以 root 用户身份在容器内运行应用程序,尽可能使用只读文件系统,并将应用程序的功能限制在最低限度。SELinux/AppArmor 和针对应用程序实际调用定制的 seccomp 配置几乎是必需的。

最后,我们不能忘记关于……的部分 执行时间监控Falco、Sysdig Secure 或 Aqua 等工具可以帮助检测异常情况、逃逸尝试(例如,对 /proc 或 /sys 的异常访问)、非典型网络流量或可疑命令,从而在攻击发生之前提供反应时间。

通过将传统虚拟化技术与 KVM、Podman 管理的容器(最好采用无根模式)、分段良好的网络以及强大的安全加固措施相结合,可以构建一个高度灵活且相当安全的虚拟化平台。理解共享内核是关键所在,在风险或监管合规要求时依赖虚拟机或微型虚拟机,并精心管理镜像供应链,是区分实验室环境和生产就绪型基础设施的关键所在。

服务器虚拟化的类型
相关文章:
服务器虚拟化的类型