天天看點

kubernetes中其他控制器之PodSecurityPolicy

PodSecurityPolicy是叢集級别的Pod安全政策,自動為叢集中的Pod和Volume設定Security Context。

Admission Controller(準入控制器)攔截對 kube-apiserver 的請求,攔截發生在請求的對象被持久化之前,但是在請求被驗證和授權之後。這樣我們就可以檢視請求對象的來源,并驗證需要的内容是否正确。通過将它們添加到 kube-apiserver 的--enable-admission-plugins參數中來啟用準入控制器。是以如果我們要使用PSP,我們就需要在kube-apiserver中添加起參數,如下:

--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodSecurityPolicy           

複制

其他插件是kubernetes官方推薦的插件。

然後重新開機kube-apiserver:

# systemctl daemon-reload
# systemctl restart kube-apiserver           

複制

這時候就已經啟動了PSP控制器,如果我們現在建立Pod試試:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent           

複制

然後我們kubectl get pod的時候發現并沒有pod。我們再檢視deploy的狀态,如下:

# kubectl get deployments.
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
nginx                  0/1     0            0           117s
# kubectl get replicasets.
NAME                              DESIRED   CURRENT   READY   AGE
nginx-55fc968d9                   1         0         0       2m17s           

複制

我們看到replicaset和deploy都建立成功了,但是replicaset并沒有建立pod,這是因為我們叢集現在缺少安全政策,是以建立新的Pod不會成功,這時我們就需要使用ServiceAccount。

正常情況下,我們并不會直接建立Pod,都是通過其他控制器比如Deployment、StatefulSet等來建立Pod。我們現在要使用PSP,需要配置kube-controller-manager來為其包含的每個控制器單獨使用ServiceAccount,我們可以通過以下參數來添加,如下:

--use-service-account-credentials=true           

複制

然後重新開機controller-manager:

# systemctl daemon-reload
# systemctl restart kube-controller-manager           

複制

然後kubenetes就會自動生成如下一些SA,這些SA就指定了哪個控制器可以解析哪些政策:

# kubectl get serviceaccount -n kube-system | egrep -o '[A-Za-z0-9-]+-controller'
attachdetach-controller
certificate-controller
clusterrole-aggregation-controller
cronjob-controller
daemon-set-controller
deployment-controller
disruption-controller
endpoint-controller
expand-controller
job-controller
namespace-controller
node-controller
pv-protection-controller
pvc-protection-controller
replicaset-controller
replication-controller
resourcequota-controller
service-account-controller
service-controller
statefulset-controller
traefik-ingress-controller
ttl-controller           

複制

PSP提供一種聲明式的方式,用于表達運作的使用者和ServiceAccount在我們叢集中建立的内容。其主要的政策有:

控制項 說明
privileged 運作特權容器
defaultAddCapabilities 可添加到容器的Capabilities
requiredDropCapabilities 會從容器中删除的Capabilities
volumes 控制容器可以使用哪些volume
hostNetwork host網絡
hostPorts 允許的host端口清單
hostPID 使用host PID namespace
hostIPC 使用host IPC namespace
seLinux SELinux Context
runAsUser user ID
supplementalGroups 允許的補充使用者組
fsGroup volume FSGroup
readOnlyRootFilesystem 隻讀根檔案系統

在上面的示例中,我們需要兩個政策:

1、提供限制通路的預設政策,保證使用特權設定無法建立Pod;

2、提升許可政策,允許将特權設定用于某些Pod,比如允許在特定命名空間下建立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 created
# kubectl get psp
NAME          PRIV    CAPS   SELINUX    RUNASUSER   FSGROUP    SUPGROUP   READONLYROOTFS   VOLUMES
restrictive   false   *      RunAsAny   RunAsAny    RunAsAny   RunAsAny   false            configMap,downwardAPI,emptyDir,persistentVolumeClaim,secret,projected           

複制

其次,建立一個提升政策,用于那些需要提升權限的Pod,比如kube-proxy,它就需要hostNetwork權限:

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 created
# 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           

複制

但是僅僅部署了這兩個政策是不夠的,我們RBAC進行授權,不然我們的Pod還是不能建立成功。RBAC确定一個ServiceAccount可以使用的政策,如果使用ClusterRoleBinding可以為ServiceAccount提供限制性政策(restrictive)的通路,如果使用RoleBinding可以為SeriveAccount提供虛空政策的通路。

首先建立允許使用restrictive政策的ClusterRole,然後再建立一個ClusterRoleBinding将所有控制器的ServiceAccount進行綁定:

psp-restrictive-rbac.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: psp-restrictive
rules:
- apiGroups:
  - extensions
  resources:
  - podsecruritypolicies
  resourceNames:
  - restrictive
  verbs:
  - use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
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           

複制

然後我們可以看到外面剛開始建立的Pod現在可以建立了:

# kubectl get pod
NAME                                    READY   STATUS    RESTARTS   AGE
nginx-55fc968d9-qn2vr                   1/1     Running   0          17m           

複制

但是如果我們現在給這個Deployment清單加一個hostNetwork=true這個特權,觀察Pod是否能夠創:

nginx-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
      hostNetwork: true           

複制

然後我們再次建立這個Deployment:

# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx created
# kubectl get pod
NAME                                    READY   STATUS    RESTARTS   AGE
# kubectl get deployments.
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
nginx                  0/1     0            0           12s
# kubectl get replicasets.
NAME                              DESIRED   CURRENT   READY   AGE
nginx-5cd65fd4c6                  1         0         0       18s           

複制

我們可以看到Pod并未被建立,我們describe一個replicaset,發現如下日志:

# kubectl describe rs nginx-5cd65fd4c6
......
Events:
  Type     Reason        Age                   From                   Message
  ----     ------        ----                  ----                   -------
  Warning  FailedCreate  41s (x16 over 3m24s)  replicaset-controller  Error creating: pods "nginx-5cd65fd4c6-" 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不允許被使用。

但是在某些情況下,我們需要在某個命名空間下使用特權,這時候我們就可以建立一個允許使用特權的ClusterRole,但是這裡我們為特定ServiceAccount設定RoleBinding,如下:

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           

複制

上面定義了對kube-system中的daemonset,replicaset,job擁有特權建立Pod。

然後我們建立RBAC資源清單:

# kubectl apply -f psp-permissive-rbac.yaml
clusterrole.rbac.authorization.k8s.io/psp-permissive created
rolebinding.rbac.authorization.k8s.io/psp-permissive created           

複制

現在我們定義一個測試Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
      hostNetwork: true           

複制

然後建立資源對象:

# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx created
[root@ecs-5704-0003 kubernetes]# kubectl get pod -n kube-system
NAME                                   READY   STATUS    RESTARTS   AGE
......
nginx-5cd65fd4c6-7pn9z                 1/1     Running   0          4s           

複制

然後我們可以看到Pod可以正常被建立。

另外還有一種特殊的需求,就是在某個指令空間下隻有某個應用可以使用特權,那麼針對這類應用就需要單獨建立一個SA,然後和permissive政策鏡像RoleBinding了,如下:

(1)、建立可以使用特權的SA

# kubectl create serviceaccount specialsa
serviceaccount/specialsa created           

複制

(2)、建立RoleBinding

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:

ng-deploy.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
        imagePullPolicy: IfNotPresent
      hostNetwork: true
      serviceAccount: specialsa  # 注意這裡使用的sa的權限綁定           

複制

然後建立資源對象:

# kubectl apply -f ng-deploy.yaml
deployment.apps/nginx-hostnetwork-deploy created
# kubectl get pod
NAME                                        READY   STATUS    RESTARTS   AGE
nginx-hostnetwork-deploy-7b65cf7bbd-g5wl5   1/1     Running   0          2s           

複制

然後可以發現在default命名空間下可以建立擁有特權的Pod了。