使用舰队 Workload Identity 的最佳实践

如您在从舰队工作负载向 API 和服务进行身份验证中所知,舰队级工作负载身份联合是一项强大的舰队功能,可让您更轻松地跨项目为应用设置 Google Cloud 身份验证。但是,除了常规 Workload Identity Federation for GKE 方面的注意事项之外,还有一些访问权限控制注意事项。本指南提供了一些示例来介绍这些潜在问题,以及如何组织舰队以尽可能降低风险。

在阅读本指南之前,您应该熟悉从舰队工作负载向 API 和服务进行身份验证中所述的概念。

如需了解有关采用其他舰队功能的最佳实践,请参阅规划舰队功能

舰队和项目身份池

如需了解为什么需要谨慎采用舰队级工作负载身份联合(尤其是在使用多项目舰队时),请更详细地了解常规适用于 GKE 的工作负载身份联合和舰队工作负载身份联合的工作原理。在这两种情况下,工作负载都会使用集群生成的短期有效令牌进行身份验证,并将每个集群作为身份提供方添加到特殊工作负载身份池中。随后在特定命名空间中运行的工作负载便可以跨集群共享相同的 IAM 身份。

以下是常规 Workload Identity Federation for GKE 在对集群启用后会发生的情况。请注意,默认情况下,系统会为 Autopilot 集群启用适用于 GKE 的工作负载身份联合。

  1. GKE 会在集群的项目中创建 Google 管理的工作负载身份池:PROJECT_ID.svc.goog.id
  2. GKE 会将这些集群作为身份提供方添加到池中。
  3. 因此,在特定命名空间中运行的工作负载会跨项目内的集群共享相同的 IAM 身份。身份采用以下格式:serviceAccount:PROJECT_ID.svc.id.goog[K8S_NAMESPACE/KSA_NAME]

将启用了适用于 GKE 的工作负载身份联合的集群添加到舰队(包括 Autopilot 集群、明确启用该功能的 Standard 集群以及 Google Cloud 外部的 GKE 集群)后,系统会自动启用舰队级工作负载身份联合。

以下是用户向舰队注册启用了适用于 GKE 的工作负载身份联合的集群时发生的情况:

  1. 系统会在舰队宿主项目中创建 Google 管理的舰队级工作负载身份池 FLEET_PROJECT_ID.svc.goog.id(如果该池尚不存在)。这与舰队宿主项目的项目工作负载身份池相同
  2. 集群会作为身份提供方添加到池中。
  3. 因此,在特定命名空间中运行的工作负载会跨舰队内的集群共享相同的 IAM 身份。我们将此称为舰队工作负载身份的隐式相同性。身份采用以下格式:serviceAccount:FLEET_PROJECT_ID.svc.id.goog[K8S_NAMESPACE/KSA_NAME]。不同项目中的舰队工作负载随后可以使用相同的身份调用 Google API 以进行身份验证。

由此可知,如果舰队仅包含来自一个项目的集群,并且这些集群全部注册到舰队,则结果将如同在没有舰队的情况下仅使用适用于 GKE 的工作负载身份联合一样:所有集群都是项目级工作负载身份池中的身份提供方,工作负载使用与适用于 GKE 的工作负载身份联合搭配使用的相同身份。不过,如果舰队在多个项目中具有成员集群,则舰队工作负载身份联合会将每个项目的身份池合并到单个舰队级身份池中(该身份池在舰队宿主项目中托管)。

如以下示例所示,如果项目中的一组集群与该项目中属于舰队成员的一组集群之间只有部分重叠,则可能会出现一些复杂情况。

场景 1:注册了所有集群的单项目舰队

在此场景中,舰队的所有成员集群都在舰队宿主项目中,并且该项目中的所有集群都是舰队的成员。

显示所有集群都在同一舰队中的项目的示意图

如前面部分所述,在此场景中使用舰队级工作负载身份联合与使用常规适用于 GKE 的工作负载身份联合相同,并且没有额外的风险。

场景 2:注册了部分集群的单项目舰队

在此场景中,一个舰队包含两个集群,这两个集群都在舰队宿主项目“项目 1”中。舰队宿主项目还包含不是舰队成员的第三个集群,该集群已启用适用于 GKE 的工作负载身份联合。

显示部分集群在同一舰队中的项目的示意图。

也就是说:

  • GKE 将集群 1、2 和 3 添加到项目工作负载身份池 project-1.svc.goog.id
  • 舰队将集群 1 和集群 2 添加到舰队工作负载身份池,这也是项目工作负载身份池 project-1.svc.goog.id(因为这是舰队宿主项目)。

管理员想要向舰队所有集群中某个命名空间中运行的工作负载授予权限。它们使用 serviceAccount:project-1.svc.goog.id[namespace/ksa] 作为身份来授予访问权限。不过,集群 3(不属于舰队)中该命名空间中的工作负载现在共享相同的访问权限。这是因为集群 3 在项目工作负载身份池中,而该身份池(因为这是舰队宿主项目)与舰队工作负载身份池相同。换句话说,管理员可能只打算向舰队中的集群授予权限,但鉴于舰队工作负载身份联合的实现方式,非舰队集群也可能会获得访问权限。

可能的缓解措施

此处的一种可能解决方案是创建一个专用项目来托管其中没有任何集群的舰队(由容器 API 上的自定义组织政策强制执行)。这样可以将舰队工作负载身份池信任网域与 GKE 项目级信任网域明确分隔。

显示两个项目的示意图,其中一个项目包含集群,另一个充当舰队宿主项目

管理员随后在向工作负载授予权限时可以选择适当的信任网域。例如,他们可以使用 serviceAccount:project-0.svc.goog.id[namespace/ksa] 向整个舰队中的命名空间型工作负载授予权限。非舰队成员集群 3 在此设置中不属于该工作负载身份池的一部分,因此不会获得访问权限。

此解决方案适用于 Google Cloud 上的集群和关联集群。

场景 3:注册了部分集群的多项目舰队

在此场景中,一个舰队具有来自两个项目(项目 1 和项目 2)的成员。

显示其集群来自两个项目的舰队的示意图。

管理员想要使用常规适用于 GKE 的工作负载身份联合,向项目 1 中所有集群中某个命名空间中运行的工作负载授予权限。但是,由于集群 4 注册到舰队,并且项目 1 是舰队宿主项目,因此项目 2 中集群 4 上的工作负载也将获得相同的权限。

可能的缓解措施

与上一个场景一样,此处的可能缓解措施是创建一个不包含集群的专用舰队宿主项目。同样,这使管理员可以在设置访问权限控制时,区分舰队身份池和每个集群的项目身份池中的身份。

场景 4:考虑命名空间相同性

使用工作负载身份联合时,工作负载身份池并不是唯一可能造成混乱的方面。如规划舰队功能中所述,许多舰队功能(包括舰队工作负载身份联合)都假定命名空间相同,以简化整个舰队的配置和管理。这意味着,相应功能会将多个舰队成员集群中具有相同名称的命名空间视为它们是同一命名空间。在此示例中,管理员已向在舰队成员集群“集群 1”和“集群 2”上运行的 NS1 命名空间中的工作负载授予权限。

显示具有相同命名空间的两个项目中的集群的示意图。

但是,用户在其他舰队成员集群上(意外或恶意地)创建了具有相同名称的命名空间。由于命名空间相同性假设,该命名空间中的工作负载会自动获得与集群 1 和集群 2 中的合法 NS1 工作负载相同的权限。

可能的缓解措施

设置权限,以便只有一小组受信任的角色可以在集群中创建命名空间。