本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。
非 Root 容器與裝置
當使用者想要在 Linux 上部署使用加速器裝置的容器(透過 Kubernetes 裝置外掛程式)時,Pod 的 securityContext
中與使用者/群組 ID 相關的安全設定會觸發問題。在這篇部落格文章中,我將討論這個問題,並描述迄今為止為了解決這個問題所做的工作。這並不是要長篇大論關於修復 k/k issue。
相反,這篇文章旨在提高人們對這個問題的認識,並強調重要的裝置用例。由於 Kubernetes 正在開發新的相關功能,例如對使用者命名空間的支援,因此這是必要的。
為什麼非 root 容器無法使用裝置以及為什麼這很重要
在 Kubernetes 中運行容器的關鍵安全原則之一是最小權限原則。Pod/容器 securityContext
指定要設定的配置選項,例如 Linux 功能、MAC 策略和使用者/群組 ID 值,以實現此目的。
此外,集群管理員可以使用 PodSecurityPolicy (已棄用) 或 Pod Security Admission (alpha 版) 等工具來強制執行在集群中部署的 Pod 的所需安全設定。例如,這些設定可能要求容器必須是 runAsNonRoot
,或者禁止它們在 runAsGroup
或 supplementalGroups
中以 root 的群組 ID 運行。
在 Kubernetes 中,kubelet 建構要提供給容器的 Device
資源列表(基於來自裝置外掛程式的輸入),並且該列表包含在發送到 CRI 容器運行時的 CreateContainer CRI 訊息中。每個 Device
都包含少量資訊:主機/容器裝置路徑和所需的裝置 cgroups 權限。
Linux 容器配置的 OCI 運行時規範期望除了裝置 cgroup 欄位之外,還必須提供有關裝置的更詳細資訊
{
"type": "<string>",
"path": "<string>",
"major": <int64>,
"minor": <int64>,
"fileMode": <uint32>,
"uid": <uint32>,
"gid": <uint32>
},
CRI 容器運行時 (containerd、CRI-O) 負責從主機取得每個 Device
的此資訊。預設情況下,運行時會複製主機裝置的使用者和群組 ID
uid
(uint32, OPTIONAL) - 容器命名空間中裝置擁有者的 ID。gid
(uint32, OPTIONAL) - 容器命名空間中裝置群組的 ID。
同樣地,運行時會根據 CRI 欄位準備其他必要的 config.json
區段,包括 securityContext
中定義的欄位:runAsUser
/runAsGroup
,它們透過以下方式成為 POSIX 平台使用者結構的一部分
uid
(int, REQUIRED) 指定容器命名空間中的使用者 ID。gid
(int, REQUIRED) 指定容器命名空間中的群組 ID。additionalGids
(整數陣列, OPTIONAL) 指定要新增到進程的容器命名空間中的其他群組 ID。
但是,當嘗試運行同時新增了裝置且透過 runAsUser
/runAsGroup
設定了非 root uid/gid 的容器時,產生的 config.json
會觸發問題:容器使用者進程沒有權限使用該裝置,即使其群組 id(gid,從主機複製)對非 root 群組是允許的。這是因為容器使用者不屬於該主機群組(例如,透過 additionalGids
)。
能夠以非 root 使用者身份運行使用裝置的應用程式是正常且預期可以運作的,以便可以滿足安全原則。因此,考慮了幾種替代方案,以填補 PodSec/CRI/OCI 今天支援的差距。
為了解決這個問題做了什麼?
您可能已經從問題定義中注意到,至少可以透過手動將裝置 gid 新增到 supplementalGroups
來解決這個問題,或者在只有一個裝置的情況下,將 runAsGroup
設定為裝置的群組 id。但是,這是有問題的,因為裝置 gid 可能具有不同的值,具體取決於集群中節點的發行版/版本。例如,對於 GPU,以下針對不同發行版和版本的命令會傳回不同的 gid
Fedora 33
$ ls -l /dev/dri/
total 0
drwxr-xr-x. 2 root root 80 19.10. 10:21 by-path
crw-rw----+ 1 root video 226, 0 19.10. 10:42 card0
crw-rw-rw-. 1 root render 226, 128 19.10. 10:21 renderD128
$ grep -e video -e render /etc/group
video:x:39:
render:x:997:
Ubuntu 20.04
$ ls -l /dev/dri/
total 0
drwxr-xr-x 2 root root 80 19.10. 17:36 by-path
crw-rw---- 1 root video 226, 0 19.10. 17:36 card0
crw-rw---- 1 root render 226, 128 19.10. 17:36 renderD128
$ grep -e video -e render /etc/group
video:x:44:
render:x:133:
在您的 securityContext
中選擇哪個數字?此外,如果 runAsGroup
/runAsUser
值無法硬編碼,因為它們是在 Pod 准入期間透過外部安全策略自動分配的,該怎麼辦?
與具有 fsGroup
的卷不同,裝置沒有 deviceGroup
/deviceUser
的官方概念,CRI 運行時(或 kubelet)可以使用它們。我們考慮使用裝置外掛程式設定的容器註解(例如,io.kubernetes.cri.hostDeviceSupplementalGroup/
)來取得自訂 OCI config.json
uid/gid 值。這將需要更改所有現有的裝置外掛程式,這並非理想。
相反,首選對終端使用者無縫且無需裝置外掛程式供應商參與的解決方案。選擇的方法是在 config.json
中重複使用 runAsUser
和 runAsGroup
值用於裝置
{
"type": "c",
"path": "/dev/foo",
"major": 123,
"minor": 4,
"fileMode": 438,
"uid": <runAsUser>,
"gid": <runAsGroup>
},
使用 runc
OCI 運行時(在非 rootless 模式下),裝置是在容器命名空間中創建的 (mknod(2)
),並且使用 chmod(2)
將所有權更改為 runAsUser
/runAsGroup
。
注意
Rootless 模式和裝置不受支援。runAsUser
/runAsGroup
,並且,例如,目前忽略容器中的 USER
設定。雖然“錯誤”的部署(即,非 root securityContext
+ 裝置)可能不存在,但為了絕對確定沒有部署中斷,在 containerd 和 CRI-O 中都新增了一個選擇加入配置條目以啟用新行為。以下
device_ownership_from_security_context (bool)
預設為 false
,並且必須啟用才能使用該功能。
請參閱修復後使用裝置的非 root 容器
為了示範新行為,讓我們以使用硬體加速器、Kubernetes CPU 管理器和 HugePages 的資料平面開發套件 (DPDK) 應用程式為例。集群運行 containerd,配置如下
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
device_ownership_from_security_context = true
或 CRI-O,配置如下
[crio.runtime]
device_ownership_from_security_context = true
以及運行 DPDK 的 crypto-perf 測試工具的 Guaranteed
QoS Class Pod,其 YAML 如下
...
metadata:
name: qat-dpdk
spec:
securityContext:
runAsUser: 1000
runAsGroup: 2000
fsGroup: 3000
containers:
- name: crypto-perf
image: intel/crypto-perf:devel
...
resources:
requests:
cpu: "3"
memory: "128Mi"
qat.intel.com/generic: '4'
hugepages-2Mi: "128Mi"
limits:
cpu: "3"
memory: "128Mi"
qat.intel.com/generic: '4'
hugepages-2Mi: "128Mi"
...
為了驗證結果,請檢查容器運行的使用者和群組 ID
$ kubectl exec -it qat-dpdk -c crypto-perf -- id
它們已設定為非零值,正如預期的那樣
uid=1000 gid=2000 groups=2000,3000
接下來,檢查裝置節點權限(qat.intel.com/generic
暴露 /dev/vfio/
裝置)是否可供 runAsUser
/runAsGroup
存取
$ kubectl exec -it qat-dpdk -c crypto-perf -- ls -la /dev/vfio
total 0
drwxr-xr-x 2 root root 140 Sep 7 10:55 .
drwxr-xr-x 7 root root 380 Sep 7 10:55 ..
crw------- 1 1000 2000 241, 0 Sep 7 10:55 58
crw------- 1 1000 2000 241, 2 Sep 7 10:55 60
crw------- 1 1000 2000 241, 10 Sep 7 10:55 68
crw------- 1 1000 2000 241, 11 Sep 7 10:55 69
crw-rw-rw- 1 1000 2000 10, 196 Sep 7 10:55 vfio
最後,檢查是否也允許非 root 容器創建 HugePages
$ kubectl exec -it qat-dpdk -c crypto-perf -- ls -la /dev/hugepages/
fsGroup
為 runAsUser
提供可寫的 HugePages emptyDir 掛載點
total 0
drwxrwsr-x 2 root 3000 0 Sep 7 10:55 .
drwxr-xr-x 7 root root 380 Sep 7 10:55 ..
幫助我們測試並提供回饋!
此處描述的功能預計將有助於集群安全性和裝置權限的可配置性。若要允許非 root 容器使用裝置,需要集群管理員透過設定 device_ownership_from_security_context = true
來選擇加入該功能。若要將其設為預設設定,請測試它並提供您的回饋(透過 SIG-Node 會議或 issue)!該標誌在 CRI-O v1.22 版本中可用,並已排隊用於 containerd v1.6。
需要更多工作才能正確地支援它。已知它與 runc
一起運作,但也需要使其與其他 OCI 運行時一起運作(如果適用)。例如,Kata Containers 支援裝置直通,並允許它將裝置提供給 VM 沙箱中的容器。
此外,額外的挑戰來自於對使用者名稱和裝置的支援。這個問題仍然是 開放的,需要更多的集思廣益。
最後,需要了解 runAsUser
/runAsGroup
是否足夠,或者是否需要在 PodSpec/CRI v2 中使用類似於 fsGroups
的裝置特定設定。
感謝
感謝 Mike Brown (IBM, containerd)、Peter Hunt (Redhat, CRI-O) 和 Alexander Kanevskiy (Intel) 提供的所有回饋和良好的對話。