从 PodSecurityPolicy 迁移到内置 PodSecurity Admission Controller
本页介绍了从 PodSecurityPolicies 迁移到内置 PodSecurity 准入控制器的过程。可以使用 dry-run 以及 `audit` 和 `warn` 模式相结合来有效地完成此操作,尽管如果使用变异 PSP,这会变得更加困难。
开始之前
您的 Kubernetes 服务器必须至少为 v1.22 版本。要检查版本,请输入 `kubectl version`。
如果您当前运行的是 Kubernetes 1.31 以外的版本,您可能需要切换到在实际运行的 Kubernetes 版本的文档中查看此页面。
本页假定您已经熟悉基本的 PodSecurityAdmission 概念。
整体方法
您可以采用多种策略从 PodSecurityPolicy 迁移到 PodSecurityAdmission。以下步骤是一种可能的迁移路径,目标是最大程度地减少生产中断和安全漏洞的风险。
- 确定 PodSecurityAdmission 是否适合您的用例。
- 查看命名空间权限
- 简化和标准化 PodSecurityPolicies
- 更新命名空间
- 确定适当的 PodSecurity 等级
- 验证 PodSecurity 等级
- 强制执行 PodSecurity 等级
- 绕过 PodSecurityPolicy
- 查看命名空间创建流程
- 禁用 PodSecurityPolicy
0. 确定 PodSecurityAdmission 是否适合您
PodSecurityAdmission 旨在开箱即用地满足最常见的安全需求,并在集群之间提供一组标准的安全级别。但是,它不如 PodSecurityPolicy 灵活。值得注意的是,以下功能受 PodSecurityPolicy 支持,但不支持 PodSecurityAdmission
- **设置默认安全约束** - PodSecurityAdmission 是一个非变异准入控制器,这意味着它不会在验证吊舱之前修改它们。如果您依赖 PSP 的这方面,您需要修改您的工作负载以满足 PodSecurity 约束,或者使用 变异准入 Webhook 来进行这些更改。有关更多详细信息,请参阅下面的 简化和标准化 PodSecurityPolicies。
- **对策略定义进行细粒度控制** - PodSecurityAdmission 只支持 3 个标准级别。如果您需要对特定约束进行更多控制,那么您需要使用 验证准入 Webhook 来强制执行这些策略。
- **子命名空间策略粒度** - PodSecurityPolicy 允许您将不同的策略绑定到不同的服务帐户或用户,即使在同一个命名空间内也是如此。这种方法有很多缺陷,不建议使用,但如果您确实需要此功能,则需要使用第三方 Webhook。例外情况是,如果您只需要完全豁免特定用户或 RuntimeClasses,在这种情况下,PodSecurityAdmission 确实会公开一些 针对豁免的静态配置。
即使 PodSecurityAdmission 无法满足您的所有需求,它也被设计为与其他策略强制执行机制互补,并且可以提供与其他准入 Webhook 一起运行的有用后备。
1. 查看命名空间权限
PodSecurityAdmission 由 命名空间上的标签 控制。这意味着任何可以更新(或修补或创建)命名空间的人也可以修改该命名空间的 PodSecurity 级别,这可以用来绕过更严格的策略。在继续之前,请确保只有受信任的、特权用户拥有这些命名空间权限。不建议将这些强大的权限授予不应该拥有提升权限的用户,但是如果您必须这样做,则需要使用 准入 Webhook 来对设置命名空间对象上的 PodSecurity 标签施加额外的限制。
2. 简化和标准化 PodSecurityPolicies
在本节中,您将减少变异 PodSecurityPolicies 并删除超出 PodSecurity 标准范围的选项。您应该将此处推荐的更改应用于正在修改的原始 PodSecurityPolicy 的脱机副本。克隆的 PSP 应该具有不同的名称,该名称在字母顺序上位于原始名称之前(例如,在它前面添加一个 `0`)。现在不要在 Kubernetes 中创建新策略 - 这将在下面的 推出更新后的策略 部分中介绍。
2.a. 消除纯粹变异的字段
如果 PodSecurityPolicy 正在变异吊舱,那么当您最终关闭 PodSecurityPolicy 时,您最终可能会得到不符合 PodSecurity 级别要求的吊舱。为了避免这种情况,您应该在切换之前消除所有 PSP 变异。不幸的是,PSP 并没有干净地分离变异和验证字段,因此这不是一个简单的迁移。
您可以从消除纯粹变异的字段开始,这些字段与验证策略无关。这些字段(也在 将 PodSecurityPolicies 映射到 PodSecurity 标准 参考中列出)是
.spec.defaultAllowPrivilegeEscalation
.spec.runtimeClass.defaultRuntimeClassName
.metadata.annotations['seccomp.security.alpha.kubernetes.io/defaultProfileName']
.metadata.annotations['apparmor.security.beta.kubernetes.io/defaultProfileName']
.spec.defaultAddCapabilities
- 虽然从技术上讲是一个变异和验证字段,但这些字段应该合并到 `spec.allowedCapabilities` 中,`spec.allowedCapabilities` 执行相同的验证,但不会进行变异。
注意
删除这些字段可能会导致工作负载缺少必需的配置,并导致问题。有关如何安全地推出这些更改的建议,请参阅下面的 推出更新后的策略。2.b. 消除 PodSecurity 标准未涵盖的选项
PodSecurityPolicy 中有几个字段不受 PodSecurity 标准涵盖。如果您必须强制执行这些选项,则需要使用 准入 Webhook 来补充 PodSecurityAdmission,这超出了本指南的范围。
首先,您可以删除 PodSecurity 标准未涵盖的纯粹验证字段。这些字段(也在 将 PodSecurityPolicies 映射到 PodSecurity 标准 参考中列出,并标注为“无意见”)是
.spec.allowedHostPaths
.spec.allowedFlexVolumes
.spec.allowedCSIDrivers
.spec.forbiddenSysctls
.spec.runtimeClass
您还可以删除以下字段,这些字段与 POSIX/UNIX 组控制相关。
注意
如果这些字段中的任何一个使用 `MustRunAs` 策略,那么它们可能是变异的!删除这些字段可能会导致工作负载未设置必需的组,并导致问题。有关如何安全地推出这些更改的建议,请参阅下面的 推出更新后的策略。.spec.runAsGroup
.spec.supplementalGroups
.spec.fsGroup
其余变异字段是正确支持 PodSecurity 标准所必需的,需要在以后逐个进行处理
.spec.requiredDropCapabilities
- 需要为 Restricted 配置文件删除 `ALL`。.spec.seLinux
- (仅与 `MustRunAs` 规则变异)需要强制执行 Baseline 和 Restricted 配置文件所需的 SELinux 要求。.spec.runAsUser
- (与 `RunAsAny` 规则非变异)需要为 Restricted 配置文件强制执行 `RunAsNonRoot`。.spec.allowPrivilegeEscalation
- (仅在设置为 `false` 时变异)需要为 Restricted 配置文件。
2.c. 推出更新后的 PSPs
接下来,您可以将更新后的策略推出到您的集群中。您应该谨慎行事,因为删除变异选项可能会导致工作负载缺少必需的配置。
对于每个更新的 PodSecurityPolicy
- 识别在原始 PSP 下运行的吊舱。这可以通过使用 `kubernetes.io/psp` 注释来完成。例如,使用 kubectl
PSP_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}"
- 将这些正在运行的吊舱与原始吊舱规范进行比较,以确定 PodSecurityPolicy 是否修改了吊舱。对于由 工作负载资源 创建的吊舱,您可以将吊舱与控制器资源中的 PodTemplate 进行比较。如果发现任何更改,则应使用所需配置更新原始吊舱或 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。 - 更新您的授权以授予对新 PSP 的访问权限。在 RBAC 中,这意味着更新任何授予原始 PSP
use
权限的角色或集群角色,使其也授予更新后的 PSP 此权限。 - 验证:在经过一段时间后,重新运行步骤 1 中的命令,查看是否有任何 Pod 仍在使用原始 PSP。请注意,在新的策略推出后,需要重新创建 Pod 才能完全验证它们。
- (可选) 一旦您验证了原始 PSP 不再使用,您就可以删除它们。
3. 更新命名空间
以下步骤需要在集群中的每个命名空间上执行。这些步骤中引用的命令使用 $NAMESPACE
变量来引用正在更新的命名空间。
3.a. 确定合适的 Pod 安全级别
开始审查 Pod 安全标准 并熟悉 3 个不同的级别。
有几种方法可以为您的命名空间选择 Pod 安全级别
- 根据命名空间的安全要求 - 如果您熟悉命名空间的预期访问级别,您可以根据这些要求选择一个合适的级别,类似于在新的集群上进行操作的方式。
- 根据现有的 PodSecurityPolicies - 使用 将 PodSecurityPolicies 映射到 Pod 安全标准 参考,您可以将每个 PSP 映射到 Pod 安全标准级别。如果您的 PSP 不是基于 Pod 安全标准,您可能需要在选择至少与 PSP 一样宽松的级别和至少与 PSP 一样严格的级别之间做出决定。您可以使用以下命令查看哪些 PSP 在给定命名空间的 Pod 中使用
kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
- 根据现有的 Pod - 使用 验证 Pod 安全级别 下的策略,您可以测试基线和限制级别,以查看它们是否对现有工作负载足够宽松,并选择最少权限的有效级别。
注意
上面的选项 2 和 3 基于现有 Pod,并且可能会错过当前未运行的工作负载,例如 CronJobs、缩放到零的工作负载或尚未推出的其他工作负载。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 会记录在审计日志中,这些日志可以在经过一段时间后进行查看,但不会被禁止。警告模式的工作方式类似,但会立即将警告返回给用户。您可以使用以下命令设置命名空间的审计级别
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 是非 mutating 的,并且 PSP 准入控制器总是优先考虑非 mutating 的 PSP,这将确保此命名空间中的 Pod 不再被 PodSecurityPolicy 修改或限制。
这样在每个命名空间的基础上禁用 PodSecurityPolicy 的优势在于,如果出现问题,您可以通过删除 RoleBinding 很容易地回滚更改。只要确保预先存在的 PodSecurityPolicies 仍然存在!
# Undo PodSecurityPolicy disablement.
kubectl delete -n $NAMESPACE rolebinding disable-psp
4. 检查命名空间创建流程
现在,现有命名空间已更新以强制执行 Pod 安全准入,您应该确保更新您的创建新命名空间的流程和/或策略,以确保将适当的 Pod 安全配置文件应用于新命名空间。
您还可以静态配置 Pod 安全准入控制器,为未标记的命名空间设置默认的强制执行、审计和/或警告级别。有关更多信息,请参阅 配置准入控制器。
5. 禁用 PodSecurityPolicy
最后,您已准备好禁用 PodSecurityPolicy。为此,您需要修改 API 服务器的准入配置:如何关闭准入控制器?.
要验证 PodSecurityPolicy 准入控制器是否不再启用,您可以通过模拟没有访问任何 PodSecurityPolicies 的用户手动运行测试(请参阅 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 准入控制器已禁用(并且经过足够长的时间以确保您不需要回滚),就可以自由删除您的 PodSecurityPolicies 以及任何相关的角色、集群角色、角色绑定和集群角色绑定(只需确保它们不授予任何其他无关的权限)。