了解服务帐号

背景

服务帐号是一种特殊类型的 Google 帐号,用于表示需要验证身份并获得授权以访问 Google API 数据的非人类用户。

通常在以下情况下使用服务帐号:

  • 在虚拟机上运行工作负载。
  • 在本地工作站或调用 Google API 的数据中心上运行工作负载。
  • 运行与真人用户生命周期无关的工作负载。

您的应用会使用服务帐号的身份调用 Google API,这样用户就不必直接参与其中。

管理服务帐号

一旦您决定需要服务帐号,便可以问自己以下问题,以了解您将如何使用服务帐号:

  • 服务帐号可以访问哪些资源?
  • 服务帐号需要什么权限?
  • 采用服务帐号身份的代码将在何处运行:在 Google Cloud Platform 上还是在本地?

使用以下流程图来找出上述问题的答案:

服务帐号流程图

注意,您可以将服务帐号视为资源身份

如果将服务帐号视为身份,您可以向服务帐号授予角色,使其能够访问资源(例如项目)。

如果将服务帐号视为资源,您可以向其他用户授予角色以访问或管理该服务帐号。

为服务帐号授予访问权限

为服务帐号授予访问资源的权限与为任何其他身份授予访问权限类似。例如,如果您有一个在 Google Compute Engine 上运行的应用,您希望该应用只能访问在 Google Cloud Storage 中创建的对象。您可以为该应用创建一个服务帐号,并为其授予 Storage Object Creator 角色。下图演示了此示例:

服务帐号流程图

了解如何为服务帐号授予角色

模拟服务帐号

您可以通过三种方法模拟服务帐号来访问 Google API:

  • 使用 RSA 私钥进行身份验证
  • 使用 Cloud IAM 政策进行授权
  • 在 Google Cloud 服务上部署作业

使用 RSA 私钥进行身份验证

所有服务帐号都具有 Google Cloud 管理的密钥对(会定期轮替),私钥由第三方托管,无法直接访问。

您也可以手动创建用户管理的密钥对。 Google Cloud 会生成私钥/公钥,存储公钥,并向用户提供私钥。密钥对从服务帐号中删除后将无法向 Google 进行身份验证。

使用 Cloud IAM 政策进行授权

所有服务帐号都具有可授予服务帐号访问权限的 Cloud IAM 政策。某些权限使用户可以根据用户凭据模拟或成为服务帐号。

在 Google Cloud 上部署作业

某些 Google Cloud 服务(如 Compute Engine,App Engine 或 Cloud Functions)允许您部署以服务帐号身份运行的作业(例如虚拟机或函数)。

要以这种方式部署作业,必须向服务帐号授予所需服务的任何必要权限,同时还必须为用户帐号授予服务帐号的 iam.serviceAccounts.actAs 权限。此权限包含在 Service Account User 角色中。Google Cloud 服务还必须维护服务帐号的 Cloud IAM 权限,但此任务通常由系统自动执行。

示例

使用服务帐号运行虚拟机

假设您有一个长期运行的作业,您的员工有权启动该作业。当最后启动该作业的员工离开公司时,您不希望该作业终止。

解决此问题的方法是创建一个服务帐号来启动和停止该作业。可按以下步骤进行操作:

  1. 创建服务帐号

  2. 将服务帐号的 Service Account User (roles/iam.serviceAccountUser) 角色授予需要启动该作业的权限的员工。在此场景中,服务帐号被视为资源。

  3. 向该员工授予 Compute Instance Admin (roles/compute.instanceAdmin.v1) 角色。

  4. 现在,员工可以创建运行此服务帐号的 Compute Engine 实例,连接到它们并使用此服务帐号启动该作业。例如:

    gcloud compute instances create my-instance --scopes=cloud-platform \
        --service-account=my-service-account@test9q.iam.gserviceaccount.com \
        --zone=us-central1-a
        

将数据迁移到 Google Cloud

假设您在其他云服务商中进行了一些数据处理,并且希望将处理后的数据转移到 Google Cloud Platform。您可以使用外部云虚拟机中的服务帐号将该数据推送到 Google Cloud Platform。为此,您必须在创建服务帐号时创建并下载服务帐号密钥,然后使用外部流程中的该密钥调用 Cloud Platform API。

跟踪服务帐号

一段时间后,随着您创建越来越多的服务帐号,您可能无法跟踪哪个服务帐号用于何种用途。

服务帐号的显示名称是一种不错的方法,可以捕获有关该服务帐号的其他信息,例如服务帐号的用途或该帐号的联系人。对于新的服务帐号,您可以在创建服务帐号时填充显示名称。对于现有服务帐号,请使用 serviceAccounts.update() 方法修改显示名称。

删除并重新创建服务帐号

删除服务帐号后创建名称相同的新服务帐号,这一操作是可以实现的。但如果您再次使用已删除服务帐号的名称,则可能会导致意外行为。

删除服务帐号时,不会立即删除其角色绑定。如果您创建一个与最近删除的服务帐号同名的新服务帐号,则旧绑定可能仍然存在;但是,它们不会应用于新的服务帐号,即使两个帐号具有相同的电子邮件地址也是如此。出现这种行为的原因是服务帐号在创建时获得了在 Cloud IAM 中的唯一 ID。系统在内部使用这些 ID 授予所有角色绑定,而不是使用服务帐号的电子邮件地址。因此,为已删除的服务帐号存在的任何角色绑定都不会应用于使用相同电子邮件地址的新服务帐号。

为防止出现这种意外行为,请考虑为每个服务帐号使用新的唯一名称。此外,如果您不小心删除了服务帐号,可以尝试恢复删除的服务帐号,而不是创建新的服务帐号。

如果您无法恢复删除的原始服务帐号,并且需要创建具有相同名称和相同角色的新服务帐号,请按以下步骤操作:

  1. 创建新服务帐号
  2. 对于新服务帐号需要访问的每项资源,请撤消针对该资源为原始服务帐号授予的所有角色

  3. 从原始服务帐号撤消角色后,向新服务帐号授予角色

服务帐号的权限

本部分介绍向服务帐号或有权模拟服务帐号的用户帐号授予权限的常见场景:

为服务帐号授予最低权限

您应该只授予服务帐号实现其目标所需的最低权限集。了解如何为服务帐号授予特定资源的角色

为用户授予访问服务帐号的权限时,请注意,用户可以访问服务帐号有权访问的所有资源。因此,请务必谨慎配置服务帐号的权限;即严格限制您的团队中的哪些人可以充当(或模拟)服务帐号。

如果用户具有可以更新 App Engine 和 Compute Engine 实例的 Cloud IAM 角色(例如 App Engine DeployerCompute Instance Admin),则可以作为用于运行这些实例的服务帐号有效运行代码,并间接访问这些服务帐号有权访问的所有资源。同样,对 Compute Engine 实例的 SSH 访问权限也能够作为该实例执行代码。

针对常见场景的服务帐号权限

服务帐号可用于许多不同的场景,每个场景都需要特定权限。本部分介绍了常见场景以及需要哪些权限。

启动长期运行的作业

权限

  • iam.serviceAccounts.actAs

角色

  • roles/editor (Editor)
  • roles/iam.serviceAccountUser (Service Account User)

用户(或服务)可以将服务帐号绑定到长期运行的作业服务。 以下是一些示例:

  • Compute Engine 虚拟机
  • App Engine 应用
  • Cloud Functions 函数
  • Dataflow 作业

在此场景中,用户必须获得部署作业的权限(该权限因具体服务而异),以及模拟服务帐号的权限(通过服务帐号的 iam.serviceAccounts.actAs 授予)。注意,被授予 iam.serviceAccounts.actAs 权限本身并不意味着被授予模拟服务帐号的权限。

在为服务帐号授予 iam.serviceAccounts.actAs 权限后,它们可以启动作为服务帐号运行的长期运行的作业。作业启动后,该用户无需再保留对该服务帐号的访问权限。即使该用户失去了访问权限,该作业仍会继续运行。作业服务会继续使用自己的服务帐号权限,让作业使用该服务帐号身份运行。

注意,有时需要 iam.serviceAccounts.actAs 权限才能更改长期运行的作业(例如在 Compute Engine 虚拟机上设置实例元数据)。

要详细了解此流程,请参阅 Compute Engine 文档中的为实例创建和启用服务帐号

直接模拟服务帐号

权限

  • iam.serviceAccounts.getAccessToken
  • iam.serviceAccounts.signBlob
  • iam.serviceAccounts.signJwt
  • iam.serviceAccounts.implicitDelegation

角色

  • roles/iam.serviceAccountTokenCreator (Service Account Token Creator)

用户(或服务)获得所需权限后,可在几种常见场景下直接模拟(或断言)服务帐号的身份。

首先,用户可以使用 iam.serviceAccounts.getAccessToken 权限并调用 generateAccessToken() 方法来获取服务帐号的短期凭据。使用短期凭据,用户可向 Google Cloud 发出命令,并且可以访问服务帐号有权访问的所有资源。例如,该流程使用户可以使用 gcloud --impersonate-service-account 标志来模拟服务帐号,而无需使用下载的外部服务帐号密钥。

其次,用户可以使用 iam.serviceAccounts.signBlob 权限并通过调用 signBlob()signJwt() 方法获得由服务帐号的 Google 管理的私钥签名的工件。由 Google 管理的私钥始终由第三方托管,且不会直接公开。signBlob() 允许对任意负载签名(例如通过 Cloud Storage 签名的网址),而 signJwt() 仅允许对格式正确的 JWT 签名。

最后,用户可以模拟(或断言)服务帐号,而无需检索服务帐号的凭据。这是一种高级用例,仅支持使用 generateAccessToken() 方法进行编程式访问。在包含至少 3 个服务帐号(即 A、B 和 C)的场景中:如果向服务帐号 A 授予对 B 的 iam.serviceAccounts.implicitDelegation 权限,向 B 授予对 C 的 iam.serviceAccounts.getAccessToken 权限,则服务帐号 A 可以获取服务帐号 C 的访问令牌

生成 OpenID Connect (OIDC) ID 令牌

权限

  • iam.serviceAccounts.getOpenIdToken

角色

  • roles/iam.serviceAccountTokenCreator (Service Account Token Creator)

用户(或服务)可以生成一个与 OpenID Connect (OIDC) 兼容的 JWT 令牌,该令牌由 Google OIDC 提供商 (accounts.google.com) 签名,代表使用 iam.serviceAccounts.getOpenIdToken 权限的服务帐号的身份。

如果您的组织没有部署其他用于授予 Google 访问权限的身份联合系统,则大部分 Google API 都不会直接接受这些令牌。但存在少数例外情况,例如 Identity-Aware Proxy,它允许基于 OIDC 访问用户运行的应用程序。

生成外部私钥

权限

  • iam.serviceAccountKeys.create

角色

  • roles/editor (Editor)
  • roles/iam.serviceAccountAdmin (Service Account Admin)

用户或服务可以生成外部私钥材料 (RSA),RSA 可用作服务帐号直接向 Google 进行身份验证。此密钥材料随后可用于应用默认凭据 (ADC) 库或 gcloud auth activate-service-account 命令。任何获得密钥材料访问权限的用户都可以拥有该服务帐号有权访问的所有资源的完全访问权限。此类私钥材料应受到高度重视,材料存在的时间越长,就越不安全。因此,轮替私钥材料对于保持较高的安全性来说至关重要。

管理服务帐号密钥

有两种类型的服务帐号密钥:

  • GCP 管理的密钥。这些密钥由 Cloud Platform 服务(例如 App Engine 和 Compute Engine)使用。您无法下载它们,它们会自动轮换并用于签名,使用时间最多两周。轮替过程具有概率性;新密钥的使用率随密钥的生命周期先逐渐上升,再逐渐下降。我们建议将服务帐号的公钥集缓存最多 24 小时,以确保您始终可以访问当前的密钥集。

  • 用户管理的密钥。这些密钥由用户创建、下载和管理。它们自创建之日起 10 年内有效,从服务帐号中删除后其身份验证有效性终止。

对于用户管理的密钥,您需要确保具有适当的流程来解决密钥管理需求,例如:

  • 密钥存储
  • 密钥分配
  • 密钥撤消
  • 密钥轮替
  • 防止未经授权用户访问密钥
  • 密钥恢复

任何有权访问服务帐号的有效私钥的人员都可以通过该服务帐号访问资源。注意,密钥访问服务帐号(以及服务帐号可访问的数据)的生命周期与已下载密钥的用户的生命周期无关。

建议开发者不要将密钥签入源代码中或将其留在工作站的“下载内容”目录中。

要增强密钥的安全性,请遵循以下指导:

在 Compute Engine 中使用服务帐号

Compute Engine 实例需要作为服务帐号运行以访问其他 Cloud Platform 资源。要确保您的 Compute Engine 实例的安全性,请考虑以下事项:

  • 您可以使用不同的服务帐号在同一个项目中创建虚拟机。要在创建虚拟机后更改其服务帐号,请使用 instances.setServiceAccount 方法。

  • 您可以将 IAM 角色授予服务帐号以定义它们可以访问的内容。在很多情况下,您不再需要依赖范围。这样,您就可以在不重新创建实例的情况下修改虚拟机服务帐号的权限。

  • 由于实例依赖其服务帐号才有权访问 Cloud Platform 资源,因此请避免在运行中的实例仍在使用服务帐号时将其删除。如果您删除服务帐号,实例就可能会开始无法成功执行其操作。

最佳做法

  • 限制谁可以充当服务帐号。属于服务帐号的服务帐号用户的用户可以间接访问该服务帐号有权访问的所有资源。因此,在向用户授予 serviceAccountUser 角色时要谨慎。

  • 只授予服务帐号实现其目标所需的最低权限集。了解如何为服务帐号授予特定资源的角色

  • 为每项服务创建服务帐号,并仅为其授予该服务所需的权限。

  • 使用服务帐号的显示名称来跟踪服务帐号。在创建服务帐号时,请根据服务帐号的用途填充显示名称。

  • 为您的服务帐号定义一种命名约定。

  • 实施相关流程以自动轮替用户管理的服务帐号密钥。

  • 利用 IAM 服务帐号 API 来实现密钥轮替。

  • 使用 serviceAccount.keys.list() 方法或控制台中的日志查看器页面审核服务帐号和密钥。

  • 请勿删除 App Engine 或 Compute Engine 上运行中的实例正在使用的服务帐号,除非您希望这些应用失去对该服务帐号的访问权限。