本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kubernetes 1.30:對具有使用者名稱字空間的 Pod 的 Beta 支援

Linux 提供了不同的名字空間來隔離程序。例如,一個典型的 Kubernetes Pod 執行在一個網路名字空間中以隔離網路身份,以及一個 PID 名字空間以隔離程序。

一個被遺忘的 Linux 名字空間是使用者名稱字空間。這個名字空間允許我們將容器內使用的使用者和組識別符號(UID 和 GID)與主機上的識別符號隔離開來。

這是一個強大的抽象,允許我們以“root”身份執行容器:我們在容器內部是 root,可以在 Pod 內部做 root 能做的一切事情,但我們與主機的互動僅限於非特權使用者可以做的事情。這對於限制容器逃逸的影響非常有用。

容器逃逸是指容器內的程序能夠利用容器執行時或核心中某些未修補的漏洞逃逸到主機上,並能訪問或修改主機或其他容器上的檔案。如果我們使用使用者名稱字空間執行 Pod,容器對主機其餘部分的許可權會減少,其能訪問的容器外檔案也會受到限制。

在 Kubernetes v1.25 中,我們僅為無狀態 Pod 引入了對使用者名稱字空間的支援。Kubernetes 1.28 取消了這一限制,現在,隨著 Kubernetes 1.30 的釋出,我們正將其推進到 Beta 階段!

什麼是使用者名稱字空間?

注意:Linux 使用者名稱字空間與 Kubernetes 名字空間是不同的概念。前者是 Linux 核心特性;後者是 Kubernetes 特性。

使用者名稱字空間是 Linux 的一項功能,可將容器的 UID 和 GID 與主機上的 UID 和 GID 隔離開來。容器中的識別符號可以對映到主機上的識別符號,使得不同容器使用的主機 UID/GID 永遠不會重疊。此外,這些識別符號可以對映到主機上非特權的、不重疊的 UID 和 GID。這帶來了兩個關鍵好處:

  • 防止橫向移動:由於不同容器的 UID 和 GID 被對映到主機上不同的 UID 和 GID,即使容器逃脫了容器邊界,它們也很難相互攻擊。例如,假設容器 A 在主機上執行的 UID 和 GID 與容器 B 不同,那麼它對容器 B 的檔案和程序的操作將受到限制:只能讀/寫檔案允許“others”訪問的內容,因為它永遠不會擁有所有者或組許可權(主機上的 UID/GID 保證對不同容器是不同的)。

  • 增強主機隔離:由於 UID 和 GID 被對映到主機上的非特權使用者,如果容器逃脫了容器邊界,即使它在容器內以 root 身份執行,它在主機上也沒有特權。這極大地保護了它可以讀/寫的主機檔案,可以向其傳送訊號的程序等。此外,授予的能力僅在使用者名稱字空間內有效,在主機上無效,從而限制了容器逃逸可能造成的影響。

Image showing IDs 0-65535 are reserved to the host, pods use higher IDs

使用者名稱字空間 ID 分配

不使用使用者名稱字空間的情況下,以 root 身份執行的容器在發生容器逃逸時,在節點上擁有 root 許可權。如果容器被授予了某些能力,這些能力在主機上也有效。而使用使用者名稱字空間時,這些情況都不會發生(當然,bug 除外 🙂)。

1.30 中的變化

在 Kubernetes 1.30 中,除了將使用者名稱字空間推向 Beta 階段外,致力於該功能的貢獻者們還

  • 引入了一種方式,讓 kubelet 可以為 UID/GID 對映使用自定義範圍
  • 增加了一種方式,讓 Kubernetes 可以強制要求執行時支援使用者名稱字空間所需的所有功能。如果不支援,當嘗試建立使用使用者名稱字空間的 Pod 時,Kubernetes 會顯示一個明確的錯誤。在 1.30 之前,如果容器執行時不支援使用者名稱字空間,Pod 可能會在沒有使用者名稱字空間的情況下被建立。
  • 添加了更多測試,包括在 cri-tools 倉庫中的測試。

您可以檢視關於使用者名稱字空間的文件,瞭解如何為對映配置自定義範圍。

演示

幾個月前,CVE-2024-21626 被披露。此漏洞評分為 8.6(高危)。它允許攻擊者逃脫容器並讀/寫節點上以及同一節點上託管的其他 Pod 的任何路徑

Rodrigo 建立了一個演示,利用了 CVE-2024-21626,並展示了在沒有使用者名稱字空間時可行的漏洞利用,在使用使用者名稱字空間時如何被緩解。

請注意,即使使用使用者名稱字空間,攻擊者仍然可以對主機檔案系統執行“others”許可權位所允許的操作。因此,該 CVE 並沒有被完全阻止,但其影響被大大降低了。

節點系統要求

要使用此功能,對 Linux 核心版本和容器執行時有特定要求。

在 Linux 上,你需要 Linux 6.3 或更高版本。這是因為該功能依賴於一個名為 idmap mounts 的核心特性,而對 idmap mounts 與 tmpfs 一起使用的支援是在 Linux 6.3 中合併的。

如果你正在使用帶有 crun 的 CRI-O,一如既往,你可以期望 CRI-O 1.30 支援 Kubernetes 1.30。請注意,你還需要 crun 1.9 或更高版本。如果你正在使用帶有 runc 的 CRI-O,這仍然不受支援。

Containerd 的支援目前目標是 containerd 2.0,並且同樣的 crun 版本要求也適用。如果你正在使用帶有 runc 的 containerd,這仍然不受支援。

請注意,containerd 1.7 增加了對使用者名稱字空間的*實驗性*支援,這是在 Kubernetes 1.25 和 1.26 中實現的。我們在 Kubernetes 1.27 中進行了重新設計,這需要對容器執行時進行更改。這些更改在 containerd 1.7 中不存在,因此它只適用於 Kubernetes 1.25 和 1.26 中的使用者名稱字空間支援。

containerd 1.7 的另一個限制是,它需要在 Pod 啟動期間更改容器映象內每個檔案和目錄的所有權。這會產生儲存開銷,並可能顯著影響容器的啟動延遲。containerd 2.0 可能會包含一個實現,該實現將消除增加的啟動延遲和儲存開銷。如果你計劃在生產環境中使用 containerd 1.7 和使用者名稱字空間,請考慮這一點。

CRI-O 不存在任何這些 containerd 1.7 的限制。

我如何參與?

你可以透過多種方式聯絡 SIG Node

你也可以直接聯絡我們:

  • GitHub: @rata @giuseppe @saschagrunert
  • Slack: @rata @giuseppe @sascha