從 PodSecurityPolicy 遷移到內建 PodSecurity 准入控制器
本頁面描述了從 PodSecurityPolicy 遷移到內建 PodSecurity 准入控制器的過程。這可以透過結合使用模擬執行、audit
和 warn
模式有效地完成,儘管如果使用可修改的 PSP,這會變得更加困難。
準備工作
你的 Kubernetes 伺服器版本必須是 v1.22 或更高。
要檢查版本,請輸入 kubectl version
。
如果你當前執行的 Kubernetes 版本不是 1.34,你可能希望切換到檢視該頁面的 Kubernetes 文件版本,該版本與你實際執行的版本相符。
本頁面假設你已經熟悉基本的 Pod 安全准入 概念。
總體方法
從 PodSecurityPolicy 遷移到 Pod 安全准入有多種策略。以下步驟是其中一種可能的遷移路徑,目標是最大限度地降低生產中斷和安全漏洞的風險。
- 決定 Pod 安全准入是否適合你的使用場景。
- 審查名稱空間許可權
- 簡化和標準化 PodSecurityPolicy
- 更新名稱空間
- 識別適當的 Pod 安全級別
- 驗證 Pod 安全級別
- 強制執行 Pod 安全級別
- 繞過 PodSecurityPolicy
- 審查名稱空間建立過程
- 停用 PodSecurityPolicy
0. 決定 Pod 安全准入是否適合你
Pod 安全准入旨在開箱即用地滿足最常見的安全需求,並在叢集中提供一套標準的安全級別。然而,它的靈活性不如 PodSecurityPolicy。值得注意的是,以下功能由 PodSecurityPolicy 支援,但 Pod 安全准入不支援:
- 設定預設安全限制 - Pod 安全准入是一個非修改性准入控制器,這意味著它在驗證 Pod 之前不會修改它們。如果你依賴 PSP 的這個方面,你需要修改你的工作負載以滿足 Pod 安全限制,或者使用 修改准入 Webhook 來進行這些更改。有關更多詳細資訊,請參閱下面的 簡化和標準化 PodSecurityPolicy。
- 對策略定義的細粒度控制 - Pod 安全准入只支援 3 個標準級別。如果你需要對特定限制進行更多控制,那麼你需要使用 驗證准入 Webhook 來強制執行這些策略。
- 子名稱空間策略粒度 - PodSecurityPolicy 允許你將不同的策略繫結到不同的 ServiceAccount 或使用者,即使在單個名稱空間內也是如此。這種方法存在許多陷阱,不建議使用,但如果你確實需要此功能,則需要使用第三方 webhook。例外情況是,如果你只需要完全豁免特定使用者或 RuntimeClass,在這種情況下,Pod 安全准入會暴露一些 豁免的靜態配置。
即使 Pod 安全准入不能滿足你的所有需求,它也被設計成與其他策略強制機制**互補**,並且可以作為其他准入 Webhook 的有用備用機制執行。
1. 審查名稱空間許可權
Pod 安全准入由名稱空間上的標籤控制。這意味著任何可以更新(或修補或建立)名稱空間的人都可以修改該名稱空間的 Pod 安全級別,這可能被用來繞過更嚴格的策略。在繼續之前,請確保只有受信任的特權使用者擁有這些名稱空間許可權。不建議向不應擁有提升許可權的使用者授予這些強大許可權,但如果必須這樣做,則需要使用准入 Webhook 對在 Namespace 物件上設定 Pod 安全標籤施加額外限制。
2. 簡化和標準化 PodSecurityPolicy
在本節中,你將減少可修改的 PodSecurityPolicy 並刪除超出 Pod 安全標準範圍的選項。你應該對所修改的原始 PodSecurityPolicy 的離線副本進行此處推薦的更改。克隆的 PSP 應該有一個不同的名稱,並且在字母順序上位於原始名稱之前(例如,在其前面加上 0
)。暫時不要在 Kubernetes 中建立新的策略 - 這將在下面的 推出更新的策略 部分介紹。
2.a. 消除純粹修改欄位
如果 PodSecurityPolicy 正在修改 Pod,那麼當最終關閉 PodSecurityPolicy 時,可能會導致 Pod 不符合 Pod 安全級別要求。為了避免這種情況,你應該在切換之前消除所有 PSP 修改。不幸的是,PSP 並未將修改欄位和驗證欄位明確分開,因此這不是一個直接的遷移過程。
你可以從消除純粹用於修改且與驗證策略無關的欄位開始。這些欄位(也列在 PodSecurityPolicy 對映到 Pod 安全標準 參考中)是:
.spec.defaultAllowPrivilegeEscalation
.spec.runtimeClass.defaultRuntimeClassName
.metadata.annotations['seccomp.security.alpha.kubernetes.io/defaultProfileName']
.metadata.annotations['apparmor.security.beta.kubernetes.io/defaultProfileName']
.spec.defaultAddCapabilities
- 儘管從技術上講這是一個可修改和可驗證的欄位,但它們應該合併到.spec.allowedCapabilities
中,後者執行相同的驗證而不會進行修改。
注意
移除這些可能會導致工作負載缺少必要的配置,並引起問題。有關如何安全推出這些更改的建議,請參閱下面的 推出更新的策略。2.b. 消除 Pod 安全標準未涵蓋的選項
PodSecurityPolicy 中有幾個欄位未涵蓋在 Pod 安全標準中。如果你必須強制執行這些選項,則需要使用准入 Webhook 補充 Pod 安全准入,這超出了本指南的範圍。
首先,你可以移除 Pod 安全標準未涵蓋的純驗證欄位。這些欄位(也列在將 PodSecurityPolicy 對映到 Pod 安全標準參考中,並標註“無意見”)是:
.spec.allowedHostPaths
.spec.allowedFlexVolumes
.spec.allowedCSIDrivers
.spec.forbiddenSysctls
.spec.runtimeClass
你還可以移除以下與 POSIX / UNIX 組控制相關的欄位。
.spec.runAsGroup
.spec.supplementalGroups
.spec.fsGroup
剩餘的修改欄位是正確支援 Pod 安全標準所必需的,稍後需要根據具體情況進行處理
.spec.requiredDropCapabilities
- 對於 Restricted 配置檔案,需要丟棄ALL
。.spec.seLinux
- (僅在MustRunAs
規則下可修改)用於強制執行 Baseline 和 Restricted 配置檔案的 SELinux 要求。.spec.runAsUser
- (在RunAsAny
規則下非修改性)用於強制執行 Restricted 配置檔案的RunAsNonRoot
。.spec.allowPrivilegeEscalation
- (僅在設定為false
時可修改)Restricted 配置檔案所需。
2.c. 推出更新的 PSP
接下來,你可以將更新的策略部署到你的叢集中。你應該謹慎行事,因為移除可修改選項可能會導致工作負載缺少所需的配置。
對於每個更新的 PodSecurityPolicy
- 識別在原始 PSP 下執行的 Pod。這可以使用
kubernetes.io/psp
註解來完成。例如,使用 kubectlPSP_NAME="original" # Set the name of the PSP you're checking for kubectl get pods --all-namespaces -o jsonpath="{range .items[?(@.metadata.annotations.kubernetes\.io\/psp=='$PSP_NAME')]}{.metadata.namespace} {.metadata.name}{'\n'}{end}"
- 將這些正在執行的 Pod 與原始 Pod 規約進行比較,以確定 PodSecurityPolicy 是否修改了 Pod。對於由工作負載資源建立的 Pod,你可以將 Pod 與控制器資源中的 PodTemplate 進行比較。如果識別出任何更改,則應使用所需的配置更新原始 Pod 或 PodTemplate。需要審查的欄位是:
.metadata.annotations['container.apparmor.security.beta.kubernetes.io/*']
(將 * 替換為每個容器名稱).spec.runtimeClassName
.spec.securityContext.fsGroup
.spec.securityContext.seccompProfile
.spec.securityContext.seLinuxOptions
.spec.securityContext.supplementalGroups
- 在容器中,位於
.spec.containers[*]
和.spec.initContainers[*]
下.securityContext.allowPrivilegeEscalation
.securityContext.capabilities.add
.securityContext.capabilities.drop
.securityContext.readOnlyRootFilesystem
.securityContext.runAsGroup
.securityContext.runAsNonRoot
.securityContext.runAsUser
.securityContext.seccompProfile
.securityContext.seLinuxOptions
- 建立新的 PodSecurityPolicy。如果任何 Role 或 ClusterRole 授予所有 PSP 的
use
許可權,這可能會導致使用新的 PSP 而不是其可變對應項。 - 更新你的授權以授予對新 PSP 的訪問許可權。在 RBAC 中,這意味著更新任何授予原始 PSP 的
use
許可權的 Role 或 ClusterRole,使其也授予對更新後的 PSP 的許可權。 - 驗證:經過一段時間的穩定執行後,重新執行步驟 1 中的命令,檢視是否有任何 Pod 仍在使用原始 PSP。請注意,Pod 需要在新策略推出後重新建立才能完全驗證。
- (可選) 確認原始 PSP 不再使用後,可以刪除它們。
3. 更新名稱空間
以下步驟需要在叢集中的每個名稱空間上執行。這些步驟中引用的命令使用 $NAMESPACE
變數來指代正在更新的名稱空間。
3.a. 識別適當的 Pod 安全級別
開始審查Pod 安全標準,並熟悉 3 種不同級別。
有幾種方法可以選擇名稱空間的 Pod 安全級別
- 根據名稱空間的安全要求 - 如果你熟悉名稱空間的預期訪問級別,則可以根據這些要求選擇適當的級別,類似於在新叢集上處理此問題的方式。
- 透過現有的 PodSecurityPolicies - 使用將 PodSecurityPolicies 對映到 Pod 安全標準參考,你可以將每個 PSP 對映到 Pod 安全標準級別。如果你的 PSP 不是基於 Pod 安全標準的,你可能需要決定是選擇一個至少與 PSP 一樣寬鬆的級別,還是選擇一個至少與 PSP 一樣嚴格的級別。你可以使用此命令檢視給定名稱空間中 Pod 正在使用哪些 PSP
kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
- 透過現有 Pod - 使用 驗證 Pod 安全級別 下的策略,你可以測試 Baseline 和 Restricted 級別,檢視它們是否對現有工作負載具有足夠的許可性,並選擇特權最小的有效級別。
注意
上述選項 2 和 3 基於**現有**的 Pod,可能會遺漏當前未執行的工作負載,例如 CronJob、縮放到零的工作負載或其他尚未推出的工作負載。3.b. 驗證 Pod 安全級別
一旦你為名稱空間選擇了 Pod 安全級別(或者如果你正在嘗試多個級別),最好先對其進行測試(如果使用特權級別,可以跳過此步驟)。Pod 安全包含幾個工具,可幫助測試和安全推出配置檔案。
首先,你可以模擬執行策略,這將根據應用的策略評估當前在名稱空間中執行的 Pod,而不會使新策略生效
# $LEVEL is the level to dry-run, either "baseline" or "restricted".
kubectl label --dry-run=server --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL
此命令將對任何在提議級別下無效的**現有** Pod 返回警告。
第二種方法更適合捕獲當前未執行的工作負載:審計模式。在審計模式下執行(與強制模式相對),違反策略級別的 Pod 會記錄在審計日誌中,可以在一段時間後進行審查,但不會被禁止。警告模式類似,但會立即向用戶返回警告。你可以使用此命令在名稱空間上設定審計級別
kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/audit=$LEVEL
如果這些方法中的任何一種產生意外的違規,你需要更新違規的工作負載以滿足策略要求,或者放寬名稱空間 Pod 安全級別。
3.c. 強制執行 Pod 安全級別
當你確信所選級別可以安全地在名稱空間上強制執行時,你可以更新名稱空間以強制執行所需的級別
kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL
3.d. 繞過 PodSecurityPolicy
最後,你可以透過將完全特權 PSP 繫結到名稱空間中的所有服務賬戶,從而在名稱空間級別有效繞過 PodSecurityPolicy。
# The following cluster-scoped commands are only needed once.
kubectl apply -f privileged-psp.yaml
kubectl create clusterrole privileged-psp --verb use --resource podsecuritypolicies.policy --resource-name privileged
# Per-namespace disable
kubectl create -n $NAMESPACE rolebinding disable-psp --clusterrole privileged-psp --group system:serviceaccounts:$NAMESPACE
由於特權 PSP 是非修改性的,並且 PSP 准入控制器總是優先選擇非修改性的 PSP,這將確保此名稱空間中的 Pod 不再被 PodSecurityPolicy 修改或限制。
像這樣按名稱空間停用 PodSecurityPolicy 的優勢在於,如果出現問題,你可以透過刪除 RoleBinding 輕鬆回滾更改。只需確保預先存在的 PodSecurityPolicy 仍然存在!
# Undo PodSecurityPolicy disablement.
kubectl delete -n $NAMESPACE rolebinding disable-psp
4. 審查名稱空間建立過程
現在,現有名稱空間已更新為強制執行 Pod 安全准入,你應該確保你的新名稱空間建立過程和/或策略已更新,以確保為新名稱空間應用適當的 Pod 安全配置檔案。
你還可以靜態配置 Pod 安全准入控制器,為未標記的名稱空間設定預設的強制、審計和/或警告級別。有關更多資訊,請參閱配置准入控制器。
5. 停用 PodSecurityPolicy
最後,你已準備好停用 PodSecurityPolicy。為此,你需要修改 API 伺服器的准入配置:如何關閉准入控制器?。
要驗證 PodSecurityPolicy 准入控制器是否不再啟用,你可以透過模擬一個無權訪問任何 PodSecurityPolicy 的使用者(請參閱 PodSecurityPolicy 示例)來手動執行測試,或透過檢查 API 伺服器日誌進行驗證。在啟動時,API 伺服器會輸出列出已載入的准入控制器外掛的日誌行
I0218 00:59:44.903329 13 plugins.go:158] Loaded 16 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,TaintNodesByCondition,Priority,DefaultTolerationSeconds,ExtendedResourceToleration,PersistentVolumeLabel,DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,DefaultIngressClass,MutatingAdmissionWebhook.
I0218 00:59:44.903350 13 plugins.go:161] Loaded 14 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DenyServiceExternalIPs,ValidatingAdmissionWebhook,ResourceQuota.
你應該會看到 PodSecurity
(在驗證准入控制器中),並且兩個列表中都不應包含 PodSecurityPolicy
。
一旦你確定 PSP 准入控制器已停用(並且經過足夠長的穩定執行時間以確信你不需要回滾),你就可以自由刪除你的 PodSecurityPolicy 以及任何相關的 Role、ClusterRole、RoleBinding 和 ClusterRoleBinding(只需確保它們沒有授予任何其他不相關的許可權)。