从 PodSecurityPolicy 迁移到内置的 PodSecurity 准入控制器

此页面描述了从 PodSecurityPolicies 迁移到内置的 PodSecurity 准入控制器的过程。这可以通过结合使用试运行以及 auditwarn 模式来有效地完成,尽管如果使用 mutating PSP,这会变得更加困难。

开始之前

您的 Kubernetes 服务器必须是 v1.22 或更高版本。要检查版本,请输入 kubectl version

如果您当前运行的 Kubernetes 版本不是 1.32,您可能需要切换到查看您实际运行的 Kubernetes 版本的文档中的此页面。

此页面假定您已经熟悉基本的Pod 安全准入概念。

总体方法

您可以采用多种策略从 PodSecurityPolicy 迁移到 Pod Security Admission。以下步骤是一种可能的迁移路径,其目标是最大限度地减少生产中断和安全漏洞的风险。

  1. 确定 Pod Security Admission 是否适合您的用例。
  2. 审查命名空间权限
  3. 简化和标准化 PodSecurityPolicies
  4. 更新命名空间
    1. 确定合适的 Pod 安全级别
    2. 验证 Pod 安全级别
    3. 强制执行 Pod 安全级别
    4. 绕过 PodSecurityPolicy
  5. 审查命名空间创建过程
  6. 禁用 PodSecurityPolicy

0. 确定 Pod Security Admission 是否适合您

Pod Security Admission 旨在满足开箱即用的最常见安全需求,并在集群中提供一组标准的安全性级别。但是,它的灵活性不如 PodSecurityPolicy。值得注意的是,PodSecurityPolicy 支持以下功能,但 Pod Security Admission 不支持

  • 设置默认安全约束 - Pod Security Admission 是一个非 mutating 的准入控制器,这意味着它不会在验证 Pod 之前修改它们。如果您依赖于 PSP 的这一方面,则需要修改您的工作负载以满足 Pod 安全约束,或者使用Mutating Admission Webhook进行这些更改。有关更多详细信息,请参阅下面的简化和标准化 PodSecurityPolicies
  • 对策略定义进行细粒度控制 - Pod Security Admission 仅支持 3 个标准级别。如果您需要对特定约束进行更多控制,则需要使用Validating Admission Webhook来强制执行这些策略。
  • 子命名空间策略粒度 - PodSecurityPolicy 允许您将不同的策略绑定到不同的 Service Account 或用户,即使在单个命名空间内也是如此。此方法有很多缺陷,不建议使用,但如果您仍然需要此功能,则需要改用第三方 Webhook。这种情况的例外是,如果您只需要完全豁免特定用户或RuntimeClasses,在这种情况下,Pod Security Admission 确实公开了一些用于豁免的静态配置

即使 Pod Security Admission 不能满足您的所有需求,它也被设计为其他策略执行机制的补充,并且可以与其他准入 Webhook 一起提供有用的回退。

1. 审查命名空间权限

Pod Security Admission 由命名空间上的标签控制。这意味着任何可以更新(或修补或创建)命名空间的人也可以修改该命名空间的 Pod 安全级别,这可用于绕过更严格的策略。在继续操作之前,请确保只有受信任的特权用户才具有这些命名空间权限。不建议将这些强大的权限授予不应具有提升权限的用户,但如果必须这样做,则需要使用准入 Webhook来对在命名空间对象上设置 Pod 安全标签施加额外限制。

2. 简化和标准化 PodSecurityPolicies

在本节中,您将减少 mutating PodSecurityPolicies 并删除超出 Pod Security Standards 范围的选项。您应该对正在修改的原始 PodSecurityPolicy 的脱机副本进行此处建议的更改。克隆的 PSP 应该有一个与原始名称不同的名称(例如,在其前面加上一个 0)。请勿在 Kubernetes 中创建新策略 - 这将在下面的推出更新的策略部分中介绍。

2.a. 消除纯粹 mutating 的字段

如果 PodSecurityPolicy 正在 mutating Pod,那么当您最终关闭 PodSecurityPolicy 时,您最终可能会得到不符合 Pod 安全级别要求的 Pod。为了避免这种情况,您应该在切换之前消除所有 PSP mutation。不幸的是,PSP 没有干净地分离 mutating 和验证字段,因此这不是一个简单的迁移。

您可以先消除纯粹 mutating 的字段,这些字段与验证策略没有任何关系。这些字段(也在将 PodSecurityPolicies 映射到 Pod Security Standards参考中列出)是

  • .spec.defaultAllowPrivilegeEscalation
  • .spec.runtimeClass.defaultRuntimeClassName
  • .metadata.annotations['seccomp.security.alpha.kubernetes.io/defaultProfileName']
  • .metadata.annotations['apparmor.security.beta.kubernetes.io/defaultProfileName']
  • .spec.defaultAddCapabilities - 虽然在技术上是一个 mutating 和验证字段,但这些应合并到 .spec.allowedCapabilities 中,后者执行相同的验证而无需 mutation。

2.b. 消除 Pod Security Standards 未涵盖的选项

PodSecurityPolicy 中有几个 Pod Security Standards 未涵盖的字段。如果您必须强制执行这些选项,则需要使用准入 Webhook补充 Pod Security Admission,这不在本指南的范围之内。

首先,您可以移除 Pod 安全标准未涵盖的纯粹验证字段。这些字段(也在将 PodSecurityPolicies 映射到 Pod 安全标准参考中列出,并标记为“无意见”)包括:

  • .spec.allowedHostPaths
  • .spec.allowedFlexVolumes
  • .spec.allowedCSIDrivers
  • .spec.forbiddenSysctls
  • .spec.runtimeClass

您还可以移除以下与 POSIX / UNIX 组控制相关的字段。

  • .spec.runAsGroup
  • .spec.supplementalGroups
  • .spec.fsGroup

剩余的变更字段是正确支持 Pod 安全标准所必需的,稍后需要逐个处理。

  • .spec.requiredDropCapabilities - 对于受限配置文件,需要删除 ALL
  • .spec.seLinux - (仅在 MustRunAs 规则下发生变更)需要强制执行基线和受限配置文件的 SELinux 要求。
  • .spec.runAsUser - (在 RunAsAny 规则下不发生变更)需要强制执行受限配置文件的 RunAsNonRoot
  • .spec.allowPrivilegeEscalation - (仅在设置为 false 时发生变更)受限配置文件需要此项。

2.c. 推出更新的 PSP

接下来,您可以将更新的策略推出到您的集群。您应该谨慎操作,因为移除变更选项可能会导致工作负载缺少所需的配置。

对于每个更新的 PodSecurityPolicy:

  1. 识别在原始 PSP 下运行的 Pod。可以使用 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}"
    
  2. 将这些正在运行的 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
  3. 创建新的 PodSecurityPolicies。如果任何 Roles 或 ClusterRoles 授予所有 PSP 的 use 权限,这可能会导致新的 PSP 被使用,而不是它们发生变更的对应项。
  4. 更新您的授权,以授予对新 PSP 的访问权限。在 RBAC 中,这意味着更新任何授予原始 PSP use 权限的 Roles 或 ClusterRoles,使其也授予更新后的 PSP。
  5. 验证:经过一段时间的浸泡后,重新运行步骤 1 中的命令,以查看是否仍有任何 Pod 使用原始 PSP。请注意,Pod 需要在新策略推出后重新创建,才能完全验证。
  6. (可选)一旦您验证原始 PSP 不再使用,您可以删除它们。

3. 更新命名空间

以下步骤需要在集群中的每个命名空间上执行。这些步骤中引用的命令使用 $NAMESPACE 变量来引用正在更新的命名空间。

3.a. 确定合适的 Pod 安全级别

开始查看Pod 安全标准,并熟悉 3 个不同的级别。

有几种方法可以为您的命名空间选择 Pod 安全级别:

  1. 按命名空间的安全要求 - 如果您熟悉命名空间的预期访问级别,则可以根据这些要求选择合适的级别,类似于在新集群上处理此问题的方式。
  2. 按现有的 PodSecurityPolicies - 使用将 PodSecurityPolicies 映射到 Pod 安全标准参考,您可以将每个 PSP 映射到 Pod 安全标准级别。如果您的 PSP 不是基于 Pod 安全标准,您可能需要在选择一个至少与 PSP 一样宽松的级别和一个至少一样严格的级别之间进行选择。您可以使用此命令查看给定命名空间中哪些 PSP 用于 Pod:
    kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
    
  3. 按现有 Pod - 使用验证 Pod 安全级别下的策略,您可以测试基线和受限级别,以查看它们对于现有工作负载是否足够宽松,并选择最低权限的有效级别。

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 是非变更的,并且 PSP 准入控制器始终首选非变更的 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 以及任何相关的 Roles、ClusterRoles、RoleBindings 和 ClusterRoleBindings(只需确保它们不授予任何其他不相关的权限)。

上次修改时间为 2023 年 4 月 15 日下午 6:38 PST:修复 minor typo permision -> permission (5ed63def8a)