一、实验环境
底层系统为ubuntu18.04,然后在每个node上安装k8s,并构建集群。Master node的IP地址为192.168.26.71/24,两个Worker node的IP地址为192.168.26.72/24、192.168.26.73/24。
二、PSP基础
1.PSP概述
Pod 安全策略(Pod Security Policy)是集群级别的资源(默认不属于任何Namespace,但也可以在创建PSP规则时指定绑定到具体的Namespace),它能够控制Pod的各个安全方面。包括如下的内容:
只有当我们部署pod时满足了上述的要求后,才能够正常部署。PSP由设置和策略组成,它们能够控制 Pod 访问的安全特征。这些设置分为如下三类:
- 基于布尔值控制 :这种类型的字段默认为最严格限制的值;
- 基于被允许的值集合控制 :这种类型的字段会与这组值进行对比,以确认值被允许;
- 基于策略控制 :设置项通过一种策略提供的机制来生成该值,这种机制能够确保指定的值落在被允许的这组值中。
此外,我们需要注意的是,由于部署的困难,PodSecurityPolicy 在 Kubernetes v1.21 版本中被弃用,将在 v1.25 中删除,后续由简化的PodSecurity代替PSP,描述地址如下:https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/.。 目前,如果我们想要使用PSP,需要在集群中的准入控制器(admission controller)处添加才行。
2.PSP工作流程
无论是我们通过user account还是service account(例如Deployment控制器会使用)创建pod,请求都会通过准入控制器。如果我们在准入控制器处开启了PSP,并没有设置任何的规则,那么K8s账号(包括admin)发送的请求都会被拒绝。而当我们创建了PSP规则,那么K8s账号发送的请求必须在满足PSP的规则后才能够对Pod进行操作,否则请求依旧会被拒绝。
需要注意的是,如果我们没有给自己创建的User Account或者Service Account赋予访问对应PSP的权限,用户发送的操作请求是无法到达PSP的,那么请求将被拒绝;由于默认的K8s admin的账号拥有着所有的权限,所以可以访问集群中所有的PSP。
所以在PSP开启的情况,如果我们想要部署一个pod,则需要满足如下条件:
- 使用的K8s用户被赋予了create pod的权限;
- 使用的K8s用户需要有访问PSP的权限;
- 部署的Pod能够通过访问的PSP的检查。
3.开启PSP
首先,在保证K8s集群正常工作的情况下,启用PSP。否则如果K8s集群的系统pod还没有正常启动,PSP规则又开启了,那么所有K8s集群中的系统pod都无法启动,集群将无法正常工作。
在Master上编辑/etc/kubernetes/manifests/kube-apiserver.yaml文件,然后我们需要在command下的enable-admission-plugins下添加PodSecurityPolicy
command:
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
然后重启kubelet,并查看是否启动成功:
systemctl restart kubelet.service
root@vms71:~/psp# kubectl get psp
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
controller false RunAsAny MustRunAs MustRunAs MustRunAs true configMap,secret,emptyDir
speaker true NET_RAW RunAsAny RunAsAny RunAsAny RunAsAny true configMap,secret,emptyDir
删除掉两个默认的PSP规则,方便我们后续实验:
root@vms71:~/psp# kubectl delete psp controller
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy "controller" deleted
root@vms71:~/psp# kubectl delete psp speaker
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy "speaker" deleted
接下来,我们试着使用admin账号创建一个pod,查看结果:
root@vms71:~/psp# kubectl run pod1 --image=nginx --image-pull-policy=IfNotPresent
Error from server (Forbidden): pods "pod1" is forbidden: PodSecurityPolicy: no providers available to validate pod request
可以看到,就算我们使用管理员账户,也无法创建对应的pod,报错提示我们 PodSecurityPolicy: no providers available to validate pod request,说明请求已经被PodSecurityPolicy阻止了。
三、实践一:限制部署Privileged的pod
实验使用的K8s账号是前面笔记中创建的testuser,认证文件为kc1,目前testuser没有任何权限。
现在我们先使用管理员账号创建一个限制部署Privileged pod的PSP,yaml文件如下:
root@vms71:~/psp# cat privileged.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: deny-privilege
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:
- '*'
root@vms71:~/psp# kubectl apply -f privileged.yaml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/deny-privilege created
可以看到,这个文件并没有对其他内容有很大的限制,比如说我们在pod中做卷挂载可以是任意的类型、也没有对pod中的UID、GID进行限制等。但是我们可以看到其中privileged的值设置为了false,表示不允许以特权的方式创建pod。
紧接着,我们继续使用如下的yaml文件为testuser创建一个clusterrole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: crole-pod
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- delete
- list
- create
- watch
在这个clusterrole中,允许了对于pods资源的一些操作。接下来我们需要将创建的clusterrole和testuser绑定起来,命令如下:
kubectl create clusterrolebinding crolebinding-pod --clusterrole=crole-pod --user=testuser
使用testuser以非特权的形式创建pod,可以看到不能够正常创建,这是因为testuser没有权限访问我们设置的PSP。
root@vms71:~/psp# kubectl run pod1 --image=nginx --image-pull-policy=IfNotPresent --kubeconfig=kc1
Error from server (Forbidden): pods "pod1" is forbidden: PodSecurityPolicy: unable to admit pod: []
所以我们还应该设置一个clusterrole,设置能够访问我们设置PSP规则的权限,并绑定到testuser上。如下是对应的yaml文件创建思路和内容:
root@vms71:~/psp# kubectl create clusterrole psp-deny-privilege --verb=use --resource=psp --resource-name=deny-privilege --dry-run=client -o yaml > cluster-role-psp-deny-privilege.yaml
root@vms71:~/psp# cat cluster-role-psp-deny-privilege.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: psp-deny-privilege
rules:
- apiGroups:
- policy
resourceNames:
- deny-privilege
resources:
- podsecuritypolicies
verbs:
- use
使用上述的yaml文件创建psp-deny-privilege的clusterrole后,创建一个clusterrolebinding,绑定到testuser:
kubectl create clusterrolebinding crolebinding-psp-privilege --clusterrole=psp-deny-privilege --user=testuser
现在我们重新使用testuser创建pod,查看是否能够成功:
root@vms71:~/psp# kubectl run pod1 --image=nginx --image-pull-policy=IfNotPresent --kubeconfig=kc1
pod/pod1 created
可以看到,现在已经能够正常创建了。那个,接下来我们以特权的方式来创建一个pod,yaml文件如下:
apiVersion: v1
kind: Pod
metadata:
name: privileged
spec:
containers:
- name: pod1
image: nginx
securityContext:
privileged: true
再次使用testuser创建用户,结果如下:
root@vms71:~/psp# kubectl apply -f privileged-pod.yaml --kubeconfig=kc1
Error from server (Forbidden): error when creating "privileged-pod.yaml": pods "privileged" is forbidden: PodSecurityPolicy: unable to admit pod: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
error信息显示Invalid value: true: Privileged containers are not allowed],说明设置的PSP规则阻止了创建特权pod的请求。
四、实践二:使用Deployment部署pod
我们可以使用K8s的user调动Deployment控制器或者其他控制器部署pod时。而这些控制器在收到了我们的请求后,也会利用自己的Service Accounts完成后续操作。具体步骤如下图所示:
那么,如果Deployment控制器的SA没有被赋予访问运行Deployment操作的PSP规则的权限,就算我们使用admin这个默认的账号也无法完成Deployment的操作。验证步骤如下:
目前,整个环境中依旧存在deny-privilege部署的PSP:
root@vms71:~/psp# kubectl get psp
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
deny-privilege false RunAsAny RunAsAny RunAsAny RunAsAny false *
然后我们使用admin账号使用如下的Deployment的yaml文件创建Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: web1
name: web1
spec:
replicas: 3
selector:
matchLabels:
app1: web1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app1: web1
app2: web1
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: IfNotPresent
resources: {}
status: {}
应用此yaml文件后,虽然显示web1部署成功,但是我们查看pod和Deployment,可以看到pod并没有部署:
root@vms71:~/psp# kubectl get deployments.apps web1
NAME READY UP-TO-DATE AVAILABLE AGE
web1 0/3 0 0 10s
root@vms71:~/psp# kubectl get pods
No resources found in default namespace.
原因就是因为Deployment控制器的SA没有权限去访问PSP。所以,现在我们需要给Deployment控制器的SA赋予访问PSP的权限。首先,创建clusterrole设置访问PSP的权限,yaml文件如下:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: psp-deny-privilege
rules:
- apiGroups:
- policy
resourceNames:
- deny-privilege
resources:
- podsecuritypolicies
verbs:
- use
然后使用clusterrolebinding将创建的clusterrole与Kube-system命名空间内所有的SA(包括Deployment控制器的SA)做绑定:
kubectl create clusterrolebinding crolebinding-psp-privilege --clusterrole=psp-deny-privilege --group=system:serviceaccounts -n kube-system
完成配置后,重新应用development的yaml文件,查看pod是否能够正常部署:
root@vms71:~/psp# kubectl get pods
NAME READY STATUS RESTARTS AGE
web1-6748654d8d-6f26l 1/1 Running 0 5s
web1-6748654d8d-6zzb8 1/1 Running 0 5s
web1-6748654d8d-ph89j 1/1 Running 0 5s
root@vms71:~/psp# kubectl get deployments.apps web1
NAME READY UP-TO-DATE AVAILABLE AGE
web1 3/3 3 3 10s
可以看到,目前Deployment已经能够成功部署pod了,测试成功。
五、其他PSP策略
如下是来自K8s官网的一个具有约束性的策略,要求用户以非特权账号运行,禁止可能的向 root 权限 的升级,同时要求使用若干安全机制。
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
seccomp.security.alpha.kubernetes.io/defaultProfileName: 'runtime/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
具体字段的解释请参考:https://kubernetes.io/zh/docs/concepts/policy/pod-security-policy/#example-policies
整理资料来源:
K8s pod-security-policy:https://kubernetes.io/zh/docs/concepts/policy/pod-security-policy/ Sysdig:https://sysdig.com/blog/psp-in-production/ 《老段CKS课程》