使用者名稱空間
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 執行時提供支援:
注意
一些 OCI 執行時不包含在 Linux Pod 中使用使用者名稱空間所需的支援。如果你使用託管的 Kubernetes,或者從軟體包下載並設定它,則叢集中的節點可能使用了不支援此功能的執行時。要在 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
中的 runAsUser
、runAsGroup
、fsGroup
等欄位始終指容器內部的使用者。這些使用者將用於卷掛載(在 pod.spec.volumes
中指定),因此主機 UID/GID 對 Pod 可以掛載的卷的寫入/讀取沒有任何影響。換句話說,Pod 掛載的卷中建立/讀取的 inode 將與 Pod 不使用使用者名稱空間時相同。
透過這種方式,Pod 可以輕鬆啟用和停用使用者名稱空間(而不影響其卷的檔案所有權),並且還可以透過僅設定容器內的適當使用者(RunAsUser
、RunAsGroup
、fsGroup
等)來與不帶使用者名稱空間的 Pod 共享卷。這適用於 Pod 可以掛載的任何卷,包括 hostPath
(如果 Pod 允許掛載 hostPath
卷)。
預設情況下,啟用此功能時,有效的 UID/GID 範圍為 0-65535。這適用於檔案和程序(runAsUser
、runAsGroup
等)。
使用此範圍之外的 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 身份執行(因此 apt
、yum
等工具可以正常工作),而實際上該程序在主機上沒有特權。你可以透過在主機上執行 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
(此處不能使用其他使用者名稱) - 安裝了二進位制檔案
getsubids
(shadow-utils 的一部分)並在 kubelet 二進位制檔案的PATH
中。 - 為
kubelet
使用者配置的附屬 UID/GID(參見man 5 subuid
和man 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 網站指南。
在提議新增額外第三方連結的更改之前,你應該閱讀內容指南。