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 屬於第二類,而 Linux 伺服器上由 libvirt 管理的 KVM 虛擬機器則更接近第一類,即使它與宿主機共享資源。

這種方法最大的優點在於每個虛擬機器都擁有自己完整的堆疊:核心、使用者空間、服務、網路和儲存。這提供了極大的靈活性,並允許在同一台實體機上運行不同的作業系統和核心版本。

在 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`或`modprobe kvm_amd / kvm_intel`的指令應該能夠載入核心模組,且不會出現錯誤訊息。如果 `dmesg` 沒有顯示任何相關信息,或者`lsmod | grep kvm`只顯示 `kvm` 和 `irqbypass`,並且缺少 CPU 特定的模組,則很可能是硬體虛擬化沒有對客戶機系統開放,或者在固件中被禁用。

在這種情況下,即使在虛擬機器中啟用了所有可能的功能,如果 VirtualBox 不支援巢狀虛擬化或設定錯誤,KVM 也無法正常運作。解決方法是檢查宿主機器管理程式(VirtualBox、VMware 等)的配置,並在可用時啟用巢狀虛擬化。

容器:作業系統層級的輕量級虛擬化

與傳統虛擬機相比,容器提供了一種不同的方法:它們不模擬所有硬件,而是重用主機內核,並使用 Linux 內核函數隔離進程和資源。

容器本質上是一組使用命名空間和 cgroups 封裝的流程。在這個「環境」中,應用程式認為它運行在自己的機器上,擁有自己的檔案系統、網路介面和進程 ID (PID),但實際上,它與宿主機共享核心。

容器能夠提供接近原生系統的效能,因為它無需第二個內核,也無需完整的硬體模擬。其開銷極小:僅足以掛載命名空間、使用 cgroups 限制資源以及強制執行安全性原則(seccomp、AppArmor、SELinux 等)。

然而,這種設計存在一個重大後果:所有工作負載共享同一個實體核心。該核心中的一個嚴重漏洞可能突然成為運行在該節點上的所有容器的潛在逃逸途徑。

因此,人們常說容器隔離不像虛擬機器那樣「絕對」。如果有人成功利用了嚴重的核心漏洞,命名空間就不再是有效的屏障,容器和主機之間的邊界就可能被打破。

命名空間和 cgroups:容器隔離的基礎

在 Linux 中,容器隔離建立在兩個主要支柱之上:命名空間用於分離系統視圖,cgroups 用於限制和統計資源。

  虛擬機器 vs 雙系統啟動:哪種方式最適合安裝 Linux 和 Windows?

命名空間隔離了進程ID (PID)、網路、掛載點、進程間通訊 (IPC)、主機名稱(統一主機空間,UTS)和使用者等資源。這樣,進程只能「看到」自身命名空間內的進程、網路介面、檔案系統和主機名,使其感覺像是在不同的系統中運作。

cgroups 可讓您指定容器可以使用的 CPU、記憶體、磁碟 I/O、進程數等資源。這可以防止不受控制的負載壓垮整個主機,這在團隊或專案之間共享節點時至關重要。

然而,必須明確的是,命名空間和 cgroups 本身並不能構成完整的安全系統。它們無法阻止核心漏洞的利用、過濾系統呼叫或管理細粒度的存取策略。它們是基礎,但需要進一步加強。

加固環境的合理方法是將系統呼叫過濾器與 seccomp、MAC 策略(AppArmor 或 SELinux)以及大幅降低進程功能相結合,理想情況下,最好再結合使用者命名空間和無根模式,以最大限度地減少有人設法逃離容器時造成的損害。

容器平台的安全威脅

在真正的容器平台中,攻擊面分佈在多個層面:核心和運行時(runc、containerd、CRI-O)、鏡像供應鏈、容器/編排器配置以及底層基礎設施(雲端、儲存、IAM、網路)。

常見的攻擊途徑包括核心或容器運行時中的軟體漏洞、使用不安全或未維護的映像、在 Kubernetes 或 Docker 中出現配置錯誤(特權容器、危險的主機掛載、過度開放的網路模式)以及註冊表或 CI/CD 管道中的安全缺陷。

在許多現實事件中,並不存在單一的“神奇漏洞”,而是已知漏洞與寬鬆配置的組合:容器以 root 使用者身份運行、使用 --privileged 標誌、在容器內掛載主機檔案系統以及缺少 seccomp 或 MAC。

除此之外,供應鏈問題也不容忽視:公開的基礎映像通常包含漏洞程式碼 (CVE)、過時的軟體包以及管理不善的依賴項。惡意軟體很容易繞過這些限制,或者被入侵的庫可以保持休眠狀態,直到合適的條件出現才執行。

因此,在生產環境中,這不是選項:您必須使用 Trivy、Clair 或 Grype 等工具掃描鏡像是否存在漏洞,控制使用哪些註冊表(Harbor、Quay、GHCR 及其策略),並在將鏡像部署到叢集之前對其進行簽署/驗證。

Docker 與 Podman:對威脅模型的影響

在應用容器領域,Docker 和 Podman 涵蓋了類似的功能,但它們的架構卻大相逕庭,改變了我們在主機上使用的安全模型。

Docker 在其經典的 root 模式下,依賴一個以 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 用戶,但從主機的角度來看,該進程實際上是具有已分配子 UID/子群組 ID 範圍的普通用戶。

這樣一來,即使攻擊者在容器內獲得了 root 權限並成功鏈接了內核或運行時漏洞利用程序,返回主機後,他們也只能以非特權用戶的身份進行操作。他們無法覆蓋 root 二進位檔案、掛載敏感檔案系統,也無法在缺乏必要權限的情況下操作裝置。

Podman 從一開始就以這種模型為設計理念:盡可能預設使用無根容器化,即使沒有 root 權限也使用 SELinux/AppArmor 和 cgroups,並專注於最小化結構權限。

Docker 現在也提供了真正的無根模式,在這個模式下,dockerd 和容器都位於使用者命名空間中,這與舊版的 userns-remap 不同,舊版的 userns-remap 中守護程式仍然以 root 使用者身分執行。從安全性角度來看,這可以防止針對 dockerd 的漏洞自動授予主機 root 權限。

使用 Podman 的容器網路:橋接、主機、macvlan 和無根介面

容器網路層是安全性和實際運作的另一個重要因素。 Podman 提供了多種連接機制,尤其關注無根部署場景。

  Linux 用戶在使用 Windows 11 時遇到的挫折

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 rootless 使用的經典機制。該專案在容器內創建一個隔離的網路環境,並使用核心的 slirp 模組執行位址轉換 (NAT),從而允許容器透過主機網路存取網際網路。

實際上,slirp4netns會在容器的網路命名空間內建立一個 TAP 介面(例如,IP 位址為 10.0.2.100、網關為 10.0.2.2 的 tap0),並透過用戶空間中實現的 TCP/IP 協定堆疊路由流量。雖然這種方法可行,但會增加一些開銷並存在一些限制。

其中一項限制是,非特權使用者不能使用 1024 以下的連接埠。 sysctl參數 net.ipv4.ip_unprivileged_port_start 標記了使用者被視為「非特權」的連接埠(預設為 1024),儘管如果安全模型允許,可以對其進行仔細調整。

在 Podman 5 中,slirp4netns 被pasta 取代,成為預設的無根機制。 Pasta 也完全在用戶空間運行,但由於採用了 tap 介面和零拷貝技術,它實現了接近原生網路的效能,與 slirp4netns 相比,顯著降低了對延遲和吞吐量的影響。

在無根容器中橋接和託管網絡

即使在非 root 模式下,我們也可以使用 `--network=podman`將無 root 容器連接到預設的「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使用 `podman pull` 指令取得鏡像,該指令會從 `/etc/containers/registries.conf` 檔案中定義的鏡像倉庫下載鏡像。如果指定鏡像時未指定鏡像倉庫名稱,Podman 會嘗試在配置的鏡像倉庫順序中尋找該鏡像,預設使用 `latest` 標籤。

除了 Podman CLI 之外,我們還可以使用skopeo 作為專用工具來管理、檢查鏡像以及在鏡像倉庫和本地倉庫之間複製鏡像。諸如 copy、delete 和 sync 之類的命令允許我們自動執行倉庫之間的同步,將鏡像標記為需要垃圾回收,以及在不同環境之間移動鏡像檔案。

映像下載完成後,可以使用 `podman run` 指令啟動容器,只需指定傳輸方式和路徑即可(預設使用 Docker 傳輸,並在已配置的映像倉庫中搜尋)。常用的選項(-d、-p、--name、--pod 等)既適用於實驗室環境,也適用於較正式的部署。

日常管理使用`podman ps` 列出容器,`podman stop/start` 停止或啟動現有實例,以及 `podman rm` 刪除已停止的容器。如果容器已被修改,並且我們想要將其轉換為新鏡像,則可以使用 `podman commit` 將這些變更儲存到新名稱中。

  什麼是UPS?為什麼你的電腦需要UPS?

使用 Podman 的 Pod 和進階執行模型

受 Kubernetes 的啟發,Podman 讓你可以將容器分組到共用相同網路堆疊和特定資源的 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 標準的應用程式容器,而 LXC 在需要具有多個服務和進程的持久環境時表現出色,其行為類似於虛擬機,但開銷更小,並且與 Linux 核心的整合非常自然。

從效能角度來看,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 權限和原子更新系統大大減少了攻擊面,如果打補丁後出現問題,也便於回滾。

關於設定:生產環境中除極少數嚴格控制的情況外,不應使用特權容器;不要在容器內以 root 使用者身分執行應用程式;盡可能使用唯讀檔案系統;並將功能限制在最低限度。 SELinux/AppArmor 和針對應用程式實際呼叫客製化的 seccomp 配置幾乎是必需的。

最後,我們不能忽視執行時間監控的重要性。像 Falco、Sysdig Secure 或 Aqua 這樣的工具可以幫助檢測異常情況、逃逸嘗試(例如對 /proc 或 /sys 的異常訪問)、非典型網路流量或可疑命令,從而為攻擊發生前的反應爭取時間。

透過將傳統虛擬化技術與 KVM、Podman 管理的容器(最好採用無根模式)、分段良好的網路以及強大的安全加固措施相結合,可以建立一個高度靈活且相當安全的虛擬化平台。理解共享核心是關鍵所在,在風險或監管合規要求時依賴虛擬機或微型虛擬機,並精心管理鏡像供應鏈,是區分實驗室環境和生產就緒型基礎設施的關鍵所在。

伺服器虛擬化的類型
相關文章:
伺服器虛擬化的類型