【云原生】K8s PSP 和 securityContext 介绍与使用
一、概述
Pod Security Policies(Adminssion Controller)
授予users和service accounts创建或更新pods使用资源的权限。这是一种集群级别的资源类型,用来限制pod对敏感资源的使用。它能够控制Pod的各个安全方面。
举例来说PSP可以做的事情:
- 是否允许Pod使用宿主节点的PID,IPC,网络命名空间。
- Pod是否允许绑定到宿主节点端口。
- 容器运行时允许使用的用户ID。
- 是否允许特权模式的POD。
【温馨提示】PodSecurityPolicy 在 Kubernetes v1.21 中被弃用, 在 Kubernetes v1.25 中被移除。
作为替代,你可以使用下面任一方式执行类似的限制,或者同时使用下面这两种方式。
- PodSecurity admission
- 自行部署并配置第三方插件
官方文档:https://kubernetes.io/zh/docs/concepts/policy/pod-security-policy/
关于 k8s RBAC的介绍,可以参考我这篇文章:Kubernetes(k8s)权限管理RBAC详解
二、PodSecurityPolicy 的发展
1)以前为什么需要 PodSecurityPolicy?
-
在 Kubernetes 中,我们定义了 Deployment、StatefulSet 和 Service 等资源。 这些资源代表软件应用程序的构建块。Kubernetes 集群中的各种控制器根据这些资源做出反应, 创建更多的 Kubernetes 资源或配置一些软件或硬件来实现我们的目标。
-
在大多数 Kubernetes 集群中,由 RBAC(基于角色的访问控制)规则 控制对这些资源的访问。 list、get、create、edit 和 delete 是 RBAC 关心的 API 操作类型, 但 RBAC 不考虑其所控制的资源中加入了哪些设置。例如, Pod 几乎可以是任何东西,例如简单的网络服务器,或者是特权命令提示(提供对底层服务器节点和所有数据的完全访问权限)。 这对 RBAC 来说都是一样的:Pod 就是 Pod 而已。
-
要控制集群中定义的资源允许哪些类型的设置,除了 RBAC 之外,还需要访问控制。 从 Kubernetes 1.3 开始,内置 PodSecurityPolicy 一直被作为 Pod 安全相关字段的访问控制机制。 使用 PodSecurityPolicy,可以防止“创建 Pod”这个能力自动变成“每个集群节点上的 root 用户”, 并且无需部署额外的外部访问控制器。
2)现在为什么 PodSecurityPolicy 要消失?
-
自从首次引入 PodSecurityPolicy 以来,我们已经意识到 PSP 存在一些严重的可用性问题, 只有做出断裂式的改变才能解决。
-
实践证明,PSP 应用于 Pod 的方式让几乎所有尝试使用它们的人都感到困惑。 很容易意外授予比预期更广泛的权限,并且难以查看某种特定情况下应用了哪些 PSP。 “更改 Pod 默认值”功能很方便,但仅支持某些 Pod 设置,而且无法明确知道它们何时会或不会应用于的 Pod。 如果没有“试运行”或审计模式,将 PSP 安全地改造并应用到现有集群是不切实际的,并且永远都不可能默认启用 PSP。
三、PSP 简单使用
因为PSP在高版本中已经不再使用了,所以这里稍微了解即可。目前,如果我们想要使用PSP,需要手动配置启动。PSP控制pod的方面包容如下:
1)开启PSP
在Master上编辑/etc/kubernetes/manifests/kube-apiserver.yaml
文件,然后我们需要在command下的enable-admission-plugins下添加PodSecurityPolicy
command:
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
重启kubelet
systemctl restart kubelet.service
【注意】开启PodSecurityPolicy功能后,即使没有使用任何安全策略,都会使得创建pods(包括调度任务重新创建pods)失败
2)示例演示
1、没有PSP场景测试
通过下面的deployment yaml文件测试在没有PSP策略的情况下是否可以创建pod:
cat >nginx-deployment.yaml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.4
EOF
使用下面的命令创建deployment资源:
kubectl apply -f nginx-deployment.yaml
使用下面的命令检查pod是否创建:
kubectl get pods,replicasets,deployments
通过上图可见deployments和relicaset正在运行,由于缺少PSP policy,集群并没有创建相应的pods。
2、定义PSP
这里定义两个不同权限的PSP策略:
【1】资源限制策略
下面是典型的限制资源使用的restrictive policy
:
cat >default-restrict-psp.yaml<<EOF
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restrictive
spec:
privileged: false
hostNetwork: false
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
hostPID: false
hostIPC: false
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
volumes:
- 'configMap'
- 'downwardAPI'
- 'emptyDir'
- 'persistentVolumeClaim'
- 'secret'
- 'projected'
allowedCapabilities:
- '*'
EOF
【2】资源允许策略
下面是允许使用资源的permissive policy
,多条permissive policy规则被用来设置相应user或者sevice account使用相关类型的资源。
cat >permissive-psp.yaml<<EOF
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: permissive
spec:
privileged: true
hostNetwork: true
hostIPC: true
hostPID: true
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
hostPorts:
- min: 0
max: 65535
volumes:
- '*'
EOF
上面两种poicies可以使用下面的命令创建对应的PSP资源:
kubectl apply -f default-restrict-psp.yaml
kubectl apply -f permissive-psp.yaml
核对psp是否创建成功与否:
kubectl get psp
下面将通过cluster role和cluster role binding在集群中使用刚才定义的安全策略(Pod Security Policy)。
3、角色定义(Cluster Role)
关于restrictive policy的restrictive cluster role
cat >psp-restrictive-cluster-roleyaml<<EOF
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: psp-restrictive
rules:
- apiGroups:
- extensions
resources:
- podsecuritypolicies
resourceNames:
- restrictive
verbs:
- use
EOF
关于permissive policy的permissive cluster role
cat >psp-permissive-cluster-roleyaml <<EOF
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: psp-permissive
rules:
- apiGroups:
- extensions
resources:
- podsecuritypolicies
resourceNames:
- permissive
verbs:
- use
EOF
通过下面命令创建相应的cluster role资源:
kubectl apply -f psp-restrictive-cluster-roleyaml
kubectl apply -f psp-permissive-cluster-roleyaml
4、角色绑定(Cluster Role Bindings)
cat >psp-restrictive-cluster-role-binding.yaml<<EOF
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: psp-default
subjects:
- kind: Group
name: system:serviceaccounts
namespace: kube-system
roleRef:
kind: ClusterRole
name: psp-restrictive
apiGroup: rbac.authorization.k8s.io
EOF
上面的role binding会将restrictive cluster role绑定到所有的system service account。
使用下面的命令生成role binding资源:
kubectl apply -f psp-restrictive-cluster-role-binding.yaml
5、再测试验证
kubectl delete deploy nginx-deployment
kubectl get po,rs,deploy
通过上图可知,可以正常调度pod,启动pod了。
四、PodSecurity admission
Pod security admission 是 Kubernetes 内置的一种准入控制器,在 Kubernetes V1.23版本中这一特性门是默认开启的,在V1.22中需要通过 Kube-apiserver 参数
--feature-gates="...,PodSecurity=true"
开启。在低于V1.22的 Kuberntes 版本中也可以自行安装 Pod Security Admission Webhook。
PodSecurity admission
是通过执行内置的Pod Security Standards
来限制集群中的 pod 的创建。- Pod 安全性标准定义了三种不同的 策略(Policy),以广泛覆盖安全应用场景。 这些策略是 叠加式的(Cumulative),安全级别从高度宽松至高度受限。 本指南概述了每个策略的要求。
1)podSecurity Standards
官方文档:https://kubernetes.io/zh-cn/docs/concepts/security/pod-security-standards/
为了广泛的覆盖安全应用场景, Pod Security Standards 渐进式的定义了三种不同的 Pod 安全标准策略:
策略 | 描述 |
---|---|
Privileged | 不受限制的策略,提供最大可能范围的权限许可。此策略允许已知的特权提升。 |
Baseline | 限制性最弱的策略,禁止已知的策略提升。允许使用默认的(规定最少)Pod 配置。 |
Restricted | 限制性非常强的策略,遵循当前的保护 Pod 的最佳实践。 |
2)podSecurity admission
官方文档:https://kubernetes.io/zh-cn/docs/concepts/security/pod-security-admission/
在 Kubernetes 集群中开启了 podSecurity admission 后,就可以通过给 namespace 设置 label 的方式来实施 Pod Security Standards。其中有三种设定模式可选用:
模式 | 描述 |
---|---|
enforce | 违反安全标准策略的 Pod 将被拒绝。 |
audit | 违反安全标准策略触发向审计日志中记录的事件添加审计注释,但其他行为被允许。 |
warn | 违反安全标准策略将触发面向用户的警告,但其他行为被允许。 |
label 设置模板解释:
# 设定模式及安全标准策略等级
# MODE必须是 `enforce`, `audit`或`warn`其中之一。
# LEVEL必须是`privileged`, `baseline`或 `restricted`其中之一
pod-security.kubernetes.io/<MODE>: <LEVEL>
# 此选项是非必填的,用来锁定使用哪个版本的的安全标准
# MODE必须是 `enforce`, `audit`或`warn`其中之一。
# VERSION必须是一个有效的kubernetes minor version(例如v1.23),或者 `latest`
pod-security.kubernetes.io/<MODE>-version: <VERSION>
通过准入控制器配置文件,可以为 Pod security admission 设置默认配置:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1beta1
kind: PodSecurityConfiguration
# Defaults applied when a mode label is not set.
#
# Level label values must be one of:
# - "privileged" (default)
# - "baseline"
# - "restricted"
#
# Version label values must be one of:
# - "latest" (default)
# - specific version like "v1.23"
defaults:
enforce: "privileged"
enforce-version: "latest"
audit: "privileged"
audit-version: "latest"
warn: "privileged"
warn-version: "latest"
exemptions:
# Array of authenticated usernames to exempt.
usernames: []
# Array of runtime class names to exempt.
runtimeClassNames: []
# Array of namespaces to exempt.
namespaces: []
podSecurity admission 可以从 username,runtimeClassName,namespace三个维度对 Pod 进行安全标准检查的豁免。
3)podSecurity standards 示例演示
1、Baseline 策略
Baseline
策略目标是应用于常见的容器化应用,禁止已知的特权提升,在官方的介绍中此策略针对的是应用运维人员和非关键性应用开发人员,在该策略中包括:
必须禁止共享宿主命名空间、禁止容器特权、 限制 Linux 能力、禁止 hostPath 卷、限制宿主机端口、设定 AppArmor、SElinux、Seccomp、Sysctls 等。
违反 Baseline 策略存在的风险:
- 特权容器可以看到宿主机设备
- 挂载 procfs 后可以看到宿主机进程,打破进程隔离
- 可以打破网络隔离
- 挂载运行时 socket 后可以不受限制的与运行时通信
【1】创建名为 my-baseline-namespace 的 namespace,并设定 enforce 和 warn 两种模式都对应 Baseline 等级的 Pod 安全标准策略:
apiVersion: v1
kind: Namespace
metadata:
name: my-baseline-namespace
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.23
pod-security.kubernetes.io/warn: baseline
pod-security.kubernetes.io/warn-version: v1.23
【2】创建一个违反 baseline 策略的 pod
apiVersion: v1
kind: Pod
metadata:
name: hostnamespaces2
namespace: my-baseline-namespace
spec:
containers:
- image: bitnami/prometheus:2.33.5
name: prometheus
securityContext:
allowPrivilegeEscalation: true
privileged: true
capabilities:
drop:
- ALL
hostPID: true
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
执行 apply 命令,显示不能设置 hostPID=true
,securityContext.privileged=true
,Pod 创建被拒绝,特权容器的运行,并且开启 hostPID,容器进程没有与宿主机进程隔离,容易造成 Pod 容器逃逸:
【3】创建不违反 baseline 策略的 pod,设定 Pod 的 hostPID=false
,securityContext.privileged=false
apiVersion: v1
kind: Pod
metadata:
name: hostnamespaces2
namespace: my-baseline-namespace
spec:
containers:
- image: bitnami/prometheus:2.33.5
name: prometheus
securityContext:
allowPrivilegeEscalation: false
privileged: false
capabilities:
drop:
- ALL
hostPID: false
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
执行 apply 命令,pod 被允许创建。
2、Restricted 策略
Restricted
策略目标是实施当前保护 Pod 的最佳实践,在官方介绍中此策略主要针对运维人员和安全性很重要的应用开发人员,以及不太被信任的用户。该策略包含所有的 baseline 策略的内容,额外增加:限制可以通过 PersistentVolumes 定义的非核心卷类型、禁止(通过 SetUID 或 SetGID 文件模式)获得特权提升、必须要求容器以非 root 用户运行、Containers 不可以将 runAsUser 设置为 0、 容器组必须弃用 ALL capabilities 并且只允许添加 NET_BIND_SERVICE 能力。
Restricted 策略进一步的限制在容器内获取 root 权限,linux 内核功能。例如针对 kubernetes 网络的中间人攻击需要拥有 Linux 系统的 CAP_NET_RAW 权限来发送 ARP 包。
【1】创建名为 my-restricted-namespace的namespace,并设定 enforce 和 warn 两种模式都对应 Restricted 等级的 Pod 安全标准策略:
apiVersion: v1
kind: Namespace
metadata:
name: my-restricted-namespace
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.23
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.23
【2】创建一个违反 Restricted 策略的 pod
apiVersion: v1
kind: Pod
metadata:
name: runasnonroot0
namespace: my-restricted-namespace
spec:
containers:
- image: bitnami/prometheus:2.33.5
name: prometheus
securityContext:
allowPrivilegeEscalation: false
securityContext:
seccompProfile:
type: RuntimeDefault
执行 apply 命令,显示必须设置 securityContext.runAsNonRoot=true,securityContext.capabilities.drop=["ALL"],Pod 创建被拒绝,容器以 root 用户运行时容器获取权限过大,结合没有 Drop linux 内核能力有 kubernetes 网络中间人攻击的风险。
【3】创建不违反 Restricted 策略的 pod,设定 Pod 的 securityContext.runAsNonRoot=true,Drop 所有 linux 能力。
apiVersion: v1
kind: Pod
metadata:
name: runasnonroot0
namespace: my-restricted-namespace
spec:
containers:
- image: bitnami/prometheus:2.33.5
name: prometheus
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
执行 apply 命令,pod 被允许创建。
4)Pod Security admission 当前局限性
如果你的集群中已经配置 PodSecurityPolicy,考虑把它们迁移到 podSecurity admission 是需要一定的工作量的。
首先需要考虑当前的 podSecurity admission 是否适合你的集群,目前它旨在满足开箱即用的最常见的安全需求,与 PSP 相比它存在以下差异:
podSecurity admission
只是对 pod 进行安全标准的检查,不支持对 pod 进行修改,不能为 pod 设置默认的安全配置。podSecurity admission
只支持官方定义的三种安全标准策略,不支持灵活的自定义安全标准策略。这使得不能完全将PSP规则迁移到 podSecurity admission,需要进行具体的安全规则考量。- podSecurity admission 不像 PSP 一样可以与具体的用户进行绑定,只支持豁免特定的用户或者 RuntimeClass 及 namespace。
五、默认的 securityContext 介绍和使用
SecurityContext 包含 Pod 级别的安全属性和常见的容器设置。 可选:默认为空。每个字段的默认值见类型描述。
【温馨提示】PodSecurityContext 包含 Pod 级别的安全属性和常用容器设置。 一些字段也存在于 container.securityContext 中。
container.securityContext
中的字段值优先于PodSecurityContext
的字段值。
securityContext.runAsUser
——运行容器进程入口点(Entrypoint)的 UID。如果未指定,则默认为镜像元数据中指定的用户。 也可以在 SecurityContext 中设置。 如果同时在SecurityContext
和PodSecurityContext
中设置,则在对应容器中所设置的 SecurityContext 值优先。 注意,spec.os.name 为 "windows" 时不能设置此字段。securityContext.runAsGroup
——运行容器进程入口点(Entrypoint)的 GID。如果未设置,则使用运行时的默认值。 也可以在 SecurityContext 中设置。如果同时在SecurityContext
和PodSecurityContext
中设置, 则在对应容器中设置的 SecurityContext 值优先。 注意,spec.os.name 为 "windows" 时不能设置该字段。securityContext.privileged
——以特权模式运行容器。特权容器中的进程本质上等同于主机上的 root。默认为 false。 默认情况下,容器是不可以访问宿主上的任何设备的,不过一个“privileged(特权的)” 容器则被授权访问宿主上所有设备。 这种容器几乎享有宿主上运行的进程的所有访问权限。 【注意】这个参数不能在PodSecurityContext
中设置。 注意,spec.os.name 为 "windows" 时不能设置此字段。securityContext.fsGroup
——应用到 Pod 中所有容器的特殊补充组。某些卷类型允许 kubelet 将该卷的所有权更改为由 Pod 拥有:- 文件系统的属主 GID 将是 fsGroup 字段值
- setgid 位已设置(在卷中创建的新文件将归 fsGroup 所有)
- 权限位将与 rw-rw---- 进行按位或操作
securityContext.fsGroupChangePolicy
——fsGroupChangePolicy
定义了在卷被在 Pod 中暴露之前更改其属主和权限的行为。 此字段仅适用于支持基于 fsGroup 的属主权(和权限)的卷类型。它不会影响临时卷类型, 例如:secret、configmap 和 emptydir。 有效值为 "OnRootMismatch" 和 "Always"。如果未设置,则使用 "Always"。 注意,spec.os.name 为 "windows" 时不能设置此字段。
上面只是列出了几个常用的参数,更多参数,可以参考官方文档。对应的字段如下:
spec.securityContext.runAsUser
spec.securityContext.runAsGroup
spec.containers[*].securityContext.privileged
spec.containers[*].securityContext.runAsUser
spec.containers[*].securityContext.runAsGroup
spec.securityContext.fsGroup
spec.securityContext.fsGroupChangePolicy
【示例1】PodSecurityContext
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
securityContext:
runAsUser: 10000
runAsGroup: 10000
containers:
- name: nginx
image: nginx:1.15.4
【示例2】container.securityContext
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: default
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
securityContext:
runAsUser: 10000
runAsGroup: 10000
containers:
- name: nginx
image: nginx:1.15.4
securityContext:
runAsUser: 10000
runAsGroup: 10000
privileged: true
fsGroup: 2000
如上这个配置,可以看到 Pod 中所有容器内的进程都是已用户 uid=10000 和主组 gid=10000来运行的,而创建的任何文件的属组都是 groups=2000。通过这样的方法,可以达到让用户的进程不使用默认的 Root (uid=0,gid=0)权限的目的。
【温馨提示】镜像中必须存储10000 UID 和 10000 GUI ,如果同时设置了,container.securityContext优先级更高。
K8s PSP 和 securityContext 介绍与简单使用就先到这里,有任何疑问欢迎给我留言,后续会持续更新【云原生+大数据】相关的文章。