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

Kubernetes 1.31:細粒度的 SupplementalGroups 控制

這篇博文討論了 Kubernetes 1.31 中的一個新功能,該功能旨在改進 Pod 內容器中補充組的處理。

動機:容器映象中 /etc/group 定義的隱式組成員關係

雖然這種行為可能不受許多 Kubernetes 叢集使用者/管理員的歡迎,但 Kubernetes 預設會**合併**來自 Pod 的組資訊與容器映象中 /etc/group 中定義的資訊。

讓我們看一個例子,下面的 Pod 在其安全上下文中指定了 runAsUser=1000runAsGroup=3000supplementalGroups=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:它僅將 fsGroupsupplementalGroupsrunAsGroup 欄位中指定的組 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
...

功能可用性

要啟用 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 策略。

我如何瞭解更多資訊?

如何參與?

此功能由 SIG Node 社群推動。歡迎加入我們,與社群建立聯絡,分享你對上述功能及其他方面的想法和反饋。我們期待你的迴音!