kubernetes高级之pod安全策略

系列目录html

什么是pod安全策略

pod安全策略是集群级别的用于控制pod安全相关选项的一种资源.PodSecurityPolicy定义了一系列pod相要进行在系统中必须知足的约束条件,以衣一些默认的约束值.它容许管理员控制如下方面内容node

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!docker

因为pod安全策略api(policy/v1beta1/podsecuritypolicy)独立于admission controller以外启用,对于已经存在的集群建议在启用admission controller以前添加并受权策略.api

受权策略

当一个pod安全策略资源被建立(前面说过,psp(PodSecurityPolicy )pod安全策略是一种kubernetes资源),它什么都不会作.为了使用它,请求操做的用户或者目标pod的serviceaccount必须经过策略的use动词来受权.安全

绝大部分kubernetes pod并非直接由用户直接建立的.相反,典型使用场景是它们经过Deployment或者ReplicaSet间接被建立,或者经过控制器管理器的其它模板控制器来建立.对控制器进行策略受权也将对它所建立的全部pod进行策略受权.所以首选的受权方法是对pod的serviceaccount进行策略受权(后面有示例).bash

经过RBAC受权

RBAC是kubernetes标准的受权模式,而且很容易用于受权安全策略使用.网络

首先,一个角色(role)或者集群角色(clusterRole)须要被受权使用(use动词)它想要的策略.对角色的受权相似下面app

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: <role name>
rules:
- apiGroups: ['policy']
  resources: ['podsecuritypolicies']
  verbs:     ['use']
  resourceNames:
  - 一系列要进行受权的资源名称

而后把集群角色(或角色)与受权的用户绑定ide

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