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

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

准备工作

你的 Kubernetes 服务器版本必须是 v1.22 或更高。

要检查版本,请输入 kubectl version

如果你当前运行的 Kubernetes 版本不是 1.34,你可能希望切换到查看该页面的 Kubernetes 文档版本,该版本与你实际运行的版本相符。

本页面假设你已经熟悉基本的 Pod 安全准入 概念。

总体方法

从 PodSecurityPolicy 迁移到 Pod 安全准入有多种策略。以下步骤是其中一种可能的迁移路径,目标是最大限度地降低生产中断和安全漏洞的风险。

  1. 决定 Pod 安全准入是否适合你的使用场景。
  2. 审查命名空间权限
  3. 简化和标准化 PodSecurityPolicy
  4. 更新命名空间
    1. 识别适当的 Pod 安全级别
    2. 验证 Pod 安全级别
    3. 强制执行 Pod 安全级别
    4. 绕过 PodSecurityPolicy
  5. 审查命名空间创建过程
  6. 禁用 PodSecurityPolicy

0. 决定 Pod 安全准入是否适合你

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

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

即使 Pod 安全准入不能满足你的所有需求,它也被设计成与其他策略强制机制**互补**,并且可以作为其他准入 Webhook 的有用备用机制运行。

1. 审查命名空间权限

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

2. 简化和标准化 PodSecurityPolicy

在本节中,你将减少可修改的 PodSecurityPolicy 并删除超出 Pod 安全标准范围的选项。你应该对所修改的原始 PodSecurityPolicy 的离线副本进行此处推荐的更改。克隆的 PSP 应该有一个不同的名称,并且在字母顺序上位于原始名称之前(例如,在其前面加上 0)。暂时不要在 Kubernetes 中创建新的策略 - 这将在下面的 推出更新的策略 部分介绍。

2.a. 消除纯粹修改字段

如果 PodSecurityPolicy 正在修改 Pod,那么当最终关闭 PodSecurityPolicy 时,可能会导致 Pod 不符合 Pod 安全级别要求。为了避免这种情况,你应该在切换之前消除所有 PSP 修改。不幸的是,PSP 并未将修改字段和验证字段明确分开,因此这不是一个直接的迁移过程。

你可以从消除纯粹用于修改且与验证策略无关的字段开始。这些字段(也列在 PodSecurityPolicy 映射到 Pod 安全标准 参考中)是:

  • .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 中,后者执行相同的验证而不会进行修改。

2.b. 消除 Pod 安全标准未涵盖的选项

PodSecurityPolicy 中有几个字段未涵盖在 Pod 安全标准中。如果你必须强制执行这些选项,则需要使用准入 Webhook 补充 Pod 安全准入,这超出了本指南的范围。

首先,你可以移除 Pod 安全标准未涵盖的纯验证字段。这些字段(也列在将 PodSecurityPolicy 映射到 Pod 安全标准参考中,并标注“无意见”)是:

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

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

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

剩余的修改字段是正确支持 Pod 安全标准所必需的,稍后需要根据具体情况进行处理

  • .spec.requiredDropCapabilities - 对于 Restricted 配置文件,需要丢弃 ALL
  • .spec.seLinux - (仅在 MustRunAs 规则下可修改)用于强制执行 Baseline 和 Restricted 配置文件的 SELinux 要求。
  • .spec.runAsUser - (在 RunAsAny 规则下非修改性)用于强制执行 Restricted 配置文件的 RunAsNonRoot
  • .spec.allowPrivilegeEscalation - (仅在设置为 false 时可修改)Restricted 配置文件所需。

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. 创建新的 PodSecurityPolicy。如果任何 Role 或 ClusterRole 授予所有 PSP 的 use 权限,这可能会导致使用新的 PSP 而不是其可变对应项。
  4. 更新你的授权以授予对新 PSP 的访问权限。在 RBAC 中,这意味着更新任何授予原始 PSP 的 use 权限的 Role 或 ClusterRole,使其也授予对更新后的 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 正在使用哪些 PSP
    kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
    
  3. 通过现有 Pod - 使用 验证 Pod 安全级别 下的策略,你可以测试 Baseline 和 Restricted 级别,查看它们是否对现有工作负载具有足够的许可性,并选择特权最小的有效级别。

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 轻松回滚更改。只需确保预先存在的 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 以及任何相关的 Role、ClusterRole、RoleBinding 和 ClusterRoleBinding(只需确保它们没有授予任何其他不相关的权限)。

上次修改时间:2023 年 4 月 15 日太平洋标准时间下午 6:38:修正小拼写错误 permision -> permission (5ed63def8a)