本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
Kubernetes 1.31:細粒度的 SupplementalGroups 控制
這篇博文討論了 Kubernetes 1.31 中的一個新功能,該功能旨在改進 Pod 內容器中補充組的處理。
動機:容器映象中 /etc/group
定義的隱式組成員關係
雖然這種行為可能不受許多 Kubernetes 叢集使用者/管理員的歡迎,但 Kubernetes 預設會**合併**來自 Pod 的組資訊與容器映象中 /etc/group
中定義的資訊。
讓我們看一個例子,下面的 Pod 在其安全上下文中指定了 runAsUser=1000
、runAsGroup=3000
和 supplementalGroups=4000
。
apiVersion: v1
kind: Pod
metadata:
name: implicit-groups
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
supplementalGroups: [4000]
containers:
- name: ctr
image: registry.k8s.io/e2e-test-images/agnhost:2.45
command: [ "sh", "-c", "sleep 1h" ]
securityContext:
allowPrivilegeEscalation: false
ctr
容器中 id
命令的結果是什麼?
# Create the Pod:
$ kubectl apply -f https://k8s.io/blog/2024-08-22-Fine-grained-SupplementalGroups-control/implicit-groups.yaml
# Verify that the Pod's Container is running:
$ kubectl get pod implicit-groups
# Check the id command
$ kubectl exec implicit-groups -- id
那麼,輸出應類似於此
uid=1000 gid=3000 groups=3000,4000,50000
補充組(groups
欄位)中的組 ID 50000
是從哪裡來的,即使在 Pod 的清單中根本沒有定義 50000
?答案是容器映象中的 /etc/group
檔案。
檢查容器映象中 /etc/group
的內容,應該會顯示以下內容
$ kubectl exec implicit-groups -- cat /etc/group
...
user-defined-in-image:x:1000:
group-defined-in-image:x:50000:user-defined-in-image
啊哈!在最後一個條目中,容器的主使用者 1000
屬於組 50000
。
因此,容器映象中為容器主使用者在 /etc/group
中定義的組成員關係被**隱式**地合併到了來自 Pod 的資訊中。請注意,這是當前的 CRI 實現從 Docker 繼承的設計決策,社群直到現在才真正重新考慮過它。
這有什麼問題?
從容器映象中 /etc/group
**隱式**合併的組資訊可能會引起一些問題,特別是在訪問卷時(詳情請參閱 kubernetes/kubernetes#112879),因為在 Linux 中檔案許可權是由 uid/gid 控制的。更糟糕的是,來自 /etc/group
的隱式 gid 無法被任何策略引擎檢測/驗證,因為在清單中沒有關於隱式組資訊的線索。這也可能成為 Kubernetes 的一個安全問題。
Pod 中細粒度的 SupplementalGroups 控制:SupplementaryGroupsPolicy
為了解決上述問題,Kubernetes 1.31 在 Pod 的 .spec.securityContext
中引入了一個新欄位 supplementalGroupsPolicy
。
此欄位提供了一種控制如何為 Pod 中容器程序計算補充組的方法。可用的策略如下
Merge:為容器主使用者在
/etc/group
中定義的組成員關係將被合併。如果未指定,將應用此策略(即,為了向後相容,保持現有行為)。Strict:它僅將
fsGroup
、supplementalGroups
或runAsGroup
欄位中指定的組 ID 作為容器程序的補充組附加。這意味著為容器主使用者在/etc/group
中定義的任何組成員關係都不會被合併。
讓我們看看 Strict
策略是如何工作的。
apiVersion: v1
kind: Pod
metadata:
name: strict-supplementalgroups-policy
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
supplementalGroups: [4000]
supplementalGroupsPolicy: Strict
containers:
- name: ctr
image: registry.k8s.io/e2e-test-images/agnhost:2.45
command: [ "sh", "-c", "sleep 1h" ]
securityContext:
allowPrivilegeEscalation: false
# Create the Pod:
$ kubectl apply -f https://k8s.io/blog/2024-08-22-Fine-grained-SupplementalGroups-control/strict-supplementalgroups-policy.yaml
# Verify that the Pod's Container is running:
$ kubectl get pod strict-supplementalgroups-policy
# Check the process identity:
kubectl exec -it strict-supplementalgroups-policy -- id
輸出應類似於此
uid=1000 gid=3000 groups=3000,4000
你可以看到 Strict
策略可以從 groups
中排除組 50000
!
因此,確保 supplementalGroupsPolicy: Strict
(透過某些策略機制強制執行)有助於防止 Pod 中出現隱式的補充組。
說明
實際上,這還不夠,因為具有足夠許可權/能力的容器可以更改其程序身份。有關詳細資訊,請參閱下一節。Pod 狀態中附加的程序身份
此功能還透過 .status.containerStatuses[].user.linux
欄位公開了附加到容器的第一個容器程序的程序身份。這有助於檢視是否附加了隱式的組 ID。
...
status:
containerStatuses:
- name: ctr
user:
linux:
gid: 3000
supplementalGroups:
- 3000
- 4000
uid: 1000
...
說明
請注意,status.containerStatuses[].user.linux
欄位中的值是**首次附加**到容器中第一個容器程序的程序身份。如果容器具有足夠的許可權來呼叫與程序身份相關的系統呼叫(例如 setuid(2)
、setgid(2)
或 setgroups(2)
等),容器程序可以更改其身份。因此,**實際**的程序身份將是動態的。功能可用性
要啟用 supplementalGroupsPolicy
欄位,必須使用以下元件
- Kubernetes:v1.31 或更高版本,並啟用了
SupplementalGroupsPolicy
特性門控。自 v1.31 起,該門控被標記為 Alpha。 - CRI 執行時
- containerd:v2.0 或更高版本
- CRI-O:v1.31 或更高版本
你可以在節點的 .status.features.supplementalGroupsPolicy
欄位中檢視該功能是否受支援。
apiVersion: v1
kind: Node
...
status:
features:
supplementalGroupsPolicy: true
接下來是什麼?
Kubernetes SIG Node 希望——並期望——該功能將在未來的 Kubernetes 版本中提升到 Beta 並最終正式釋出(GA),這樣使用者就不再需要手動啟用該特性門控。
為了向後相容,當未指定 supplementalGroupsPolicy
時,會應用 Merge
策略。
我如何瞭解更多資訊?
- 為 Pod 或容器配置安全上下文 以獲取有關
supplementalGroupsPolicy
的更多詳細資訊 - KEP-3619:細粒度的 SupplementalGroups 控制
如何參與?
此功能由 SIG Node 社群推動。歡迎加入我們,與社群建立聯絡,分享你對上述功能及其他方面的想法和反饋。我們期待你的迴音!