PodSecurityPolicy(简称PSP)是Kubernetes中Pod部署时重要的安全校验手段,能够有效地约束应用运行时行为安全。本文主要介绍如何创建、管理和绑定PSP策略,以及如何在容器服务ACK中使用PSP安全策略。

使用说明

本文主要介绍如何在策略管理(旧版)使用PSP安全策略,而策略管理(旧版)即将下线。关于如何在策略管理(新版)使用策略治理,请参见配置容器安全策略(新版)

背景信息

在Kubernetes Pod定义中,可以通过设定安全参数来约束应用容器运行时的行为。作为集群的管理员,则会希望在集群的安全纵深防护中具有像AppArmor,SELinux这样的策略引擎,能够在应用部署时对应用Pod进行强制的安全校验,而PSP正是这样的解决方案。
  • 强制校验:PSP是Kubernetes原生的集群维度资源模型,通过API Server的Admission准入机制,在应用部署时对Pod的安全参数进行强制校验。如果参数配置不满足指定PSP策略的定义,API Server会禁止该Pod的部署请求。
  • PSP策略的RBAC绑定:创建PSP策略,需要开启PSP对应的admission plugin,同时建立指定Pod部署凭证对PSP策略的使用权限的RBAC绑定。
  • 开启PSP特性:当前ACK新创建的托管和专有集群默认开启PSP特性,减少开启PSP带来的运维工作。
  • 权限最小化原则:为了保证集群使用上的兼容性,默认给所有合法认证用户绑定了一个ack.privileged的特权策略,而在PSP的实际应用场景中,希望策略遵循权限最小化的安全原则。例如,禁止部分用户部署特权容器,只能使用只读的根文件系统,或者只能挂载指定范围的host目录。

前提条件

  • 已创建ACK托管版或专有版集群,且集群版本至少为1.14.8-aliyun.1。具体操作,请参见创建Kubernetes托管版集群创建Kubernetes专有版集群
  • 以阿里云账号(即主账号)登录容器服务管理控制台控制台,或赋予RAM用户(即子账号)管理员权限后以RAM用户登录容器服务管理控制台。具体操作,请参见配置RAM用户RBAC权限
  • 如果您使用RAM用户进行PSP策略管理,RAM用户需要拥有以下RAM授权条件:
    • cs:DescribeClusterPSPState:获取集群PSP开启状态。
    • cs:DescribeServiceAccountBindingPSP:获取指定serviceaccount绑定的PSP策略。
    • cs:UpdateClusterPodSecurityPolicy:更新集群中的PSP策略模型实例。
    • cs:CreatePSPBinding:创建对指定serviceaccount的PSP绑定。
    • cs:UnbindingServiceAccountPSP:解绑指定serviceaccount的PSP策略模型实例。

    关于如何自定义RAM授权策略,请参见自定义RAM授权策略

创建PSP策略

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,选择安全管理 > 策略管理
  5. 策略管理页面单击创建,在选择策略模板对话框中选择目标策略模板,单击目标策略模板上的使用
    除了集群默认设置的特权策略(默认放开所有安全校验)ack.privileged之外,默认提供了两个PSP策略实例模板:
    • privileged-restricted:限制privileged,共用主机网络、端口、Namespace等特权容器的部署。
    • privileged-root-volumes-restricted:限制privileged,共用主机网络、端口、Namespace等特权容器的部署,同时限制挂载卷的类型和root权限的使用。
  6. 创建YAML页面根据不同的安全等级需求修改对应的配置项,从而创建不同应用场景下的PSP策略。关于PSP模板中的配置项详细说明,请参见Pod Security Policies
    配置项 描述
    privileged 启动特权容器。
    hostPIDhostIPC 使用主机命名空间。
    hostNetworkhostPorts 使用主机网络和端口。
    volumes 允许使用的挂载卷类型。
    allowedHostPaths 允许hostPath类型挂载卷在主机上挂载的路径,通过pathPrefix字段声明允许挂载的主机路径前缀组。
    allowedFlexVolumes 允许使用的指定FlexVolume驱动。
    fsGroup 配置Pod中挂载卷使用的辅组ID。
    readOnlyRootFilesystem 约束启动Pod使用只读的root文件系统。
    runAsUserrunAsGroupsupplementalGroups 指定Pod中容器启动的用户ID以及主组和辅组ID。
    allowPrivilegeEscalationdefaultAllowPrivilegeEscalation 约束Pod中是否允许配置allowPrivilegeEscalation=true,该配置会控制Setuid的使用,同时控制程序是否可以使用额外的特权系统调用。
    defaultAddCapabilitiesrequiredDropCapabilitiesallowedCapabilities 控制Pod中使用的Linux Capabilities。
    seLinux 控制Pod使用seLinux配置。
    allowedProcMountTypes 控制Pod允许使用的ProcMountTypes。
    annotations 配置Pod中容器使用的AppArmor或Seccomp。
    forbiddenSysctlsallowedUnsafeSysctls 控制Pod中容器使用的Sysctl配置。
  7. 单击确定
    策略实例创建成功后,您可以在策略管理页面中查看创建的PSP策略实例。
    说明
    • 策略管理页签,单击目标策略操作列的YAML,可以在弹出的窗口中修改PSP策略。
    • 策略管理页签,单击目标策略操作列的删除,删除PSP策略。

绑定PSP策略

您需要给应用部署者赋予相应的PSP策略使用权限,否则如果开启了PSP特性且应用部署者没有绑定任何的PSP策略(已经删除了默认特权绑定),部署的Pod会遇到以下错误。
Error from server (Forbidden): error when creating xxx: pods "xxx" is forbidden: unable to validate against any pod security policy: []
您可以对指定用户的服务账号或某个命名空间下的服务账号绑定PSP策略。
  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,选择安全管理 > 策略管理
  5. 选择策略绑定页签,单击新增绑定规则
  6. 新增绑定规则编辑框中设置命名空间服务账号绑定策略
  7. 单击确定

解除默认绑定

为了使用已经创建的PSP策略和绑定的约束能力,您需要删除默认的特权绑定。
  • 为了确保系统组件和已部署应用的正常运行,在解除默认特权绑定的同时,后台会自动给kube-system命名空间下的所有ServiceAccount对象和系统默认的system:nodes用户组绑定特权策略的使用权限,以确保系统组件的正常工作。
  • 对于已经存在的命名空间,如果在命名空间下您没有通过策略管理控制台创建任何的PSP策略绑定,后台会给该命名空间下的所有ServiceAccount对象绑定特权策略的使用权限,以确保该命名空间中已有应用的正常启动。
注意
  • 对于已经在控制台创建了策略绑定的命名空间。

    为了使策略绑定的约束能力生效,在解除绑定时后台不会自动创建该命名空间下的特权策略绑定,因此在解除绑定前请您务必确认PSP策略和绑定的正确性。

  • 对于新创建的命名空间。

    容器服务Kubernetes版后台不会自动为新命名空间下的serviceaccount对象创建对应的特权策略绑定,请您在新命名空间创建后及时创建对应的PSP策略和绑定对象。

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,选择安全管理 > 策略管理
  5. 策略管理使用引导区域中单击解除默认绑定
  6. 解绑对话框中单击确定

确保PSP的admission controller为开启状态

当前ACK新建的集群默认开启了PSP的Admission Controller。然而对于运行中的已有集群,为了使上述PSP操作生效,需要确保集群API Server的PSP Admission Controller为开启状态。

  1. 登录容器服务管理控制台
  2. 在控制台左侧导航栏中,单击集群
  3. 集群列表页面中,单击目标集群名称或者目标集群右侧操作列下的详情
  4. 在集群管理页左侧导航栏中,选择安全管理 > 策略管理
  5. 开启Admission Controller。
    • 如果在策略管理页面右上角显示关闭,则单击改变PSP状态,在PSP状态变更对话框中单击确定,开启Admission Controller。
    • 如果在策略管理页面右上角显示开启,则表示已开启Admission Controller。
    注意 开启PSP会重启集群API Server,如果集群中存在依赖API Server的业务服务,请选择非业务高峰期进行状态变更。

ACK默认的PSP策略

在ACK中,Kubernetes 1.16.6版本的标准专有集群和标准托管集群将默认启用PSP策略准入控制组件,并配置一个名为ack.privileged的PSP策略。这个安全策略将放行任意类型的Pod,效果等同于集群未开启PSP策略准入控制组件。

kubectl get psp ack.privileged
NAME             PRIV   CAPS   SELINUX    RUNASUSER   FSGROUP    SUPGROUP   READONLYROOTFS   VOLUMES
ack.privileged   true   *      RunAsAny   RunAsAny    RunAsAny   RunAsAny   false            *
kubectl describe psp ack.privileged
Name:  ack.privileged

Settings:
  Allow Privileged:                       true
  Allow Privilege Escalation:             true
  Default Add Capabilities:               <none>
  Required Drop Capabilities:             <none>
  Allowed Capabilities:                   *
  Allowed Volume Types:                   *
  Allow Host Network:                     true
  Allow Host Ports:                       0-65535
  Allow Host PID:                         true
  Allow Host IPC:                         true
  Read Only Root Filesystem:              false
  SELinux Context Strategy: RunAsAny
    User:                                 <none>
    Role:                                 <none>
    Type:                                 <none>
    Level:                                <none>
  Run As User Strategy: RunAsAny
    Ranges:                               <none>
  FSGroup Strategy: RunAsAny
    Ranges:                               <none>
  Supplemental Groups Strategy: RunAsAny
    Ranges:                               <none>

---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: ack.privileged
  annotations:
    kubernetes.io/description: 'privileged allows full unrestricted access to
      pod features, as if the PodSecurityPolicy controller was not enabled.'
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
  labels:
    kubernetes.io/cluster-service: "true"
    ack.alicloud.com/component: pod-security-policy
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'
  readOnlyRootFilesystem: false

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ack:podsecuritypolicy:privileged
  labels:
    kubernetes.io/cluster-service: "true"
    ack.alicloud.com/component: pod-security-policy
rules:
- apiGroups:
  - policy
  resourceNames:
  - ack.privileged
  resources:
  - podsecuritypolicies
  verbs:
  - use

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ack:podsecuritypolicy:authenticated
  annotations:
    kubernetes.io/description: 'Allow all authenticated users to create privileged pods.'
  labels:
    kubernetes.io/cluster-service: "true"
    ack.alicloud.com/component: pod-security-policy
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ack:podsecuritypolicy:privileged
subjects:
  - kind: Group
    apiGroup: rbac.authorization.k8s.io
    name: system:authenticated

删除ACK默认PSP策略对应的集群角色绑定

警告 在删除ACK默认的PSP策略对应的集群角色绑定前必须先配置好自定义的PSP策略及其相应的RBAC绑定,否则所有用户、控制器、服务账号都将无法创建或更新Pod。
在配置好自定义的PSP策略及其相应的RBAC绑定后,您可以通过删除ACK默认PSP策略ack.privileged的集群角色绑定的方式来启用您自定义的PSP策略。
注意 请不要删除或修改名为ack.privileged的PSP策略以及名为ack:podsecuritypolicy:privileged的集群角色,ACK集群的正常运行需要依赖这两个资源。
cat <<EOF | kubectl delete -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ack:podsecuritypolicy:authenticated
  annotations:
    kubernetes.io/description: 'Allow all authenticated users to create privileged pods.'
  labels:
    kubernetes.io/cluster-service: "true"
    ack.alicloud.com/component: pod-security-policy
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ack:podsecuritypolicy:privileged
subjects:
  - kind: Group
    apiGroup: rbac.authorization.k8s.io
    name: system:authenticated
EOF

配置或恢复ACK默认的PSP策略

在您启用您自定义的PSP策略后,若需要配置或恢复ACK默认的PSP策略,请执行以下操作。

cat <<EOF | kubectl apply -f -
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: ack.privileged
  annotations:
    kubernetes.io/description: 'privileged allows full unrestricted access to
      pod features, as if the PodSecurityPolicy controller was not enabled.'
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
  labels:
    kubernetes.io/cluster-service: "true"
    ack.alicloud.com/component: pod-security-policy
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'
  readOnlyRootFilesystem: false

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ack:podsecuritypolicy:privileged
  labels:
    kubernetes.io/cluster-service: "true"
    ack.alicloud.com/component: pod-security-policy
rules:
- apiGroups:
  - policy
  resourceNames:
  - ack.privileged
  resources:
  - podsecuritypolicies
  verbs:
  - use

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ack:podsecuritypolicy:authenticated
  annotations:
    kubernetes.io/description: 'Allow all authenticated users to create privileged pods.'
  labels:
    kubernetes.io/cluster-service: "true"
    ack.alicloud.com/component: pod-security-policy
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ack:podsecuritypolicy:privileged
subjects:
  - kind: Group
    apiGroup: rbac.authorization.k8s.io
    name: system:authenticated
EOF

验证PSP策略效果

下面将通过示例展示PSP策略管理的应用以及具体的安全校验应用效果。

  1. 通过Kubectl连接集群。具体操作,请参见通过kubectl工具连接集群
  2. 创建privileged-restricted和privileged-root-volumes-restricted策略模板。具体操作,请参见创建PSP策略
  3. 执行以下命令,创建2个测试命名空间,同时在命名空间下创建测试用的ServiceAccount实例并建立相应的RBAC权限绑定。
    kubectl create ns ack-all-restrictive
    kubectl create ns ack-restrictive
    
    kubectl create sa ack-dev -n ack-restrictive
    kubectl create sa ack-tester -n ack-all-restrictive
    
    kubectl -n ack-restrictive create rolebinding ack-dev-editor \
               --clusterrole=edit \
               --serviceaccount=ack-restrictive:ack-dev
    kubectl -n ack-all-restrictive create rolebinding ack-tester-editor \
               --clusterrole=edit \
               --serviceaccount=ack-all-restrictive:ack-tester          
  4. 策略绑定页签中绑定PSP策略。
    1. 单击新增绑定规则,在新增绑定规则编辑框中设置命名空间ack-restrictive,设置服务账号 ack-dev,设置绑定策略privileged-restricted,单击确定
    2. 单击新增绑定规则,在新增绑定规则编辑框中设置命名空间ack-restrictive,设置服务账号 ack-dev,设置绑定策略privileged-root-volumes-restricted,单击确定
  5. 执行以下命令,扮演使用ack-restrictive命名空间下的服务账号ack-dev和ack-all-restrictive命名空间下的服务账号ack-tester。
    alias kubectl-dev='kubectl --as=system:serviceaccount:ack-restrictive:ack-dev -n ack-restrictive'
    alias kubectl-tester='kubectl --as=system:serviceaccount:ack-all-restrictive:ack-tester -n ack-all-restrictive'
    
    kubectl-dev auth can-i use  podsecuritypolicy/privileged-restricted
    Warning: resource 'podsecuritypolicies' is not namespace scoped in group 'policy'
    yes
                            
  6. 执行以下命令,创建共享主机网络的特权容器。
    kubectl-dev apply -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: privileged
    spec:
      hostNetwork: true
      containers:
        - name: busybox
          image: busybox
          command: [ "sh", "-c", "sleep 1h" ]
    EOF
    Error from server (Forbidden): error when retrieving current configuration of:
    Resource: "/v1, Resource=pods", GroupVersionKind: "/v1, Kind=Pod"
    Name: "privileged", Namespace: "ack-restrictive"
    Object: &{map["apiVersion":"v1" "kind":"Pod" "metadata":map["annotations":map["kubectl.kubernetes.io/last-applied-configuration":""] "name":"privileged" "namespace":"ack-restrictive"] "spec":map["containers":[map["command":["sh" "-c" "sleep 1h"] "image":"busybox" "name":"busybox"]] "hostNetwork":%!q(bool=true)]]}
    from server for: "STDIN": pods "privileged" is forbidden: User "system:serviceaccount:ack-restrictive:ack-dev" cannot get resource "pods" in API group "" in the namespace "ack-restrictive"
    系统显示报错,创建特权容器失败。
  7. 执行以下命令,创建非特权容器。
    kubectl-dev apply -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: non-privileged
    spec:
      containers:
        - name: busybox
          image: busybox
          command: [ "sh", "-c", "sleep 1h" ]
    EOF
    pod/non-privileged created
    系统显示正常,创建非特权容器成功。
  8. 执行以下命令,创建一个root权限Pod和一个非root权限Pod。
    kubectl-tester apply -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: root-pod
    spec:
      containers:
        - name: busybox
          image: busybox
          command: [ "sh", "-c", "sleep 1h" ]
    EOF
    
    kubectl-tester apply -f- <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: non-root-pod
    spec:
      containers:
        - name: busybox
          image: busybox
          command: [ "sh", "-c", "sleep 1h" ]
          securityContext:
            runAsUser: 1000
            runAsGroup: 3000
    EOF
  9. 执行以下命令,查看Pod状态。
    使用root权限的容器创建失败,使用的ack-tester sa绑定的PSP策略会约束pod使用非root权限启动容器。
    kubectl-tester get pod

    预期输出:

    NAME           READY   STATUS                       RESTARTS   AGE
    non-root-pod   1/1     Running                      0          115s
    root-pod       0/1     CreateContainerConfigError   0          24s
  10. 执行以下命令,查看错误详情。
    kubectl describe pod root-pod

    预期输出:

    Warning  Failed     28s (x5 over 2m1s)   kubelet, cn-shenzhen.192.168.1.52  Error: container has runAsNonRoot and image will run as root