什麼是pod安全策略
pod安全策略是叢集級別的用於控制pod安全相關選項的一種資源.PodSecurityPolicy
定義了一系列pod相要進行在系統中必須滿足的約束條件,以衣一些預設的約束值.它允許管理員控制以下方面內容
Control Aspect | Field Names |
---|---|
以特權執行容器 | privileged |
使用宿主名稱空間 | hostPID, hostIPC |
使用宿主網路和埠 | hostNetwork, hostPorts |
使用儲存卷型別 | volumes |
使用宿主機檔案系統 | allowedHostPaths |
flex儲存卷白名單 | allowedFlexVolumes |
分配擁有 Pod 資料卷的 FSGroup | fsGroup |
只讀root檔案系統 | readOnlyRootFilesystem |
容器的使用者id和組id | runAsUser, runAsGroup, supplementalGroups |
禁止提升到root許可權 | allowPrivilegeEscalation, defaultAllowPrivilegeEscalation |
Linux能力 | defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities |
SELinux上下文 | seLinux |
允許容器載入的proc型別 | allowedProcMountTypes |
The AppArmor profile used by containers | annotations |
The seccomp profile used by containers | annotations |
The sysctl profile used by containers | annotations |
啟用pod安全策略
pod安全策略作為可選的(但強烈建議的)admission controller的實現.pod安全策略通過啟用admission controller來實現,但是僅僅啟用而沒有對策略授權則會導致整個叢集無法建立pod!
由於pod安全策略api(policy/v1beta1/podsecuritypolicy)獨立於admission controller
之外啟用,對於已經存在的叢集建議在啟用admission controller
之前新增並授權策略.
授權策略
當一個pod安全策略資源被建立(前面說過,psp(PodSecurityPolicy )pod安全策略是一種kubernetes資源),它什麼都不會做.為了使用它,請求操作的使用者或者目標pod的serviceaccount必須通過策略的use
動詞來授權.
絕大部分kubernetes pod並不是直接由使用者直接建立的.相反,典型使用場景是它們通過Deployment
或者ReplicaSet
間接被建立,或者通過控制器管理器的其它模板控制器來建立.對控制器進行策略授權也將對它所建立的所有pod進行策略授權.因此首選的授權方法是對pod的serviceaccount進行策略授權(後面有示例).
通過RBAC授權
RBAC是kubernetes標準的授權模式,並且很容易用於授權安全策略使用.
首先,一個角色(role)或者叢集角色(clusterRole)需要被授權使用(use動詞)
它想要的策略.對角色的授權類似下面
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: <role name>
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames:
- 一系列要進行授權的資源名稱
然後把叢集角色(或角色)與授權的使用者繫結
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: 繫結名稱
roleRef:
kind: ClusterRole
name: 角色名稱
apiGroup: rbac.authorization.k8s.io
subjects:
# Authorize specific service accounts:
- kind: ServiceAccount
name: 授權的serviceaccount名稱
namespace: <authorized pod namespace>
# Authorize specific users (not recommended):
- kind: User
apiGroup: rbac.authorization.k8s.io
name: 授權的使用者名稱
如果一個角色繫結(不是叢集角色繫結)被使用,它僅對和它處於同一名稱空間下的pod才能進行有效策略授權,這樣同樣適用於使用者和使用者組
# Authorize all service accounts in a namespace:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:serviceaccounts
# Or equivalently, all authenticated users in a namespace:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:authenticated
故障排除
控制器管理器必須執行在安全的api埠上,並且不能有超級許可權.不然請求就會繞過認證和授權模組,將導致所有的策略均被允許,並且使用者可以建立特權pod
策略順序
除了限制pod的建立和更新,pod安全策略還用於提供它所控制的諸多欄位的預設值.當有多個策略時,pod安全策略根據以下因素來選擇策略
任何成功通過驗證沒有警告的策略將被使用
如果是請求建立pod,則按通過驗證的策略按字母表順序被選用
否則,如果是一個更新請求,將會返回錯誤.因為在更新操作過程中不允許pod變化
示例
以下示例假定你執行的叢集開啟了pod安全策略admission controller並且你有叢集管理員許可權
初始設定
我們為示例建立一個名稱空間和一個serviceaccount.我們使用這個serviceaccount來模擬一個非管理員使用者
kubectl create namespace psp-example
kubectl create serviceaccount -n psp-example fake-user
kubectl create rolebinding -n psp-example fake-editor --clusterrole=edit --serviceaccount=psp-example:fake-user
為了方便辨認我們使用的賬戶,我們建立兩個別名
alias kubectl-admin='kubectl -n psp-example'
alias kubectl-user='kubectl --as=system:serviceaccount:psp-example:fake-user -n psp-example'
建立一個策略和一個pod
以下定義檔案定義了一個簡單pod安全策略(PodSecurityPolicy),這個策略僅僅阻止建立特權pod
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: example
spec:
privileged: false # Don't allow privileged pods!
# The rest fills in some required fields.
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
我們使用kubectl命令來應用以上檔案.
現在,做為一個非特權使用者,我們建立一個簡單pod
kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pause
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
EOF
Error from server (Forbidden): error when creating "STDIN": pods "pause" is forbidden: unable to validate against any pod security policy: []
發生了什麼?儘管pod安全策略已建立,不管是pod的serviceaccount還是fack-user都沒有許可權使用這個策略.
kubectl-user auth can-i use podsecuritypolicy/example
no
建立一個rolebing
來授權fake-user
來使用example
策略(example是前面建立的策略的名稱)
但是請注意這裡並不是首選方式!後面的示例將介紹首選的方式
kubectl-admin create role psp:unprivileged \
--verb=use \
--resource=podsecuritypolicy \
--resource-name=example
role "psp:unprivileged" created
kubectl-admin create rolebinding fake-user:psp:unprivileged \
--role=psp:unprivileged \
--serviceaccount=psp-example:fake-user
rolebinding "fake-user:psp:unprivileged" created
kubectl-user auth can-i use podsecuritypolicy/example
yes
此時,再重新嘗試建立pod
kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pause
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
EOF
pod "pause" created
這次正如我們期待的一樣,可以正常工作.但是試圖建立特權pod仍然會被阻止(因此策略本身阻止建立特權pod)
kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: privileged
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
securityContext:
privileged: true
EOF
Error from server (Forbidden): error when creating "STDIN": pods "privileged" is forbidden: unable to validate against any pod security policy: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
再執行一個其它pod
我們再嘗試建立一個pod,這次有一點不同
ubectl-user run pause --image=k8s.gcr.io/pause
deployment "pause" created
kubectl-user get pods
No resources found.
kubectl-user get events | head -n 2
LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON SOURCE MESSAGE
1m 2m 15 pause-7774d79b5 ReplicaSet Warning FailedCreate replicaset-controller Error creating: pods "pause-7774d79b5-" is forbidden: no providers available to validate pod request
從以上可以看到deployment已經成功建立(kubectl run 實際上會建立一個deployment).但是使用kubectl get pod
命令卻沒有發現pod被建立.這是為什麼?問題的答案隱藏在replicaset控制器裡.Fake-user
成功建立的deployment(deployment又成功建立replicaset),但是當replicaset嘗試建立pod的時候,它並沒有被授權使用example
定義的策略.
為了解決這個問題,需要把psp:unprivileged
角色(前面建立的)繫結到pod的serviceaccount上(前面我們是繫結在了fake-user上).這裡serviceaccount是default
(因為我們沒有指定其它使用者)
看到這裡如果你仍然覺得難以理解,可以回頭再看看,還是無法理解的話則需要補充關於角色,使用者和RBAC相關的知識.
kubectl-admin create rolebinding default:psp:unprivileged \
--role=psp:unprivileged \
--serviceaccount=psp-example:default
rolebinding "default:psp:unprivileged" created
這時候等待若干分鐘,replicaset的控制器最終會成功建立pod
kubectl-user get pods --watch
NAME READY STATUS RESTARTS AGE
pause-7774d79b5-qrgcb 0/1 Pending 0 1s
pause-7774d79b5-qrgcb 0/1 Pending 0 1s
pause-7774d79b5-qrgcb 0/1 ContainerCreating 0 1s
pause-7774d79b5-qrgcb 1/1 Running 0 2s
清理工作
刪除名稱空間以刪除絕大部分示例中用到的資源
kubectl-admin delete ns psp-example
namespace "psp-example" deleted
注意現在剛剛建立的pod安全策略已經沒有了名稱空間,並且需要單獨被清除
kubectl-admin delete psp example
podsecuritypolicy "example" deleted
策略示例
以下是一個最小限制的策略,和不使用pod安生策略admission controller效果一樣
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'
以下的一個示例有限制性策略,需要使用者是一個非特權使用者,阻止pod的許可權提升
之所以要求是非特權使用者,因為特權使用者將會繞過限制
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default'
apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
spec:
privileged: false
# Required to prevent escalations to root.
allowPrivilegeEscalation: false
# This is redundant with non-root + disallow privilege escalation,
# but we can provide it for defense in depth.
requiredDropCapabilities:
- ALL
# Allow core volume types.
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
# Assume that persistentVolumes set up by the cluster admin are safe to use.
- 'persistentVolumeClaim'
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
策略參考
特權的
它決定了pod中的所有容器是否被允許以特權方式執行.預設情況下容器不允許訪問主機的裝置,但是特權容器卻被允許訪問.這將允許容器有幾乎和它所在的程式一樣的訪問主機的權利.這將非常有用當容器想要使用主機的功能,比如訪問網路的裝置.
Host名稱空間
HostPID
- 控制容器是否可以共享主機的程式id名稱空間
HostIPC
- 控制容器是否可以共享主機的IPC名稱空間
HostNetwork
- 控制容器是否可以使用所在節點的網路名稱空間.這將允許pod訪問迴環裝置,監聽localhost,並且可以窺探同一節點上其它pod的網路活動狀況
AllowedHostPaths - 控制允許訪問的宿主機路徑
儲存卷和檔案系統
Volumes
- 提供了一系列的儲存卷型別白名單.這些允許的值和建立儲存卷時定義的資源型別相對應.想要獲取所有儲存卷型別,可以檢視儲存卷型別列表.此外,*
可以被用來允許所有的儲存卷型別
以下是推薦的最小化的允許儲存卷型別的安全策略配置
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
- projected
AllowedHostPaths
- 它定義了一個hostPath
型別的儲存卷可用的宿主機路徑的白名單.空叢集意味著對宿主機的path無使用限制.它被定義為一個包含了一系列物件的單個pathPrefix
欄位,允許hostpath型別的儲存卷掛載以pathPrefix
欄位開頭的宿主機路徑.readonly
欄位意味著必須以readonly
方式掛載(即不能寫入,只能讀)
allowedHostPaths:
# This allows "/foo", "/foo/", "/foo/bar" etc., but
# disallows "/fool", "/etc/foo" etc.
# "/foo/../" is never valid.
- pathPrefix: "/foo"
readOnly: true # only allow read-only mounts
警告,一個可以無限制訪問宿主機檔案系統的容器可以有很多方式提升許可權,包括讀取其它容器內的資料,濫用系統服務的金鑰,比如kubecctl
可寫的hostpath目錄儲存卷允許容器寫入到宿主機檔案系統,並且可以遍歷
pathPrefix
以外的檔案系統,readOnly: true
在kubernetes 1.11+版本以後才能使用,並且在allowedHostPaths
必須使用以有效限制訪問特定的pathPrefix
ReadOnlyRootFilesystem
- 限制容器必須以只讀的root檔案系統執行(沒有可寫層)
特權提升
這個選項控制著容器的allowPrivilegeEscalation
選項.這個布林值直接控制著no_new_privs
是否設定到容器執行的程式.它將阻止setuid
來改變user ID,並且阻止檔案有其它的能力(比如禁止使用ping工具).這個行為需要啟用MustRunAsNonRoot
AllowPrivilegeEscalation
- 它決定著容器的安全上下文是否可以設定allowPrivilegeEscalation=true
,為true是預設值.設定為false將使得容器所有的子程式沒有比父程式更高的特權
DefaultAllowPrivilegeEscalation
,為allowPrivilegeEscalation
設定預設值,從上面可以看到,預設的值為true.如果這個行為不是我們期待的,這個欄位可以用於把它設定為不允許,但是仍然pod顯式請求allowPrivilegeEscalation