Kubernetes 多租戶:Pod 安全策略
預設情況下,Kubernetes 允許建立一個有特權容器的 Pod,這些容器很可能會危機系統安全,而 Pod 安全策略(PSP)則通過確保請求者有許可權按配置來建立 Pod,從而來保護叢集免受特權 Pod 的影響。
PodSecurityPolicy 是 Kubernetes API 物件,你可以在不對 Kubernetes 進行任何修改的情況下建立它們,但是,預設情況下不會強制執行我們建立的一些策略,我們需要一個准入控制器、kube-controller-manager 配置以及 RBAC 許可權配置,下面我們就來對這些配置進行一一說明。
Admission Controller
Admission Controller(准入控制器)攔截對 kube-apiserver 的請求,攔截髮生在請求的物件被持久化之前,但是在請求被驗證和授權之後。這樣我們就可以檢視請求物件的來源,並驗證需要的內容是否正確。通過將它們新增到 kube-apiserver 的 --enable-admission-plugins 引數中來啟用准入控制器。在1.10版本之前,使用現在已經棄用的 --admision-control 引數,另外需要注意准入控制器的順序很重要。
將 PodSecurityPolicy 新增到 kube-apiserver 上的 --enabled-admission-plugins 引數中,然後重啟 kube-apiserver:
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodSecurityPolicy
其他外掛來自 Kubernetes 文件中推薦的一些外掛列表。
PodSecurityPolicy 已經新增到上面的列表中了,現在 PSP 的控制器已經啟用了,但是我們叢集中現在缺少一些安全策略,那麼新的 Pod 建立就會失敗。
比如現在我們建立一個 Nginx 的 Deployment,YAML 檔案內容如下所示:(nginx.yaml)
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: default labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx
然後直接建立上面的 Deployment:
$ kubectl apply -f nginx.yaml
deployment.apps/nginx-deploy created
我們可以看到 Deployment 已經建立成功了,現在檢查下 default 名稱空間下面的 pod、replicaset、deployment:
$ kubectl get po,rs,deploy -l app=nginx
NAME READY STATUS RESTARTS AGE
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-deploy-77f7d4c6b4 1 0 0 40s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-deploy 0/1 0 0 40s
可以看到 replicaset 和 deployment 都建立成功了,但是 replicaset 控制器卻並沒有建立 Pod,這個時候就需要使用 PodSecurityPolicy 物件和 ServiceAccount 了。
策略
PodSecurityPolicy 物件提供了一種宣告式的方式,用於表達我們執行使用者和 ServiceAccount 在我們的叢集中建立的內容。我們可以檢視策略文件來了解如何設定。在我們當前示例中,我們來建立兩個策略,第一個是提供限制訪問的“預設”策略,保證使用特權設定(例如使用 hostNetwork)無法建立 Pod。第二種是一個“提升”的許可策略,允許將特權設定用於某些 Pod,例如在 kube-system 名稱空間下面建立的 Pod 有許可權。
首先,建立一個限制性的策略,作為預設策略:(psp-restrictive.yaml)
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restrictive
spec:
privileged: false
hostNetwork: false
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
hostPID: false
hostIPC: false
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
volumes:
- 'configMap'
- 'downwardAPI'
- 'emptyDir'
- 'persistentVolumeClaim'
- 'secret'
- 'projected'
allowedCapabilities:
- '*'
直接建立上面的 psp 物件:
$ kubectl apply -f psp-restrictive.yaml
podsecuritypolicy.policy/restrictive configured
雖然限制性的訪問對於大多數 Pod 建立是足夠的了,但是對於需要提升訪問許可權的 Pod 來說,就需要一些允許策略了,例如,kube-proxy 就需要啟用 hostNetwork:
$ kubectl get pods -n kube-system -l k8s-app=kube-proxy
NAME READY STATUS RESTARTS AGE
kube-proxy-4z4vf 1/1 Running 0 18d
$ kubectl get pods -n kube-system kube-proxy-4z4vf -o yaml |grep hostNetwork
hostNetwork: true
這就需要建立一個用於提升建立許可權的許可策略了:(psp-permissive.yaml)
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: permissive
spec:
privileged: true
hostNetwork: true
hostIPC: true
hostPID: true
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
hostPorts:
- min: 0
max: 65535
volumes:
- '*'
同樣直接建立上面的 psp 物件:
$ kubectl apply -f psp-permissive.yaml
podsecuritypolicy.policy/permissive configured
$ kubectl get psp
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
permissive true RunAsAny RunAsAny RunAsAny RunAsAny false *
restrictive false * RunAsAny RunAsAny RunAsAny RunAsAny false configMap,downwardAPI,emptyDir,persistentVolumeClaim,secret,projected
現在配置都已經就緒了,但是我們需要引入到 Kubernetes 授權,這樣才可以確定請求 Pod 建立的使用者或者 ServiceAccount 是否解決了限制性或許可性策略,這就需要用到 RBAC 了。
RBAC
在我們啟用 Pod 安全策略的時候,可能會對 RBAC 引起混淆,它確定了一個賬戶可以使用的策略,使用叢集範圍的 ClusterRoleBinding 可以為 ServiceAccount(例如 replicaset-controller)提供對限制性策略的訪問許可權。使用名稱空間範圍的 RoleBinding,可以啟用對許可策略的訪問,這樣可以在特定的名稱空間(如 kube-system)中進行操作。
我們首先建立允許使用 restrictive 策略的 ClusterRole。然後建立一個 ClusterRoleBinding,將 restrictive 策略和系統中所有的控制器 ServiceAccount 進行繫結:(psp-restrictive-rbac.yaml)
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: psp-restrictive
rules:
- apiGroups:
- extensions
resources:
- podsecuritypolicies
resourceNames:
- restrictive
verbs:
- use
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: psp-default
subjects:
- kind: Group
name: system:serviceaccounts
namespace: kube-system
roleRef:
kind: ClusterRole
name: psp-restrictive
apiGroup: rbac.authorization.k8s.io
直接建立上面的 RBAC 相關的資源物件:
$ kubectl apply -f psp-restrictive-rbac.yaml
clusterrole.rbac.authorization.k8s.io/psp-restrictive created
clusterrolebinding.rbac.authorization.k8s.io/psp-default created
然後現在我們再重新建立上面我們的定義的 Deployment:
$ kubectl delete -f nginx.yaml
deployment.apps "nginx-deploy" deleted
$ kubectl apply -f nginx.yaml
deployment.apps/nginx-deploy created
建立完成後同樣檢視下 default 名稱空間下面我們建立的一些資源物件:
$ kubectl get po,rs,deploy -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-deploy-77f7d4c6b4-njfdl 1/1 Running 0 13s
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-deploy-77f7d4c6b4 1 1 1 13s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-deploy 1/1 1 1 13s
我們可以看到 Pods 被成功建立了,但是,如果我們嘗試做一些策略不允許的事情,正常來說就應該被拒絕了。首先刪除上面的這個 Deployment:
$ kubectl delete -f nginx.yaml
deployment.apps "nginx-deploy" deleted
現在我們在 nginx-deploy 基礎上新增 hostNetwork: true 來使用 hostNetwork 這個特權:(nginx-hostnetwork.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hostnetwork-deploy
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
hostNetwork: true # 注意新增hostNetwork
然後直接建立上面的 Deployment 這個資源物件:
$ kubectl apply -f nginx-hostnetwork.yaml
deployment.apps/nginx-hostnetwork-deploy created
建立完成後同樣檢視 default 這個名稱空間下面的一些資源物件:
$ kubectl get po,rs,deploy -l app=nginx
NAME READY STATUS RESTARTS AGE
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-hostnetwork-deploy-74c8fbd687 1 0 0 44s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-hostnetwork-deploy 0/1 0 0 44s
現在我們發現 ReplicaSet 又沒有建立 Pod 了,可以使用kubectl describe命令去檢視這裡我們建立的 ReplicaSet 資源物件來了解更多的資訊:
$ kubectl describe rs nginx-hostnetwork-deploy-74c8fbd687
Name: nginx-hostnetwork-deploy-74c8fbd687
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreate 80s (x15 over 2m42s) replicaset-controller Error creating: pods "nginx-hostnetwork-deploy-74c8fbd687-" is forbidden: unable to validate against any pod security policy: [spec.securityContext.hostNetwork: Invalid value: true: Host network is not allowed to be used]
我們可以看到很明顯 Hostnetwork 不被允許使用,但是在某些情況下,我們的確有在某個名稱空間(比如 kube-system)下面建立使用 hostNetwork 的 Pod,這裡就需要我們建立一個允許執行的 ClusterRole,然後為特定的名稱空間建立一個 RoleBinding,將這裡的 ClusterRole 和相關的控制器 ServiceAccount 進行繫結:(psp-permissive-rbac.yaml)
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: psp-permissive
rules:
- apiGroups:
- extensions
resources:
- podsecuritypolicies
resourceNames:
- permissive
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: psp-permissive
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp-permissive
subjects:
- kind: ServiceAccount
name: daemon-set-controller
namespace: kube-system
- kind: ServiceAccount
name: replicaset-controller
namespace: kube-system
- kind: ServiceAccount
name: job-controller
namespace: kube-system
然後直接建立上面的 RBAC 相關的資源物件:
$ kubectl apply -f psp-permissive-rbac.yaml
clusterrole.rbac.authorization.k8s.io/psp-permissive created
rolebinding.rbac.authorization.k8s.io/psp-permissive created
現在,我們就可以在 kube-system 這個名稱空間下面使用 hostNetwork 來建立 Pod 了,將上面的 nginx 資源清單更改成 kube-system 名稱空間下面:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hostnetwork-deploy
namespace: kube-system
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.4
hostNetwork: true
重新建立這個 Deployment:
$ kubectl apply -f nginx-hostnetwork.yaml
deployment.apps/nginx-hostnetwork-deploy created
建立完成後同樣檢視下對應的資源物件建立情況:
$ kubectl get po,rs,deploy -n kube-system -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-hostnetwork-deploy-74c8fbd687-7x8px 1/1 Running 0 2m1s
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-hostnetwork-deploy-74c8fbd687 1 1 1 2m1s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-hostnetwork-deploy 1/1 1 1 2m1s
現在我們可以看到 Pod 在 kube-system 這個名稱空間下面建立成功了。
如果我們現在有這樣的一個需求,在某個名稱空間下面要強制執行我們建立的 restrictive(限制性)策略,但是這個名稱空間下面的某個應用需要使用 permissive(許可)策略,那麼應該怎麼辦呢?在當前模型中,我們只有叢集級別和名稱空間級別的配置。為了給某個應用提供單獨的許可策略,我們可以為應用的 ServiceAccount 提供使用 permissive 這個 ClusterRole 的能力。
比如,還是在預設的名稱空間下面建立一個名為 specialsa 的 ServiceAccount:
$ kubectl create serviceaccount specialsa
serviceaccount/specialsa created
然後建立一個 RoleBinding 將 specialsa 繫結到上面的 psp-permissive 這個 CluterRole 上:(specialsa-psp.yaml)
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: specialsa-psp-permissive
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp-permissive
subjects:
- kind: ServiceAccount
name: specialsa
namespace: default
建立上面的 RoleBinding 物件:
$ kubectl apply -f specialsa-psp.yaml
rolebinding.rbac.authorization.k8s.io/specialsa-psp-permissive created
然後為我們上面的 Deployment 新增上 serviceAccount 屬性:(nginx-hostnetwork-sa.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hostnetwork-deploy
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
hostNetwork: true
serviceAccount: specialsa # 注意這裡使用的sa的許可權繫結
然後直接建立即可:
$ kubectl apply -f nginx-hostnetwork-sa.yaml
deployment.apps/nginx-hostnetwork-deploy configured
這個時候我們檢視 default 這個名稱空間下面帶有 hostNetwork 的 Pod 也建立成功了:
$ kubectl get po,rs,deploy -l app=nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-hostnetwork-deploy-6c85dfbf95-hqt8j 1/1 Running 0 65s
NAME DESIRED CURRENT READY AGE
replicaset.extensions/nginx-hostnetwork-deploy-6c85dfbf95 1 1 1 65s
replicaset.extensions/nginx-hostnetwork-deploy-74c8fbd687 0 0 0 31m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.extensions/nginx-hostnetwork-deploy 1/1 1 1 31m
Pod 安全策略是一種通過使用 PSP 授權策略來保護 Kubernetes 叢集中的 Pod 的建立過程的方法。