从 PodSecurityPolicy 迁移到内置 PodSecurity 准入控制器
本页面介绍了如何从 PodSecurityPolicies 迁移到内置的 PodSecurity 准入控制器。这可以通过结合使用 dry-run 和 audit
和 warn
模式来有效地完成,但如果使用了 mutating PSP,这将变得更加困难。
准备工作
您的 Kubernetes 服务器版本必须为 v1.22 或更高版本。要检查版本,请输入 kubectl version
。
如果您当前运行的 Kubernetes 版本不是 1.30,您可能希望切换到您实际运行的 Kubernetes 版本的文档中查看此页面。
本页面假设您已经熟悉基本的 Pod 安全准入 概念。
总体方法
您可以采用多种策略从 PodSecurityPolicy 迁移到 Pod 安全准入。以下步骤是一种可能的迁移路径,其目标是最大限度地降低生产中断和安全漏洞的风险。
- 确定 Pod 安全准入是否适合您的用例。
- 查看命名空间权限
- 简化和标准化 PodSecurityPolicies
- 更新命名空间
- 确定合适的 Pod 安全级别
- 验证 Pod 安全级别
- 强制执行 Pod 安全级别
- 绕过 PodSecurityPolicy
- 查看命名空间创建过程
- 禁用 PodSecurityPolicy
0. 确定 Pod 安全准入是否适合您
Pod 安全准入旨在满足开箱即用的最常见安全需求,并在集群之间提供一组标准的安全级别。但是,它不如 PodSecurityPolicy 灵活。值得注意的是,PodSecurityPolicy 支持以下功能,但 Pod 安全准入不支持
- 设置默认安全约束 - Pod 安全准入是一个非 mutating 准入控制器,这意味着它不会在验证 Pod 之前修改它们。如果您依赖于 PSP 的这方面,您将需要修改您的工作负载以满足 Pod 安全约束,或者使用 Mutating 准入 Webhook 来进行这些更改。有关更多详细信息,请参阅下面的 简化和标准化 PodSecurityPolicies。
- 对策略定义进行细粒度控制 - Pod 安全准入仅支持 3 个标准级别。如果您需要对特定约束进行更多控制,则需要使用 Validating 准入 Webhook 来强制执行这些策略。
- 子命名空间策略粒度 - PodSecurityPolicy 允许您将不同的策略绑定到不同的服务帐户或用户,即使在单个命名空间内也是如此。这种方法有很多缺陷,不建议使用,但如果您仍然需要此功能,则需要使用第三方 webhook。例外情况是,如果您只需要完全豁免特定用户或 RuntimeClasses,在这种情况下,Pod 安全准入确实公开了一些 用于豁免的静态配置。
即使 Pod 安全准入不能满足您的所有需求,它的设计也是为了补充其他策略执行机制,并且可以提供与其他准入 webhook 一起运行的有用回退。
1. 查看命名空间权限
Pod 安全准入由 命名空间上的标签 控制。这意味着任何可以更新(或修补或创建)命名空间的人也可以修改该命名空间的 Pod 安全级别,这可用于绕过更严格的策略。在继续之前,请确保只有受信任的特权用户才具有这些命名空间权限。不建议将这些强大的权限授予不应该具有提升权限的用户,但如果您必须这样做,则需要使用 准入 webhook 对在命名空间对象上设置 Pod 安全标签施加额外的限制。
2. 简化和标准化 PodSecurityPolicies
在本节中,您将减少 mutating PodSecurityPolicies 并删除 Pod 安全标准范围之外的选项。您应该将此处建议的更改应用于要修改的原始 PodSecurityPolicy 的离线副本。克隆的 PSP 应该有一个与原始名称不同的名称,并且按字母顺序排列在原始名称之前(例如,在其前面添加一个 0
)。请勿在 Kubernetes 中创建新策略 - 这将在下面的 滚动更新策略 部分中介绍。
2.a. 消除纯粹的 mutating 字段
如果 PodSecurityPolicy 正在改变 Pod,那么当您最终关闭 PodSecurityPolicy 时,您最终可能会得到不符合 Pod 安全级别要求的 Pod。为了避免这种情况,您应该在切换之前消除所有 PSP 突变。不幸的是,PSP 没有干净地分离 mutating 和 validating 字段,因此这不是一个简单的迁移。
您可以先消除纯粹的 mutating 字段,这些字段与 validating 策略没有任何关系。这些字段(也在 将 PodSecurityPolicies 映射到 Pod 安全标准 参考中列出)是
.spec.defaultAllowPrivilegeEscalation
.spec.runtimeClass.defaultRuntimeClassName
.metadata.annotations['seccomp.security.alpha.kubernetes.io/defaultProfileName']
.metadata.annotations['apparmor.security.beta.kubernetes.io/defaultProfileName']
.spec.defaultAddCapabilities
- 虽然技术上是一个 mutating 和 validating 字段,但这些应该合并到.spec.allowedCapabilities
中,后者在没有突变的情况下执行相同的验证。
注意
删除这些可能会导致工作负载缺少所需的配置,并导致问题。有关如何安全地推出这些更改的建议,请参阅下面的 滚动更新策略。2.b. 消除 Pod 安全标准未涵盖的选项
PodSecurityPolicy 中有几个字段不在 Pod 安全标准的涵盖范围内。如果您必须强制执行这些选项,则需要使用 准入 webhook 来补充 Pod 安全准入,这超出了本指南的范围。
首先,您可以删除 Pod 安全标准未涵盖的纯粹 validating 字段。这些字段(也在 将 PodSecurityPolicies 映射到 Pod 安全标准 参考中列为“无意见”)是
.spec.allowedHostPaths
.spec.allowedFlexVolumes
.spec.allowedCSIDrivers
.spec.forbiddenSysctls
.spec.runtimeClass
您还可以删除以下与 POSIX / UNIX 组控件相关的字段。
注意
如果其中任何一个使用MustRunAs
策略,则它们可能是 mutating 的!删除这些可能会导致工作负载未设置所需的组,并导致问题。有关如何安全地推出这些更改的建议,请参阅下面的 滚动更新策略。.spec.runAsGroup
.spec.supplementalGroups
.spec.fsGroup
其余的 mutating 字段是正确支持 Pod 安全标准所必需的,稍后需要根据具体情况进行处理
.spec.requiredDropCapabilities
- 需要为受限配置文件删除ALL
。.spec.seLinux
- (仅使用MustRunAs
规则时是 mutating 的)需要强制执行基线和受限配置文件的 SELinux 要求。.spec.runAsUser
- (使用RunAsAny
规则时是非 mutating 的)需要为受限配置文件强制执行RunAsNonRoot
。.spec.allowPrivilegeEscalation
- (仅当设置为false
时才是 mutating 的)受限配置文件需要。
2.c. 滚动更新 PSP
接下来,您可以将更新后的策略滚动更新到您的集群。您应该谨慎行事,因为删除 mutating 选项可能会导致工作负载缺少所需的配置。
对于每个更新的 PodSecurityPolicy
- 识别在原始 PSP 下运行的 Pod。这可以使用
kubernetes.io/psp
注释来完成。例如,使用 kubectlPSP_NAME="original" # Set the name of the PSP you're checking for kubectl get pods --all-namespaces -o jsonpath="{range .items[?(@.metadata.annotations.kubernetes\.io\/psp=='$PSP_NAME')]}{.metadata.namespace} {.metadata.name}{'\n'}{end}"
- 将这些正在运行的 Pod 与原始 Pod 规范进行比较,以确定 PodSecurityPolicy 是否修改了 Pod。对于由 工作负载资源 创建的 Pod,您可以将 Pod 与控制器资源中的 PodTemplate 进行比较。如果识别出任何更改,则应使用所需的配置更新原始 Pod 或 PodTemplate。要查看的字段是
.metadata.annotations['container.apparmor.security.beta.kubernetes.io/*']
(将 * 替换为每个容器名称).spec.runtimeClassName
.spec.securityContext.fsGroup
.spec.securityContext.seccompProfile
.spec.securityContext.seLinuxOptions
.spec.securityContext.supplementalGroups
- 在容器上,在
.spec.containers[*]
和.spec.initContainers[*]
下.securityContext.allowPrivilegeEscalation
.securityContext.capabilities.add
.securityContext.capabilities.drop
.securityContext.readOnlyRootFilesystem
.securityContext.runAsGroup
.securityContext.runAsNonRoot
.securityContext.runAsUser
.securityContext.seccompProfile
.securityContext.seLinuxOptions
- 创建新的 PodSecurityPolicies。如果任何角色或集群角色都对所有 PSP 授予
use
权限,这可能会导致使用新的 PSP 而不是它们的 mutating 副本。 - 更新您的授权以授予对新 PSP 的访问权限。在 RBAC 中,这意味着更新任何对原始 PSP 授予 `use` 权限的角色或集群角色,以使其也对更新后的 PSP 授予该权限。
- 验证:经过一段时间后,重新运行步骤 1 中的命令,以查看是否有任何 Pod 仍在使用原始 PSP。请注意,在推出新策略后,需要重新创建 Pod 才能对其进行全面验证。
- (可选)验证原始 PSP 不再使用后,您可以将其删除。
3. 更新命名空间
以下步骤需要在集群中的每个命名空间上执行。这些步骤中引用的命令使用 `$NAMESPACE` 变量来引用要更新的命名空间。
3.a. 确定合适的 Pod 安全级别
首先查看Pod 安全标准,并熟悉 3 个不同的级别。
有几种方法可以为您的命名空间选择 Pod 安全级别
- 根据命名空间的安全要求 - 如果您熟悉命名空间的预期访问级别,则可以根据这些要求选择合适的级别,类似于在新集群上处理此问题的方式。
- 根据现有的 PodSecurityPolicy - 使用将 PodSecurityPolicy 映射到 Pod 安全标准参考,您可以将每个 PSP 映射到一个 Pod 安全标准级别。如果您的 PSP 不是基于 Pod 安全标准的,则您可能需要决定是选择至少与 PSP 一样宽松的级别,还是选择至少与 PSP 一样严格的级别。您可以使用以下命令查看给定命名空间中 Pod 正在使用的 PSP
kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
- 根据现有 Pod - 使用验证 Pod 安全级别下的策略,您可以测试基准和受限级别,以查看它们是否对现有工作负载足够宽松,并选择权限最低的有效级别。
注意
上述选项 2 和 3 基于*现有* Pod,并且可能会遗漏当前未运行的工作负载,例如 CronJob、缩放到零的工作负载或其他尚未推出的工作负载。3.b. 验证 Pod 安全级别
为命名空间选择 Pod 安全级别后(或者如果您正在尝试多个级别),最好先对其进行测试(如果使用特权级别,则可以跳过此步骤)。Pod 安全性包含多个工具,可帮助测试和安全地推出配置文件。
首先,您可以对策略进行空运行,这将根据应用的策略评估当前在命名空间中运行的 Pod,而不会使新策略生效
# $LEVEL is the level to dry-run, either "baseline" or "restricted".
kubectl label --dry-run=server --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL
对于任何在建议级别下无效的*现有* Pod,此命令将返回警告。
第二种选择更适合捕获当前未运行的工作负载:审计模式。在审计模式下运行时(而不是强制执行),违反策略级别的 Pod 会记录在审计日志中,您可以在一段时间后查看这些日志,但不会禁止这些 Pod。警告模式的工作原理类似,但会立即将警告返回给用户。您可以使用以下命令在命名空间上设置审计级别
kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/audit=$LEVEL
如果这两种方法中的任何一种都产生了意外的违规,则您需要更新违规工作负载以满足策略要求,或者放宽命名空间 Pod 安全级别。
3.c. 强制执行 Pod 安全级别
当您确信所选级别可以在命名空间上安全地强制执行时,您可以更新命名空间以强制执行所需的级别
kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL
3.d. 绕过 PodSecurityPolicy
最后,您可以通过将完全特权 PSP绑定到命名空间中的所有服务帐户,从而在命名空间级别有效地绕过 PodSecurityPolicy。
# The following cluster-scoped commands are only needed once.
kubectl apply -f privileged-psp.yaml
kubectl create clusterrole privileged-psp --verb use --resource podsecuritypolicies.policy --resource-name privileged
# Per-namespace disable
kubectl create -n $NAMESPACE rolebinding disable-psp --clusterrole privileged-psp --group system:serviceaccounts:$NAMESPACE
由于特权 PSP 是非突变的,并且 PSP 准入控制器始终优先选择非突变 PSP,因此这将确保此命名空间中的 Pod 不再被 PodSecurityPolicy 修改或限制。
像这样在每个命名空间的基础上禁用 PodSecurityPolicy 的优势在于,如果出现问题,您可以通过删除 RoleBinding 轻松回滚更改。只要确保预先存在的 PodSecurityPolicy 仍然存在即可!
# Undo PodSecurityPolicy disablement.
kubectl delete -n $NAMESPACE rolebinding disable-psp
4. 查看命名空间创建过程
现在,现有命名空间已更新为强制执行 Pod 安全准入,您应确保更新用于创建新命名空间的过程和/或策略,以确保将适当的 Pod 安全配置文件应用于新命名空间。
您还可以静态配置 Pod 安全准入控制器,以便为未标记的命名空间设置默认的强制执行、审计和/或警告级别。有关更多信息,请参阅配置准入控制器。
5. 禁用 PodSecurityPolicy
最后,您已准备好禁用 PodSecurityPolicy。为此,您需要修改 API 服务器的准入配置:如何关闭准入控制器?。
要验证 PodSecurityPolicy 准入控制器是否不再启用,您可以通过模拟无权访问任何 PodSecurityPolicy 的用户(请参阅PodSecurityPolicy 示例)手动运行测试,或者通过在 API 服务器日志中进行验证。在启动时,API 服务器会输出列出已加载的准入控制器插件的日志行
I0218 00:59:44.903329 13 plugins.go:158] Loaded 16 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,TaintNodesByCondition,Priority,DefaultTolerationSeconds,ExtendedResourceToleration,PersistentVolumeLabel,DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,DefaultIngressClass,MutatingAdmissionWebhook.
I0218 00:59:44.903350 13 plugins.go:161] Loaded 14 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DenyServiceExternalIPs,ValidatingAdmissionWebhook,ResourceQuota.
您应该会看到 `PodSecurity`(在验证准入控制器中),并且两个列表都不应包含 `PodSecurityPolicy`。
一旦您确定 PSP 准入控制器已被禁用(并且经过足够的浸泡时间以确信您不需要回滚),您就可以自由删除您的 PodSecurityPolicy 以及任何相关的角色、集群角色、角色绑定和集群角色绑定(只要确保它们没有授予任何其他不相关的权限)。