从 PodSecurityPolicy 迁移到内置 PodSecurity Admission Controller

本页介绍了从 PodSecurityPolicies 迁移到内置 PodSecurity 准入控制器的过程。可以使用 dry-run 以及 `audit` 和 `warn` 模式相结合来有效地完成此操作,尽管如果使用变异 PSP,这会变得更加困难。

开始之前

您的 Kubernetes 服务器必须至少为 v1.22 版本。要检查版本,请输入 `kubectl version`。

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

本页假定您已经熟悉基本的 PodSecurityAdmission 概念。

整体方法

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

  1. 确定 PodSecurityAdmission 是否适合您的用例。
  2. 查看命名空间权限
  3. 简化和标准化 PodSecurityPolicies
  4. 更新命名空间
    1. 确定适当的 PodSecurity 等级
    2. 验证 PodSecurity 等级
    3. 强制执行 PodSecurity 等级
    4. 绕过 PodSecurityPolicy
  5. 查看命名空间创建流程
  6. 禁用 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 组控制相关。

  • .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

  1. 识别在原始 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}"
    
  2. 将这些正在运行的吊舱与原始吊舱规范进行比较,以确定 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
  3. 创建新的 PodSecurityPolicies。如果任何角色或集群角色授予了对所有 PSP 的 use 权限,这可能会导致使用新的 PSP 而不是它们对应的 mutating PSP。
  4. 更新您的授权以授予对新 PSP 的访问权限。在 RBAC 中,这意味着更新任何授予原始 PSP use 权限的角色或集群角色,使其也授予更新后的 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 一样严格的级别之间做出决定。您可以使用以下命令查看哪些 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 是非 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 以及任何相关的角色、集群角色、角色绑定和集群角色绑定(只需确保它们不授予任何其他无关的权限)。

上次修改时间:2023 年 4 月 15 日下午 6:38 PST: 修复了轻微的排版错误 permision -> permission (5ed63def8a)