来源:伪架构师
作者:崔秀龙
很多人分不清 SecurityContext 和 PodSecurityPolicy 这两个关键字的差别,其实很简单:
•SecurityContext 是 Pod 中的一个字段,而 PSP 是一个独立的资源类型。
•SecurityContext 是 Pod 自身对安全上下文的声明;
而 PSP 则是强制实施的——不合规矩的 Pod 无法创建。
PSP 的用法和 RBAC 是紧密相关的,换句话说,应用 PSP 的基础要求是:
•不同运维人员的操作账号需要互相隔离并进行单独授权。
•不同命名空间,不同 ServiceAccount 也同样要纳入管理流程。
PSP 环境下,运维人员或者新应用要接入集群,除了 RBAC 设置之外,还需要声明其工作范围所需的安全策略,并进行绑定,才能完成工作。
PSP 的官方文档中提到,PSP 是通过 Admission Controller 启用的,并且注明了:启用 PSP 是一个有风险的工作,未经合理授权,可能导致 Pod 无法创建。
开始之前,首先设置一个别名,在 default 命名空间新建 ServiceAccount 来模拟一个有权创建 Pod 的用户:
$ kubectl create sa common serviceaccount/common created $ kubectl create rolebinding common --clusterrole=edit --serviceaccount=default:common rolebinding.rbac.authorization.k8s.io/common created $ alias kube-common='kubectl --as=system:serviceaccount:default:common'
第一个 PSP
我们首先创建一个不允许创建特权 Pod 的策略:
apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: noprivileged spec: privileged: false seLinux: rule: RunAsAny supplementalGroups: rule: RunAsAny runAsUser: rule: RunAsAny fsGroup: rule: RunAsAny volumes: - '*'
保存为 psp.noprivileged.yaml 并提交给集群。
接下来创建两个 Pod:
apiVersion: v1 kind: Pod metadata: name: noprivileged spec: containers: - name: pause
image: k8s.gcr.io/pause
apiVersion: v1 kind: Pod metadata: name: privileged spec: containers: - name: pause image: k8s.gcr.io/pause securityContext: privileged: true
用普通用户创建这个 Pod:
$ kube-common apply -f pod.yaml && kube-common delete -f pod.yaml pod/noprivileged created pod/privileged created pod "noprivileged" deleted pod "privileged" deleted
可以看到,在不允许创建特权容器的规则之中,我们的用户还是能够创建特权容器,这是因为还没启用 PSP,接下来在集群设置中启动 PSP,各种环境的启用方式不同,例如在 GKE 环境:
$ gcloud beta container clusters update gcp-k8s --enable-pod-security-policy --zone=asia-east1-a Updating gcp-vlab-k8s...done.
删除重建 Pod:
$ kube-common apply -f pod.yaml && kube-common delete -f pod.yaml Error from server (Forbidden): error when creating "pod.yaml": pods "noprivileged" is forbidden: unable to validate against any pod security policy: [] Error from server (Forbidden): error when creating "pod.yaml": pods "privileged" is forbidden: unable to validate against any pod security policy: []
可以看到,Pod 的新建请求被拒绝了——然而使用集群管理员身份还是能成功创建的:
$ kubectl apply -f pod.yaml && kubectl delete -f pod.yaml pod/noprivileged created pod/privileged created pod "noprivileged" deleted pod "privileged" deleted
全员 admin 是万恶之源。
用 RBAC 进行授权:
$ kubectl create role psp:noprivileged \ --verb=use \ --resource=podsecuritypolicy \ --resource-name=noprivileged role.rbac.authorization.k8s.io/psp:noprivileged created $ kubectl create rolebinding common:psp:noprivileged \ --role=psp:noprivileged \ --serviceaccount=default:common rolebinding.rbac.authorization.k8s.io/common:psp:noprivileged created
再试试普通用户的能力:
$ kube-common apply -f pod.yaml ; kube-common delete -f pod.yaml pod/noprivileged created Error from server (Forbidden): error when creating "pod.yaml": 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 "noprivileged" deleted Error from server (NotFound): error when deleting "pod.yaml": pods "privileged" not found
非特权 Pod 才能够成功创建,这符合我们的预期。
副作用
Pod 成功创建了之后,顺理成章,做个 Deployment 看看:
kind: Deployment metadata: name: privileged spec: replicas: 1 template: metadata: labels: app: pause version: v1 spec: containers: - name: sleep image: k8s.gcr.io/pause securityContext:
privileged: true
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: noprivileged spec: replicas: 1 template: serviceAccount: common metadata: labels: app: pause version: v1 spec: containers: - name: sleep image: k8s.gcr.io/pause
我们会发现,Deployment 无法正常工作:
$ kubectl get pods kuNo resources found in default namespace. $ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE noprivileged 0/1 0 0 15m privileged 0/1 0 0 15m
查看一下事件:
$ kubectl get events | grep policy 8m38s Warning FailedCreate replicaset/noprivileged-6f94f9c9b8 Error creating: pods "noprivileged-6f94f9c9b8-" is forbidden: unable to validate against any pod security policy: [] 8m38s Warning FailedCreate replicaset/privileged-6d78d5458 Error creating: pods "privileged-6d78d5458-" is forbidden: unable to validate against any pod security policy: []
这次的 Pod 不是由我们授权的 common 用户创建的,而是由 RS Controller 启动的,因此会失败,加入一个 Service Account:
... spec: serviceAccount: common containers: ... spec: serviceAccount: common containers: ...
提交变更,会发现非特权 Pod 开始创建:
$ kubectl get pods NAME READY STATUS RESTARTS AGE noprivileged-6cf595c5bd-rc8cx 1/1 Running 0 4s
系统 Pod 怎么办
这时候我想到个问题,其它 Pod 会不会受到影响?我删除了 kube-system 下面的一个 kube-proxy的 Pod,发现这个 Pod 自动重建了,没有受到 PSP 的影响,查看一下 RBAC 相关配置,会发现 GCP 在更新集群的过程中已经为系统服务进行了预设:
$ kubectl get rolebinding ... gce:podsecuritypolicy:kube-proxy 80m gce:podsecuritypolicy:metadata-agent 80m gce:podsecuritypolicy:metadata-proxy 80m gce:podsecuritypolicy:nodes 80m ...
追查下去:
$ kubectl get rolebinding gce:podsecuritypolicy:metadata-proxy -o yaml ... roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: gce:podsecuritypolicy:privileged subjects: - kind: ServiceAccount name: metadata-proxy namespace: kube-system
如果追查其中涉及到的 ClusterRole,会发现它指向一个 PSP:
$ kubectl get clusterrole gce:podsecuritypolicy:privileged -o yaml ... rules: - apiGroups: - policy resourceNames: - gce.privileged resources: - podsecuritypolicies verbs: - use
看看这个 PSP 的内容:
$ kubectl get psp gce.privileged -o yaml ... privileged: true ...
的确包含了特权 Pod 的内容。
最后看看负责创建这个特权 Pod 的 Daemonset:
$ kubectl get daemonset metadata-proxy-v0.1 -o yaml ... serviceAccount: metadata-proxy serviceAccountName: metadata-proxy ...
PSP的限制能力
分为以下几个大方面:
•特权容器
•主机命名空间:
例如 HostPID、HostNetwork 等。
•卷和文件系统:
例如 PVC、configMap、emptyDir 等卷类型,以及 fsGroup、AllowedHostPaths 等加载能力。
•用户和组:
运行身份
•提权:
是否允许
•Capability 和 sysctl
•SeLinux、AppArmor 等。
马后炮
kubectl 的 advise-psp 插件,能够根据当前运行的 Pod,提取出所需的 PSP 信息。
参考链接:
•https://kubernetes.io/docs/con … licy/