為 Kubernetes 集群?jiǎn)⒂?Pod 安全策略

最近有客戶反饋在開(kāi)啟了安全策略的集群中部署產(chǎn)品失敗,因此研究了一下 Kubernetes 提供的 pod 安全策略。
文中的演示和示例均在 v1.18.17 集群中通過(guò)驗(yàn)證。
Pod Security Policies
Pod Security Policies (下文簡(jiǎn)稱 psp 或 pod 安全策略)是一種集群級(jí)別的全局資源,能夠?qū)?pod 的創(chuàng)建和更新進(jìn)行細(xì)粒度的授權(quán)控制。具體來(lái)說(shuō),一個(gè) psp 對(duì)象定義了一組安全性條件,一個(gè) pod 的 spec 字段必須滿足這些條件以及適用相關(guān)字段的默認(rèn)值,其創(chuàng)建或更新請(qǐng)求才會(huì)被 apiserver 所接受。
具體的 pod 字段和安全條件可見(jiàn)文檔 what-is-a-pod-security-policy[1] 。
啟用 Pod Security Policies
Kubernetes 默認(rèn)不開(kāi)啟 pod 安全策略功能,在集群中啟用 pod 安全策略的步驟大體上分為三步:
授予用戶訪問(wèn)安全策略資源的權(quán)限,通常會(huì)授權(quán)給整個(gè)命名空間的 service account。 在集群中創(chuàng)建指定的安全策略資源。 啟用 apiserver 的 admission-controller 插件。
注意步驟 1、2 的順序不重要,因?yàn)樗鼈儾粫?huì)產(chǎn)生實(shí)際影響。
但步驟 3 推薦在最后一步執(zhí)行,否則一旦啟用 admission-controller 插件,如果集群中沒(méi)有可用的 pod 安全策略或者未對(duì)安全策略資源預(yù)先授權(quán),所有 pod 的創(chuàng)建都會(huì)被拒絕,包括 kube-system 命名空間下的系統(tǒng)管理組件如 apiserver(但由于是受 kubelet 管理的靜態(tài) pod 實(shí)際上容器依然會(huì)運(yùn)行)。
RBAC 身份認(rèn)證
創(chuàng)建可訪問(wèn)所有安全策略資源的 ClusterRole:
apiVersion:?rbac.authorization.k8s.io/v1
kind:?ClusterRole
metadata:
??name:?all-psp
rules:
-?apiGroups:?['policy']
??resources:?['podsecuritypolicies']
??verbs:?????['use']
通過(guò) ClusterRoleBinding 將創(chuàng)建的角色綁定到指定命名空間下的所有 service account(也可以授權(quán)給指定的 sa 或者用戶):
apiVersion:?rbac.authorization.k8s.io/v1
kind:?ClusterRoleBinding
metadata:
??name:?cluster-psp-bind
roleRef:
??kind:?ClusterRole
??name:?all-psp
??apiGroup:?rbac.authorization.k8s.io
subjects:
#?授權(quán)給指定命名空間下的所有?service?account(推薦做法):
-?kind:?Group
??apiGroup:?rbac.authorization.k8s.io
??name:?system:nodes
??namespace:?kube-system
-?kind:?Group
??apiGroup:?rbac.authorization.k8s.io
??name:?system:serviceaccounts:kube-system
-?kind:?Group
??apiGroup:?rbac.authorization.k8s.io
??name:?system:serviceaccounts:security-test
#?也可授權(quán)給指定的?service?account?或者用戶(不推薦):
-?kind:?ServiceAccount
??name:??service?account?name>
??namespace:??pod?namespace>
-?kind:?User
??apiGroup:?rbac.authorization.k8s.io
??name:??user?name>
#?授權(quán)給所有的?service?accounts:
-?kind:?Group
??apiGroup:?rbac.authorization.k8s.io
??name:?system:serviceaccounts
#?授權(quán)給所有已認(rèn)證的用戶:
-?kind:?Group
??apiGroup:?rbac.authorization.k8s.io
??name:?system:authenticated
創(chuàng)建安全策略資源
在集群中創(chuàng)建一個(gè) PodSecurityPolicy 資源。寬松權(quán)限版本:
apiVersion:?policy/v1beta1
kind:?PodSecurityPolicy
metadata:
??name:?privileged
??annotations:
????seccomp.security.alpha.kubernetes.io/allowedProfileNames:?'*'
spec:
??privileged:?true
??allowPrivilegeEscalation:?true
??allowedCapabilities:
??-?'*'
??volumes:
??-?'*'
??hostNetwork:?true
??hostPorts:
??-?min:?0
????max:?65535
??hostIPC:?true
??hostPID:?true
??runAsUser:
????rule:?'RunAsAny'
??seLinux:
????rule:?'RunAsAny'
??supplementalGroups:
????rule:?'RunAsAny'
??fsGroup:
????rule:?'RunAsAny'
嚴(yán)格權(quán)限版本:
apiVersion:?policy/v1beta1
kind:?PodSecurityPolicy
metadata:
??name:?restricted
??annotations:
????seccomp.security.alpha.kubernetes.io/allowedProfileNames:?'docker/default,runtime/default'
????apparmor.security.beta.kubernetes.io/allowedProfileNames:?'runtime/default'
????apparmor.security.beta.kubernetes.io/defaultProfileName:??'runtime/default'
spec:
??privileged:?false
??#?Required?to?prevent?escalations?to?root.
??allowPrivilegeEscalation:?false
??requiredDropCapabilities:
????-?ALL
??#?Allow?core?volume?types.
??volumes:
????-?'configMap'
????-?'emptyDir'
????-?'projected'
????-?'secret'
????-?'downwardAPI'
????#?Assume?that?ephemeral?CSI?drivers?&?persistentVolumes?set?up?by?the?cluster?admin?are?safe?to?use.
????-?'csi'
????-?'persistentVolumeClaim'
????-?'ephemeral'
??hostNetwork:?false
??hostIPC:?false
??hostPID:?false
??runAsUser:
????#?Require?the?container?to?run?without?root?privileges.
????rule:?'MustRunAsNonRoot'
??seLinux:
????#?This?policy?assumes?the?nodes?are?using?AppArmor?rather?than?SELinux.
????rule:?'RunAsAny'
??supplementalGroups:
????rule:?'MustRunAs'
????ranges:
??????#?Forbid?adding?the?root?group.
??????-?min:?1
????????max:?65535
??fsGroup:
????rule:?'MustRunAs'
????ranges:
??????#?Forbid?adding?the?root?group.
??????-?min:?1
????????max:?65535
??readOnlyRootFilesystem:?false
啟用 admission controller 插件
啟用 admission controller 的 psp 插件有兩種方式:
在已存在的集群中通過(guò)修改 apiserver 的靜態(tài) manifest 文件,為 apiserver 增加啟動(dòng)參數(shù) enable-admission-plugins=PodSecurityPolicy。kubelet 會(huì)自動(dòng)檢測(cè)到變更并重啟 apiserver。下面的示例使用 sed 對(duì)原有參數(shù)進(jìn)行了替換:
$?sed?-i?'s/enable-admission-plugins=NodeRestriction/enable-admission-plugins=NodeRestriction,PodSecurityPolicy/'?/etc/kubernetes/manifests/kube-apiserver.yaml
或者在初始化集群時(shí),在 kubeadm 配置文件中添加額外參數(shù)。
apiVersion:?kubeadm.k8s.io/v1beta2
kind:?ClusterConfiguration
apiServer:
?extraArgs:
????enable-admission-plugins:?"PodSecurityPolicy"
驗(yàn)證 psp 的安全限制
我們?cè)谏衔氖跈?quán)過(guò)的 security-test 命名空間進(jìn)行測(cè)試,驗(yàn)證 psp 對(duì) pod 的限制條件。
首先確保在集群中應(yīng)用了嚴(yán)格版本的 psp 資源,然后嘗試通過(guò) deployment 創(chuàng)建一個(gè)需要使用 hostNetwork 的 pod:
apiVersion:?apps/v1
kind:?Deployment
metadata:
??name:?nginx-hostnetwork
spec:
??selector:
????matchLabels:
??????run:?nginx
??template:
????metadata:
??????labels:
????????run:?nginx
????spec:
??????hostNetwork:?true
??????containers:
??????-?image:?nginx
????????imagePullPolicy:?Always
????????name:?nginx-privileged
創(chuàng)建并查看結(jié)果:
$?kubectl?create?-f?hostnetwork-pod.yaml?-n?security-test
deployment.apps/nginx-hostnetwork?created
$?kubectl?get?deploy?-n?security-test?nginx-hostnetwork
NAME????????????????READY???UP-TO-DATE???AVAILABLE???AGE
nginx-hostnetwork???0/1?????0????????????0???????????17s
$?kubectl?-n?security-test?get?event?|?grep?"pod?security?policy"
103s????????Warning???FailedCreate?????????????deployment/nginx-hostnetwork????????????????????????????????Error?creating:?pods?"nginx-hostnetwork-"?is?forbidden:?unable?to?validate?against?any?pod?security?policy:?[spec.securityContext.hostNetwork:?Invalid?value:?true:?Host?network?is?not?allowed?to?be?used]
局限性
如果有 pod 違反了安全策略,解決方案是要么調(diào)整 pod 的規(guī)格,要么修改 pod 安全策略資源。psp 資源是全局生效的,不能針對(duì)不同的命名空間設(shè)置不同的安全策略級(jí)別,這是一個(gè)很明顯的局限性。
另外對(duì) psp 的授權(quán)機(jī)制也比較復(fù)雜。如果沒(méi)有授權(quán)或者未創(chuàng)建安全策略,結(jié)果是所有 pod 被拒絕,這也導(dǎo)致在集群中默認(rèn)開(kāi)啟該功能的操作難度很大。
從 Kubernetes v1.21 開(kāi)始,Pod Security Policy 將被棄用,并將在 v1.25 中刪除。Kubernetes 引入了 Pod Security Admission 作為其替代者,我們將在下文中詳細(xì)解析。
Pod Security Admission
為什么要替換 psp
KEP-2579[2] 詳細(xì)闡述了使用 Pod Security Admission 替代 Pod Security Policy 的三點(diǎn)主要理由:
將策略與用戶或 service account 綁定的模型削弱了安全性。 功能無(wú)法流暢切換,在沒(méi)有安全策略的情況下無(wú)法關(guān)閉。 API 不一致且缺乏靈活性。
新的 Pod Security Admission 機(jī)制在易用性和靈活性上都有了很大提升,從使用角度有以下四點(diǎn)顯著不同:
可以在集群中默認(rèn)開(kāi)啟,只要不添加約束條件就不會(huì)觸發(fā)對(duì) pod 的校驗(yàn)。 只在命名空間級(jí)別生效,可以為不同命名空間通過(guò)添加標(biāo)簽的方式設(shè)置不同的安全限制。 可以為特定的用戶、命名空間或者運(yùn)行時(shí)設(shè)置豁免規(guī)則。 根據(jù)實(shí)踐預(yù)設(shè)了三種安全等級(jí),不需要由用戶單獨(dú)去設(shè)置每一個(gè)安全條件。
工作方式
Pod Security Admission 將原來(lái) Pod Security Policy 的安全條件劃分成三種預(yù)設(shè)的安全等級(jí):
privileged: 不受限,向 pod 提供所有可用的權(quán)限。baseline:最低限度的限制策略,防止已知的特權(quán)升級(jí)。restricted:嚴(yán)格限制策略,遵循當(dāng)前 Pod 加固的最佳實(shí)踐。
三種等級(jí)從寬松到嚴(yán)格遞增,各自包含了不同限度的安全條件[3],適用于不同的 pod 工作場(chǎng)景。此外還可以將安全等級(jí)設(shè)置為固定的 Kubernetes 版本,這樣即使集群升級(jí)到了新的版本且新版本的安全等級(jí)定義發(fā)生變化,依然可以按舊版本的安全條件對(duì) pod 進(jìn)行約束。
當(dāng) pod 與安全等級(jí)沖突時(shí),我們可通過(guò)三種模式來(lái)選擇不同的處理方式:
enforce:只允許符合安全等級(jí)要求的 pod,拒絕與安全等級(jí)沖突的 pod。audit:只將安全等級(jí)沖突記錄在集群 event 中,不會(huì)拒絕 pod。warn:與安全等級(jí)沖突時(shí)會(huì)向用戶返回一個(gè)警告信息,但不會(huì)拒絕 pod。
audit 和 warn 模式是獨(dú)立的,如果同時(shí)需要兩者的功能必須分別設(shè)置兩種模式。
應(yīng)用安全策略不再需要?jiǎng)?chuàng)建單獨(dú)的集群資源,只需要為命名空間設(shè)置控制標(biāo)簽:
pod-security.kubernetes.io/: ?
pod-security.kubernetes.io/-version: ?
下文會(huì)有更完整的示例。
在舊版本啟用 psa
雖然 Pod Security Admission 是一個(gè)在 Kubernetes v1.22 引入的功能,但舊版本可以通過(guò)安裝 PodSecurity admission webhook 來(lái)啟用該功能,具體步驟如下:
$?git?clone?https://github.com/kubernetes/pod-security-admission.git
$?cd?pod-security-admission/webhook
$?make?certs
$?kubectl?apply?-k?.
以上來(lái)自官方文檔的步驟在 v1.18.17 會(huì)有兩個(gè)兼容性問(wèn)題,具體問(wèn)題和解決方案如下:
kubectl 內(nèi)置的 kustomize 版本不支持 "replacements" 字段:
$?kubectl?apply?-k?.
error:?json:?unknown?field?"replacements"解決方案:安裝最新版本的 kusomize 然后在同一目錄執(zhí)行
$?kustomize?build?.?|?kubectl?apply?-f?-
manifest/50-deployment.yaml文件中定義的Deployment.spec.template.spec.containers[0].securityContext字段在 v1.19 版本才開(kāi)始引入,因此 v1.18 需要將該字段修改為對(duì)應(yīng)的 annotation 版本,詳見(jiàn) Seccomp[4]:
error:?error?validating?"STDIN":?error?validating?data:?ValidationError(Deployment.spec.template.spec.containers[0].securityContext):?unknown?field?"seccompProfile"?in?io.k8s.api.core.v1.SecurityContext;?if?you?choose?to?ignore?these?errors,?turn?validation?off?with?--validate=false
驗(yàn)證 psa 的安全限制
首先創(chuàng)建一個(gè)新的命名空間 psa-test 用于測(cè)試,并將其定義強(qiáng)制應(yīng)用 baseline 安全等級(jí),并對(duì) restricted 等級(jí)進(jìn)行警告和審計(jì):
apiVersion:?v1
kind:?Namespace
metadata:
??name:?psa-test
??labels:
????pod-security.kubernetes.io/enforce:?baseline
????pod-security.kubernetes.io/enforce-version:?v1.18
????#?We?are?setting?these?to?our?_desired_?`enforce`?level.
????pod-security.kubernetes.io/audit:?restricted
????pod-security.kubernetes.io/audit-version:?v1.18
????pod-security.kubernetes.io/warn:?restricted
????pod-security.kubernetes.io/warn-version:?v1.18
接著在該命名空間中創(chuàng)建上文示例中用過(guò)的 deployment:
$?kubectl?create?-f?hostnetwork-pod.yaml?-n?psa-test
deployment.apps/nginx-hostnetwork?created
$?kubectl?get?deploy?-n?psa-test?nginx-hostnetwork
NAME????????????????READY???UP-TO-DATE???AVAILABLE???AGE
nginx-hostnetwork???0/1?????0????????????0???????????17s
$?kubectl?-n?psa-test?get?event?|?grep?PodSecurity
104s????????Warning???FailedCreate????????replicaset/nginx-hostnetwork-644cdd6598???Error?creating:?admission?webhook?"pod-security-webhook.kubernetes.io"?denied?the?request:?pods?"nginx-hostnetwork-644cdd6598-7rb5m"?is?forbidden:?violates?PodSecurity?"baseline:v1.23":?host?namespaces?(hostNetwork=true)
參考鏈接
KEP-2579: Pod Security Admission Control[5] Pod Security Admission | Kubernetes[6] Pod Security Standards | Kubernetes[7]
引用鏈接
what-is-a-pod-security-policy: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#what-is-a-pod-security-policy
[2]KEP-2579: https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/2579-psp-replacement
[3]不同限度的安全條件: https://kubernetes.io/docs/concepts/security/pod-security-standards/#profile-details
[4]0].securityContext` 字段在 v1.19 版本才開(kāi)始引入,因此 v1.18 需要將該字段修改為對(duì)應(yīng)的 annotation 版本,詳見(jiàn) [Seccomp: https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp
[5]KEP-2579: Pod Security Admission Control: https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/2579-psp-replacement
[6]Pod Security Admission | Kubernetes: https://kubernetes.io/docs/concepts/security/pod-security-admission/
[7]Pod Security Standards | Kubernetes: https://kubernetes.io/docs/concepts/security/pod-security-standards/#profile-details
原文鏈接:https://www.waynerv.com/posts/enable-pod-security-policy-for-cluster/


你可能還喜歡
點(diǎn)擊下方圖片即可閱讀

云原生是一種信仰???
關(guān)注公眾號(hào)
后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!


點(diǎn)擊?"閱讀原文"?獲取更好的閱讀體驗(yàn)!
發(fā)現(xiàn)朋友圈變“安靜”了嗎?


