kubernetes⾼级之pod安全策略
个人基本资料什么是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 runAsUr, runAsGroup, supplementalGroups
禁⽌提升到root权限allowPrivilegeEscalation, defaultAllowPrivilegeEscalation
Linux能⼒defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities
SELinux上下⽂Linux
允许容器加载的proc类型allowedProcMountTypes
The AppArmor profile ud by containers annotations
The ccomp profile ud by containers annotations
The sysctl profile ud by containers annotations
启⽤pod安全策略
pod安全策略作为可选的(但强烈建议的)admission controller的实现.pod安全策略通过启⽤admission controller来实现,但是仅仅启⽤⽽没有对策略授权则会导致整个集群⽆法创建pod!
由于pod安全策略api(policy/v1beta1/podcuritypolicy)独⽴于admission controller之外启⽤,对于已经存在的集群建议在启⽤admission controller之前添加并授权策略.
授权策略
当⼀个pod安全策略资源被创建(前⾯说过,psp(PodSecurityPolicy )pod安全策略是⼀种kubernetes资源),它什么都不会做.为了使⽤它,请求操作的⽤户或者⽬标pod的rviceaccount必须通过策略的u动词来授权.
绝⼤部分kubernetes pod并不是直接由⽤户直接创建的.相反,典型使⽤场景是它们通过Deployment或者ReplicaSet间接被创建,或者通过控制器管理器的其它模板控制器来创建.对控制器进⾏策略授权也将对它所创建的所有pod进⾏策略授权.因此⾸选的授权⽅法是对pod的rviceaccount进⾏策略授权(后⾯有⽰例).
通过RBAC授权
RBAC是kubernetes标准的授权模式,并且很容易⽤于授权安全策略使⽤.
⾸先,⼀个⾓⾊(role)或者集群⾓⾊(clusterRole)需要被授权使⽤(u动词)它想要的策略.对⾓⾊的授权类似下⾯
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: <role name>
rules:
- apiGroups: ['policy']
resources: ['podcuritypolicies']
verbs: ['u']
resourceNames:
- ⼀系列要进⾏授权的资源名称
然后把集群⾓⾊(或⾓⾊)与授权的⽤户绑定
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: 绑定名称
roleRef:
kind: ClusterRole
name: ⾓⾊名称
apiGroup: rbac.authorization.k8s.io
subjects:
# Authorize specific rvice accounts:
- kind: ServiceAccount
name: 授权的rviceaccount名称
namespace: <authorized pod namespace>
# Authorize specific urs (not recommended):
- kind: Ur
apiGroup: rbac.authorization.k8s.io
name: 授权的⽤户名
如果⼀个⾓⾊绑定(不是集群⾓⾊绑定)被使⽤,它仅对和它处于同⼀名称空间下的pod才能进⾏有效策略授权,这样同样适⽤于⽤户和⽤户组
# Authorize all rvice accounts in a namespace:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:rviceaccounts
# Or equivalently, all authenticated urs in a namespace:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:authenticated
故障排除
控制器管理器必须运⾏在安全的api端⼝上,并且不能有超级权限.不然请求就会绕过认证和授权模块,将导致所有的策略均被允许,并且⽤户可以创建特权pod
策略顺序
除了限制pod的创建和更新,pod安全策略还⽤于提供它所控制的诸多字段的默认值.当有多个策略时,pod安全策略根据以下因素来选择策略
任何成功通过验证没有警告的策略将被使⽤
如果是请求创建pod,则按通过验证的策略按字母表顺序被选⽤
否则,如果是⼀个更新请求,将会返回错误.因为在更新操作过程中不允许pod变化
⽰例
以下⽰例假定你运⾏的集群开启了pod安全策略admission controller并且你有集群管理员权限
初始设置
我们为⽰例创建⼀个名称空间和⼀个rviceaccount.我们使⽤这个rviceaccount来模拟⼀个⾮管理员⽤户
kubectl create namespace psp-example
kubectl create rviceaccount -n psp-example fake-ur
kubectl create rolebinding -n psp-example fake-editor --clusterrole=edit --rviceaccount=psp-example:fake-ur
为了⽅便辨认我们使⽤的账户,我们创建两个别名
alias kubectl-admin='kubectl -n psp-example'
alias kubectl-ur='kubectl --as=system:rviceaccount:psp-example:fake-ur -n psp-example'
创建⼀个策略和⼀个pod
以下定义⽂件定义了⼀个简单pod安全策略(PodSecurityPolicy),这个策略仅仅阻⽌创建特权pod
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: example
spec:
privileged: fal # Don't allow privileged pods!
# The rest fills in some required fields.
Linux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUr:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
我们使⽤kubectl命令来应⽤以上⽂件.
现在,做为⼀个⾮特权⽤户,我们创建⼀个简单pod
kubectl-ur create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pau
spec:
containers:
- name: pau
image: io/pau
EOF
Error from rver (Forbidden): error when creating "STDIN": pods "pau" is forbidden: unable to validate against any pod curity policy: []
发⽣了什么?尽管pod安全策略已创建,不管是pod的rviceaccount还是fack-ur都没有权限使⽤这个策略.
kubectl-ur auth can-i u podcuritypolicy/example
no
创建⼀个rolebing来授权fake-ur来使⽤example策略(example是前⾯创建的策略的名称)
但是请注意这⾥并不是⾸选⽅式!后⾯的⽰例将介绍⾸选的⽅式
kubectl-admin create role psp:unprivileged \
--verb=u \
--resource=podcuritypolicy \
--resource-name=example
role "psp:unprivileged" created
kubectl-admin create rolebinding fake-ur:psp:unprivileged \
--role=psp:unprivileged \
--rviceaccount=psp-example:fake-ur
rolebinding "fake-ur:psp:unprivileged" created
kubectl-ur auth can-i u podcuritypolicy/example
yes
此时,再重新尝试创建pod
kubectl-ur create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: pau
spec:
containers:
- name: pau
image: io/pau
EOF
pod "pau" created
这次正如我们期待的⼀样,可以正常⼯作.但是试图创建特权pod仍然会被阻⽌(因此策略本⾝阻⽌创建特权pod)
kubectl-ur create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
name: privileged
spec:
containers:
- name: pau
image: io/pau
curityContext:
privileged: true
EOF
Error from rver (Forbidden): error when creating "STDIN": pods "privileged" is forbidden: unable to validate against any pod curity policy: [ainers[0].curityContext.privileged: Invalid value: true: Privileged containers are not allowe 再运⾏⼀个其它pod
我们再尝试创建⼀个pod,这次有⼀点不同
ubectl-ur run pau --io/pau
deployment "pau" created
kubectl-ur get pods
kubectl-ur get events | head -n 2
LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON SOURCE MESSAGE
1m 2m 15 pau-7774d79b5 ReplicaSet Warning FailedCreate replicat-controller Error creating: pods "pau-7774d79b5-" is forbidden: no providers available to validate pod request
从以上可以看到deployment已经成功创建(kubectl run 实际上会创建⼀个deployment).但是使⽤kubectl get pod命令却没有发现pod被创建.这是为什么?问题的答案隐藏在replicat
控制器⾥.Fake-ur成功创建的deployment(deployment⼜成功创建replicat),但是当replicat尝试创建pod的时候,它并没有被授权使⽤example定义的策略.
为了解决这个问题,需要把psp:unprivileged⾓⾊(前⾯创建的)绑定到pod的rviceaccount上(前⾯我们是绑定在了fake-ur上).这⾥rviceaccount是default(因为我们没有指定其它⽤
户)
看到这⾥如果你仍然觉得难以理解,可以回头再看看,还是⽆法理解的话则需要补充关于⾓⾊,⽤户和RBAC相关的知识.
kubectl-admin create rolebinding default:psp:unprivileged \
--role=psp:unprivileged \
--rviceaccount=psp-example:default
rolebinding "default:psp:unprivileged" created
这时候等待若⼲分钟,replicat的控制器最终会成功创建pod
kubectl-ur get pods --watch
NAME READY STATUS RESTARTS AGE
pau-7774d79b5-qrgcb 0/1 Pending 0 1s
pau-7774d79b5-qrgcb 0/1 Pending 0 1s
pau-7774d79b5-qrgcb 0/1 ContainerCreating 0 1s
pau-7774d79b5-qrgcb 1/1 Running 0 2s
清理⼯作
删除名称空间以删除绝⼤部分⽰例中⽤到的资源
kubectl-admin delete ns psp-example
黄花鱼做法namespace "psp-example" deleted
注意现在刚刚创建的pod安全策略已经没有了名称空间,并且需要单独被清除
kubectl-admin delete psp example
podcuritypolicy "example" deleted
策略⽰例
以下是⼀个最⼩限制的策略,和不使⽤pod安⽣策略admission controller效果⼀样
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: privileged
annotations:
ccomp.curity.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
privileged: true
闭经吃什么药
allowPrivilegeEscalation: true
allowedCapabilities:
- '*'
volumes:
- '*'
hostNetwork: true
hostPorts:
- min: 0
max: 65535
hostIPC: true
hostPID: true
runAsUr:
rule: 'RunAsAny'
Linux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
儿童白斑
以下的⼀个⽰例有限制性策略,需要⽤户是⼀个⾮特权⽤户,阻⽌pod的权限提升
之所以要求是⾮特权⽤户,因为特权⽤户将会绕过限制
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
annotations:
ccomp.curity.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
我见犹怜
apparmor.curity.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
ccomp.curity.alpha.kubernetes.io/defaultProfileName: 'docker/default'
apparmor.curity.beta.kubernetes.io/defaultProfileName: 'runtime/default'
spec:
privileged: fal
# Required to prevent escalations to root.
allowPrivilegeEscalation: fal
# This is redundant with non-root + disallow privilege escalation,食道癌的早期症状
# but we can provide it for defen in depth.
requiredDropCapabilities:
- ALL
# Allow core volume types.
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'cret'
- 'downwardAPI'
# Assume that persistentVolumes t up by the cluster admin are safe to u.
- 'persistentVolumeClaim'
hostNetwork: fal
hostIPC: fal
hostPID: fal
runAsUr:
# Require the container to run without root privileges.
rule: 'MustRunAsNonRoot'
Linux:
# 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:
ranges:
# Forbid adding the root group.
- min: 1
max: 65535
readOnlyRootFilesystem: fal
策略参考
特权的
它决定了pod中的所有容器是否被允许以特权⽅式运⾏.默认情况下容器不允许访问主机的设备,但是特权容器却被允许访问.这将允许容器有⼏乎和它所在的进程⼀样的访问主机的权利.这将⾮常有⽤当容器想要使⽤主机的功能,⽐如访问⽹络的设备.
Host名称空间
HostPID - 控制容器是否可以共享主机的进程id名称空间
HostIPC - 控制容器是否可以共享主机的IPC名称空间情绪化的人
HostNetwork - 控制容器是否可以使⽤所在节点的⽹络名称空间.这将允许pod访问回环设备,监听localhost,并且可以窥探同⼀节点上其它pod的⽹络活动状况AllowedHostPaths - 控制允许访问的宿主机路径
存储卷和⽂件系统
Volumes - 提供了⼀系列的存储卷类型⽩名单.这些允许的值和创建存储卷时定义的资源类型相对应.想要获取所有存储卷类型,可以查看.此外,*可以被⽤来允许所有的存储卷类型
以下是推荐的最⼩化的允许存储卷类型的安全策略配置
configMap
downwardAPI
emptyDir
persistentVolumeClaim
cret
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是否设置到容器运⾏的进程.它将阻⽌tuid来改变ur ID,并且阻⽌⽂件有其它的能⼒(⽐如禁⽌使⽤ping⼯具).这个⾏为需要启⽤MustRunAsNonRoot
AllowPrivilegeEscalation- 它决定着容器的安全上下⽂是否可以设置allowPrivilegeEscalation=true,为true是默认值.设置为fal将使得容器所有的⼦进程没有⽐⽗进程更⾼的特权DefaultAllowPrivilegeEscalation,为allowPrivilegeEscalation设置默认值,从上⾯可以看到,默认的值为true.如果这个⾏为不是我们期待的,这个字段可以⽤于把它设置为不允许,但是仍然pod显式请求allowPrivilegeEscalation