系列目录
什么是 pod 安全策略
pod 安全策略是集群级别的用于控制 pod 安全相关选项的一种资源. PodSecurityPolicy 定义了一系列 pod 相要进行在系统中必须满足的约束条件, 以衣一些默认的约束值. 它允许管理员控制以下方面内容
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!
由于 pod 安全策略 API(policy/v1beta1/podsecuritypolicy)独立于 admission controller 之外启用, 对于已经存在的集群建议在启用 admission controller 之前添加并授权策略.
授权策略
当一个 pod 安全策略资源被创建(前面说过, psp(PodSecurityPolicy )pod 安全策略是一种 kubernetes 资源), 它什么都不会做. 为了使用它, 请求操作的用户或者目标 pod 的 serviceaccount 必须通过策略的 use 动词来授权.
绝大部分 kubernetes pod 并不是直接由用户直接创建的. 相反, 典型使用场景是它们通过 Deployment 或者 ReplicaSet 间接被创建, 或者通过控制器管理器的其它模板控制器来创建. 对控制器进行策略授权也将对它所创建的所有 pod 进行策略授权. 因此首选的授权方法是对 pod 的 serviceaccount 进行策略授权(后面有示例).
通过 RBAC 授权
RBAC 是 kubernetes 标准的授权模式, 并且很容易用于授权安全策略使用.
首先, 一个角色 (role) 或者集群角色 (clusterRole) 需要被授权使用 (use 动词) 它想要的策略. 对角色的授权类似下面
- kind: ClusterRole
- apiVersion: rbac.authorization.k8s.io/v1
- metadata:
- name: <role name>
- rules:
- - apiGroups: ['policy']
- resources: ['podsecuritypolicies']
- verbs: ['use']
- resourceNames:
- 一系列要进行授权的资源名称
然后把集群角色 (或角色) 与授权的用户绑定
- 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
来源: https://www.cnblogs.com/tylerzhou/p/11078128.html