PodSecurityPolicy是集群级别的Pod安全策略,自动为集群中的Pod和Volume设置Security Context。

    Admission Controller(准入控制器)拦截对 kube-apiserver 的请求,拦截发生在请求的对象被持久化之前,但是在请求被验证和授权之后。这样我们就可以查看请求对象的来源,并验证需要的内容是否正确。通过将它们添加到 kube-apiserver 的—enable-admission-plugins参数中来启用准入控制器。所以如果我们要使用PSP,我们就需要在kube-apiserver中添加起参数,如下:

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

    其他插件是kubernetes官方推荐的插件

    然后重启kube-apiserver:

    1. # systemctl daemon-reload
    2. # systemctl restart kube-apiserver

    这时候就已经启动了PSP控制器,如果我们现在创建Pod试试:

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: nginx
    5. spec:
    6. replicas: 1
    7. selector:
    8. matchLabels:
    9. app: nginx
    10. template:
    11. metadata:
    12. labels:
    13. app: nginx
    14. spec:
    15. containers:
    16. - name: nginx
    17. image: nginx
    18. imagePullPolicy: IfNotPresent

    然后我们kubectl get pod的时候发现并没有pod。我们再查看deploy的状态,如下:

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

    我们看到replicaset和deploy都创建成功了,但是replicaset并没有创建pod,这是因为我们集群现在缺少安全策略,所以创建新的Pod不会成功,这时我们就需要使用ServiceAccount。

    正常情况下,我们并不会直接创建Pod,都是通过其他控制器比如Deployment、StatefulSet等来创建Pod。我们现在要使用PSP,需要配置kube-controller-manager来为其包含的每个控制器单独使用ServiceAccount,我们可以通过以下参数来添加,如下:

    1. --use-service-account-credentials=true

    然后重启controller-manager:

    1. # systemctl daemon-reload
    2. # systemctl restart kube-controller-manager

    然后kubenetes就会自动生成如下一些SA,这些SA就指定了哪个控制器可以解析哪些策略:

    1. # kubectl get serviceaccount -n kube-system | egrep -o '[A-Za-z0-9-]+-controller'
    2. attachdetach-controller
    3. certificate-controller
    4. clusterrole-aggregation-controller
    5. cronjob-controller
    6. daemon-set-controller
    7. deployment-controller
    8. disruption-controller
    9. endpoint-controller
    10. expand-controller
    11. job-controller
    12. namespace-controller
    13. node-controller
    14. pv-protection-controller
    15. pvc-protection-controller
    16. replicaset-controller
    17. replication-controller
    18. resourcequota-controller
    19. service-account-controller
    20. service-controller
    21. statefulset-controller
    22. traefik-ingress-controller
    23. 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

    1. apiVersion: policy/v1beta1
    2. kind: PodSecurityPolicy
    3. metadata:
    4. name: restrictive
    5. spec:
    6. privileged: false
    7. hostNetwork: false
    8. allowPrivilegeEscalation: false
    9. defaultAllowPrivilegeEscalation: false
    10. hostPID: false
    11. hostIPC: false
    12. runAsUser:
    13. rule: RunAsAny
    14. fsGroup:
    15. rule: RunAsAny
    16. seLinux:
    17. rule: RunAsAny
    18. supplementalGroups:
    19. rule: RunAsAny
    20. volumes:
    21. - 'configMap'
    22. - 'downwardAPI'
    23. - 'emptyDir'
    24. - 'persistentVolumeClaim'
    25. - 'secret'
    26. - 'projected'
    27. allowedCapabilities:
    28. - '*'

    然后直接创建这个PSP对象:

    1. # kubectl apply -f psp-restrictive.yaml
    2. podsecuritypolicy.policy/restrictive created
    3. # kubectl get psp
    4. NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
    5. restrictive false * RunAsAny RunAsAny RunAsAny RunAsAny false configMap,downwardAPI,emptyDir,persistentVolumeClaim,secret,projected

    其次,创建一个提升策略,用于那些需要提升权限的Pod,比如kube-proxy,它就需要hostNetwork权限:
    psp-permissive.yaml

    1. apiVersion: policy/v1beta1
    2. kind: PodSecurityPolicy
    3. metadata:
    4. name: permissive
    5. spec:
    6. privileged: true
    7. hostNetwork: true
    8. hostIPC: true
    9. hostPID: true
    10. seLinux:
    11. rule: RunAsAny
    12. supplementalGroups:
    13. rule: RunAsAny
    14. runAsUser:
    15. rule: RunAsAny
    16. fsGroup:
    17. rule: RunAsAny
    18. hostPorts:
    19. - min: 0
    20. max: 65535
    21. volumes:
    22. - '*'

    然后创建这个PSP对象:

    1. # kubectl apply -f psp-permissive.yaml
    2. podsecuritypolicy.policy/permissive created
    3. # kubectl get psp
    4. NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
    5. permissive true RunAsAny RunAsAny RunAsAny RunAsAny false *
    6. 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

    1. apiVersion: rbac.authorization.k8s.io/v1
    2. kind: ClusterRole
    3. metadata:
    4. name: psp-restrictive
    5. rules:
    6. - apiGroups:
    7. - extensions
    8. resources:
    9. - podsecruritypolicies
    10. resourceNames:
    11. - restrictive
    12. verbs:
    13. - use
    14. ---
    15. apiVersion: rbac.authorization.k8s.io/v1
    16. kind: ClusterRoleBinding
    17. metadata:
    18. name: psp-default
    19. subjects:
    20. - kind: Group
    21. name: system:serviceaccounts
    22. namespace: kube-system
    23. roleRef:
    24. kind: ClusterRole
    25. name: psp-restrictive
    26. apiGroup: rbac.authorization.k8s.io

    然后创建RBAC资源对象:

    1. # kubectl apply -f psp-restrictive-rbac.yaml
    2. clusterrole.rbac.authorization.k8s.io/psp-restrictive created
    3. clusterrolebinding.rbac.authorization.k8s.io/psp-default created

    然后我们可以看到外面刚开始创建的Pod现在可以创建了:

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

    但是如果我们现在给这个Deployment清单加一个hostNetwork=true这个特权,观察Pod是否能够创:
    nginx-deploy.yaml

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: nginx
    5. spec:
    6. replicas: 1
    7. selector:
    8. matchLabels:
    9. app: nginx
    10. template:
    11. metadata:
    12. labels:
    13. app: nginx
    14. spec:
    15. containers:
    16. - name: nginx
    17. image: nginx
    18. imagePullPolicy: IfNotPresent
    19. hostNetwork: true

    然后我们再次创建这个Deployment:

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

    我们可以看到Pod并未被创建,我们describe一个replicaset,发现如下日志:

    1. # kubectl describe rs nginx-5cd65fd4c6
    2. ......
    3. Events:
    4. Type Reason Age From Message
    5. ---- ------ ---- ---- -------
    6. 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

    1. kind: ClusterRole
    2. apiVersion: rbac.authorization.k8s.io/v1
    3. metadata:
    4. name: psp-permissive
    5. rules:
    6. - apiGroups:
    7. - extensions
    8. resources:
    9. - podsecuritypolicies
    10. resourceNames:
    11. - permissive
    12. verbs:
    13. - use
    14. ---
    15. apiVersion: rbac.authorization.k8s.io/v1beta1
    16. kind: RoleBinding
    17. metadata:
    18. name: psp-permissive
    19. namespace: kube-system
    20. roleRef:
    21. apiGroup: rbac.authorization.k8s.io
    22. kind: ClusterRole
    23. name: psp-permissive
    24. subjects:
    25. - kind: ServiceAccount
    26. name: daemon-set-controller
    27. namespace: kube-system
    28. - kind: ServiceAccount
    29. name: replicaset-controller
    30. namespace: kube-system
    31. - kind: ServiceAccount
    32. name: job-controller
    33. namespace: kube-system

    上面定义了对kube-system中的daemonset,replicaset,job拥有特权创建Pod。
    然后我们创建RBAC资源清单:

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

    现在我们定义一个测试Deployment:

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: nginx
    5. namespace: kube-system
    6. spec:
    7. replicas: 1
    8. selector:
    9. matchLabels:
    10. app: nginx
    11. template:
    12. metadata:
    13. labels:
    14. app: nginx
    15. spec:
    16. containers:
    17. - name: nginx
    18. image: nginx
    19. imagePullPolicy: IfNotPresent
    20. hostNetwork: true

    然后创建资源对象:

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

    然后我们可以看到Pod可以正常被创建。

    另外还有一种特殊的需求,就是在某个命令空间下只有某个应用可以使用特权,那么针对这类应用就需要单独创建一个SA,然后和permissive策略镜像RoleBinding了,如下:
    (1)、创建可以使用特权的SA

    1. # kubectl create serviceaccount specialsa
    2. serviceaccount/specialsa created

    (2)、创建RoleBinding
    specialsa-psp.yaml

    1. apiVersion: rbac.authorization.k8s.io/v1beta1
    2. kind: RoleBinding
    3. metadata:
    4. name: specialsa-psp-permissive
    5. namespace: default
    6. roleRef:
    7. apiGroup: rbac.authorization.k8s.io
    8. kind: ClusterRole
    9. name: psp-permissive
    10. subjects:
    11. - kind: ServiceAccount
    12. name: specialsa
    13. namespace: default

    然后创建上面的RoleBinding对象:

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

    然后创建一个测试的Deployment:
    ng-deploy.yaml

    1. apiVersion: apps/v1
    2. kind: Deployment
    3. metadata:
    4. name: nginx-hostnetwork-deploy
    5. namespace: default
    6. labels:
    7. app: nginx
    8. spec:
    9. replicas: 1
    10. selector:
    11. matchLabels:
    12. app: nginx
    13. template:
    14. metadata:
    15. labels:
    16. app: nginx
    17. spec:
    18. containers:
    19. - name: nginx
    20. image: nginx
    21. imagePullPolicy: IfNotPresent
    22. hostNetwork: true
    23. serviceAccount: specialsa # 注意这里使用的sa的权限绑定

    然后创建资源对象:

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

    然后可以发现在default命名空间下可以创建拥有特权的Pod了。