使用者名稱空間

特性狀態: Kubernetes v1.30 [beta]

此頁面解釋瞭如何在 Kubernetes Pod 中使用使用者名稱空間。使用者名稱空間將容器內執行的使用者與主機上的使用者隔離開來。

在容器中作為 root 使用者執行的程序可以在主機上作為不同的(非 root)使用者執行;換句話說,該程序對於使用者名稱空間內的操作擁有完全許可權,但對於名稱空間外的操作則是非特權的。

你可以使用此功能來減少受損容器對主機或同一節點中其他 Pod 造成的損害。有幾處安全漏洞被評為高危嚴重,在使用者名稱空間啟用時無法利用。預計使用者名稱空間也將緩解未來的某些漏洞。

準備工作

這是一個僅限 Linux 的功能,需要 Linux 在所使用的檔案系統上支援 idmap 掛載。這意味著:

  • 在節點上,你用於 /var/lib/kubelet/pods/ 的檔案系統,或你為此配置的自定義目錄,需要 idmap 掛載支援。
  • Pod 卷中使用的所有檔案系統都必須支援 idmap 掛載。

實際上,這意味著你需要至少 Linux 6.3,因為 tmpfs 在該版本中開始支援 idmap 掛載。這通常是必需的,因為許多 Kubernetes 功能都使用 tmpfs(預設掛載的服務賬戶令牌使用 tmpfs,Secrets 使用 tmpfs 等)。

Linux 6.3 中一些支援 idmap 掛載的流行檔案系統有:btrfs、ext4、xfs、fat、tmpfs、overlayfs。

此外,容器執行時及其底層 OCI 執行時必須支援使用者名稱空間。以下 OCI 執行時提供支援:

  • crun 版本 1.9 或更高(建議版本 1.13+)。
  • runc 版本 1.2 或更高

要在 Kubernetes 中使用使用者名稱空間,你還需要一個 CRI 容器執行時來使用 Kubernetes Pod 的此功能:

  • containerd:2.0(及更高版本)支援容器的使用者名稱空間。
  • CRI-O:1.25(及更高版本)支援容器的使用者名稱空間。

你可以在 GitHub 上的一個議題中檢視 cri-dockerd 中使用者名稱空間支援的狀態。

介紹

使用者名稱空間是 Linux 的一項功能,它允許將容器中的使用者對映到主機中的不同使用者。此外,授予 Pod 在使用者名稱空間中的能力僅在該名稱空間中有效,在名稱空間之外無效。

Pod 可以透過將 pod.spec.hostUsers 欄位設定為 false 來選擇使用使用者名稱空間。

kubelet 將選擇 Pod 對映到的主機 UID/GID,並且將以確保同一節點上的兩個 Pod 不使用相同對映的方式進行選擇。

pod.spec 中的 runAsUserrunAsGroupfsGroup 等欄位始終指容器內部的使用者。這些使用者將用於卷掛載(在 pod.spec.volumes 中指定),因此主機 UID/GID 對 Pod 可以掛載的卷的寫入/讀取沒有任何影響。換句話說,Pod 掛載的卷中建立/讀取的 inode 將與 Pod 不使用使用者名稱空間時相同。

透過這種方式,Pod 可以輕鬆啟用和停用使用者名稱空間(而不影響其卷的檔案所有權),並且還可以透過僅設定容器內的適當使用者(RunAsUserRunAsGroupfsGroup 等)來與不帶使用者名稱空間的 Pod 共享卷。這適用於 Pod 可以掛載的任何卷,包括 hostPath(如果 Pod 允許掛載 hostPath 卷)。

預設情況下,啟用此功能時,有效的 UID/GID 範圍為 0-65535。這適用於檔案和程序(runAsUserrunAsGroup 等)。

使用此範圍之外的 UID/GID 的檔案將被視為屬於溢位 ID,通常為 65534(在 /proc/sys/kernel/overflowuid/proc/sys/kernel/overflowgid 中配置)。但是,即使以 65534 使用者/組身份執行,也無法修改這些檔案。

如果使用配置旋鈕擴充套件了 0-65535 範圍,則上述限制適用於擴充套件範圍。

大多數需要以 root 身份執行但未訪問其他主機名稱空間或資源的應用程式,如果啟用使用者名稱空間,則無需任何更改即可繼續正常執行。

理解 Pod 的使用者名稱空間

預設配置的多個容器執行時(如 Docker Engine、containerd、CRI-O)使用 Linux 名稱空間進行隔離。其他技術也存在,並且可以與這些執行時一起使用(例如 Kata Containers 使用虛擬機器而不是 Linux 名稱空間)。本頁適用於使用 Linux 名稱空間進行隔離的容器執行時。

建立 Pod 時,預設會使用多個新的名稱空間進行隔離:網路名稱空間用於隔離容器的網路,PID 名稱空間用於隔離程序檢視等。如果使用使用者名稱空間,它將隔離容器中的使用者與節點中的使用者。

這意味著容器可以作為 root 執行並對映到主機上的非 root 使用者。在容器內部,程序會認為它以 root 身份執行(因此 aptyum 等工具可以正常工作),而實際上該程序在主機上沒有特權。你可以透過在主機上執行 ps aux 來檢查容器程序正在哪個使用者下執行來驗證這一點。ps 顯示的使用者與你在容器內部執行 id 命令時看到的使用者不同。

這種抽象限制了可能發生的情況,例如,如果容器設法逃逸到主機。鑑於容器在主機上作為非特權使用者執行,它能對主機做的事情是有限的。

此外,由於每個 Pod 上的使用者將對映到主機上不同的、不重疊的使用者,它們對其他 Pod 也能做的事情也是有限的。

授予 Pod 的能力也僅限於 Pod 使用者名稱空間,在名稱空間之外大多無效,有些甚至完全失效。以下是兩個示例:

  • CAP_SYS_MODULE 如果授予使用使用者名稱空間的 Pod,則沒有任何效果,Pod 無法載入核心模組。
  • CAP_SYS_ADMIN 僅限於 Pod 的使用者名稱空間,在名稱空間之外無效。

如果不使用使用者名稱空間,以 root 身份執行的容器,在容器逃逸的情況下,在節點上具有 root 許可權。如果授予容器某些能力,這些能力在主機上也有效。當我們使用使用者名稱空間時,這些情況都不成立。

如果你想了解更多關於使用使用者名稱空間時更改的詳細資訊,請參閱 man 7 user_namespaces

設定節點以支援使用者名稱空間

預設情況下,kubelet 為 Pod 分配高於 0-65535 範圍的 UID/GID,因為假定主機的資料夾和程序使用該範圍內的 UID/GID,這對於大多數 Linux 發行版來說是標準做法。這種方法可以防止主機的 UID/GID 與 Pod 的 UID/GID 發生重疊。

避免重疊對於緩解諸如 CVE-2021-25741 之類的漏洞影響很重要,這些漏洞可能導致 Pod 讀取主機上的任意檔案。如果 Pod 和主機的 UID/GID 不重疊,Pod 能夠做的事情將受到限制:Pod 的 UID/GID 不會與主機的檔案的所有者/組匹配。

kubelet 可以為 Pod 使用自定義的使用者 ID 和組 ID 範圍。要配置自定義範圍,節點需要:

  • 系統中的一個使用者 kubelet (此處不能使用其他使用者名稱)
  • 安裝了二進位制檔案 getsubidsshadow-utils 的一部分)並在 kubelet 二進位制檔案的 PATH 中。
  • kubelet 使用者配置的附屬 UID/GID(參見man 5 subuidman 5 subgid)。

此設定僅收集 UID/GID 範圍配置,不更改執行 kubelet 的使用者。

你必須遵循為 kubelet 使用者分配的附屬 ID 範圍的一些約束條件:

  • 附屬使用者 ID,即 Pod 的 UID 範圍的起始 ID,必須是 65536 的倍數,並且必須大於或等於 65536。換句話說,你不能將範圍 0-65535 中的任何 ID 用於 Pod;kubelet 強制執行此限制,以防止意外建立不安全的配置。

  • 附屬 ID 計數必須是 65536 的倍數。

  • 附屬 ID 計數必須至少為 65536 x <maxPods>,其中 <maxPods> 是節點上可以執行的最大 Pod 數量。

  • 你必須為使用者 ID 和組 ID 分配相同的範圍。其他使用者是否具有與組 ID 範圍不一致的使用者 ID 範圍無關緊要。

  • 所有分配的範圍都不應與任何其他分配重疊。

  • 附屬配置必須只有一行。換句話說,你不能有多個範圍。

例如,你可以定義 /etc/subuid/etc/subgid,使它們都包含 kubelet 使用者的以下條目:

# The format is
#   name:firstID:count of IDs
# where
# - firstID is 65536 (the minimum value possible)
# - count of IDs is 110 * 65536
#   (110 is the default limit for number of pods on the node)

kubelet:65536:7208960

每個 Pod 的 ID 計數

從 Kubernetes v1.33 開始,可以在KubeletConfiguration中設定每個 Pod 的 ID 計數。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
userNamespaces:
  idsPerPod: 1048576

idsPerPod (uint32) 的值必須是 65536 的倍數。預設值為 65536。此值僅適用於 kubelet 啟動後使用此 KubeletConfiguration 建立的容器。正在執行的容器不受此配置的影響。

在 Kubernetes v1.33 之前,每個 Pod 的 ID 計數被硬編碼為 65536。

與 Pod 安全准入檢查整合

特性狀態: Kubernetes v1.29 [alpha]

對於啟用使用者名稱空間的 Linux Pod,Kubernetes 以受控方式放寬Pod 安全標準的應用。此行為可以透過功能門 UserNamespacesPodSecurityStandards 控制,允許終端使用者提前選擇啟用。管理員必須確保,如果使用此功能門,叢集內的所有節點都已啟用使用者名稱空間。

如果啟用了相關的功能門並建立了一個使用使用者名稱空間的 Pod,以下欄位將不會受到約束,即使在強制執行“基線”或“受限”Pod 安全標準的上下文中也是如此。此行為不構成安全問題,因為具有使用者名稱空間的 Pod 內的 root 實際上指的是容器內的使用者,該使用者從不對映到主機上的特權使用者。以下是這些情況下 Pod 檢查的欄位列表:

  • spec.securityContext.runAsNonRoot
  • spec.containers[*].securityContext.runAsNonRoot
  • spec.initContainers[*].securityContext.runAsNonRoot
  • spec.ephemeralContainers[*].securityContext.runAsNonRoot
  • spec.securityContext.runAsUser
  • spec.containers[*].securityContext.runAsUser
  • spec.initContainers[*].securityContext.runAsUser
  • spec.ephemeralContainers[*].securityContext.runAsUser

限制

當 Pod 使用使用者名稱空間時,不允許使用其他主機名稱空間。特別是,如果將 hostUsers: false 設定為 false,則不允許設定以下任何項:

  • hostNetwork: true
  • hostIPC: true
  • hostPID: true

任何容器都不能使用 volumeDevices (原始塊卷,如 /dev/sda)。這包括 Pod Spec 中的所有容器陣列:

  • containers
  • initContainers
  • ephemeralContainers

指標和可觀測性

kubelet 匯出了兩個特定於使用者名稱空間的 prometheus 指標:

  • started_user_namespaced_pods_total:一個計數器,用於跟蹤嘗試建立的使用者名稱空間 Pod 的數量。
  • started_user_namespaced_pods_errors_total:一個計數器,用於跟蹤建立使用者名稱空間 Pod 時發生的錯誤數量。

下一步

本頁面上的專案引用了提供 Kubernetes 所需功能的第三方產品或專案。Kubernetes 專案作者不負責這些第三方產品或專案。有關更多詳細資訊,請參見 CNCF 網站指南

在提議新增額外第三方連結的更改之前,你應該閱讀內容指南

最後修改於 2025 年 7 月 1 日下午 3:15 PST: docs: 在 1.34 中新增 userns 更改 (f3a6e8e88d)