服务账号使用最佳实践

服务账号代表非人类用户。它们旨在用于工作负载(例如自定义应用)在没有最终用户参与的情况下访问资源或执行操作的场景。

虽然服务账号是一种有用的工具,但服务账号也可能被滥用,主要有以下几种方式:

  • 提升权限:不法分子可能会通过模拟服务账号而访问他们无权访问的资源。
  • 仿冒:不法分子可能会使用服务账号模拟来模糊其身份。
  • 不可否认性:不法分子可能会使用服务账号代表其执行操作,以此来隐藏其身份和操作。在某些情况下,可能无法跟踪不法分子的这些操作。
  • 信息披露:不法分子可能会从存在的某些服务账号派生有关基础架构、应用或进程的信息。

为帮助保护服务账号,请考虑其双重性质:

  • 由于服务账号是主账号,因此您必须限制其权限,以降低被盗用服务账号可能造成的潜在危害。
  • 由于服务账号是一种资源,因此您必须防止它被盗用。

本指南介绍管理、使用和保护服务账号的最佳实践。

选择何时使用服务账号

并非每种场景都需要服务账号来访问 Google Cloud服务,并且许多场景可以使用比使用服务账号密钥更安全的方法进行身份验证。我们建议您尽可能避免使用服务账号密钥。

当您使用 Google Cloud CLI、Cloud 客户端库、支持应用默认凭据 (ADC) 的工具(如 Terraform 或 REST 请求)访问 Google Cloud 服务时,请使用下图来帮助您选择身份验证方法:

用于根据使用场景选择身份验证方法的决策树

此图将引导您完成以下问题:

  1. 您是否在单用户开发环境(例如您自己的工作站、Cloud Shell 或虚拟桌面界面)中运行代码?
    1. 如果是,请继续回答问题 4。
    2. 如果不是,请继续回答问题 2。
  2. 您是否在 Google Cloud中运行代码?
    1. 如果是,请继续回答问题 3。
    2. 如果不是,请继续回答问题 5。
  3. 您是否在 Google Kubernetes Engine 中运行容器?
    1. 如果是,请使用 Workload Identity Federation for GKE 将服务账号关联到 Kubernetes pod。
    2. 如果不是,请将服务账号附加到资源。
  4. 您的使用场景是否需要服务账号?

    例如,您希望在所有环境中为您的应用一致地配置身份验证和授权。

    1. 如果不是,请使用用户凭据进行身份验证
    2. 如果是,请使用用户凭据模拟服务账号
  5. 您的工作负载是否通过支持工作负载身份联合的外部身份提供方进行身份验证?
    1. 如果是,请配置工作负载身份联合,以允许在本地或其他云提供商上运行的应用使用服务账号。
    2. 如果不是,请创建服务账号密钥

管理服务账号

服务账号与其他类型的主账号的区别不仅在于使用方式,还在于管理方式。以下部分提供了管理服务账号的最佳做法。

将服务账号作为资源管理

个人用户的账号通常根据组织的“joiner-mover-leaver”流程进行管理:员工加入时,系统会为他们创建新的用户账号。当员工调动部门时,其用户账号会更新。当员工离开公司时,他们的用户账号会被暂停或删除。

相比之下,服务账号不与任何特定员工关联。而是最好将服务账号视为资源(属于其他资源或是其他资源的一部分),例如特定虚拟机实例或应用。

要有效地管理服务账号,请不要孤立地看待服务账号,而应在其关联资源的背景中进行考量,并将服务账号及其关联资源作为一个整体进行管理:对服务账号及其关联资源应用相同的进程、相同的生命周期和相同的安全级别,并使用相同的工具进行管理。

创建单一用途的服务账号

在多个应用之间共享一个服务账号可能会导致服务账号的管理复杂化:

  • 这些应用可能有不同的生命周期。如果应用被停用,您可能不清楚服务账号是否可停用,以及是否仍需要服务账号。
  • 随着时间的推移,应用的访问权限要求可能会有所不同。如果应用使用同一个服务账号,您可能需要向服务账号授予越来越多的资源的访问权限,导致增加总体风险。
  • Cloud Audit Logs 包含执行更改或访问数据的服务账号的名称,但不会显示使用该服务账号的应用的名称。如果多个应用共享一个服务账号,您可能无法将活动追溯到正确的应用。

具体而言,一些 Google Cloud 服务(包括 App Engine 和 Compute Engine)会创建默认具有项目的 Editor 角色 (roles/editor) 的默认服务账号。当您创建 Compute Engine 虚拟机 (VM) 实例等资源时,如果您未指定服务账号,则该资源可以自动使用默认服务账号。虽然默认服务账号可让您更轻松地上手,但在多个应用之间共享此类有影响力的服务账号的风险非常高。

您可以采取以下步骤来避免这些情况:

遵循命名和文档惯例

为了帮助跟踪服务与应用或资源之间的关联,请在创建新的服务账号时遵循命名惯例:

  • 为服务账号电子邮件地址添加一个前缀,以标识该账号的使用方式。例如:
    • vm-,表示挂接到虚拟机实例的服务账号。
    • wlifgke-,表示 Workload Identity Federation for GKE 使用的服务账号。
    • wlif-,表示工作负载身份联合使用的服务账号。
    • onprem-,表示本地应用使用的服务账号。
  • 如果虚拟机运行差旅费用应用,则在服务账号电子邮件地址中嵌入该应用的名称,例如 vm-travelexpenses@
  • 使用说明字段添加联系人、指向相关文档的链接或其他说明。

请勿在服务账号的电子邮件地址中嵌入敏感信息或字词。

识别并停用未使用的服务账号

如果不再使用某服务账号,请停用该服务账号。通过停用未使用的服务账号,可以降低服务账号因不法分子水平扩散或权限提升而被滥用的风险。

对于与特定资源(如虚拟机实例)关联的单一用途的服务账号,请在停用或删除关联资源后,立即停用该服务账号。

对于用于多种用途或跨多个资源共享的服务账号,要确定服务账号是否仍在使用则更为困难。在这种情况下,您可以使用活动分析器来查看服务账号的最近身份验证活动。

停用未使用的服务账号,然后再将其删除

如果您删除一个服务账号,然后创建具有相同名称的新服务账号,系统会为新服务账号分配不同的身份。因此,原始 IAM 绑定不会应用于新服务账号。相反,如果停用并重新启用服务账号,所有的 IAM 绑定保持不变。

为避免意外丢失 IAM 绑定,最好不要立即删除服务账号。相反,如果不再需要某个服务账号,请禁用它,并且仅在过一段时间后才将其删除。通过等待删除服务账号,您可以确保在不影响任何 IAM 绑定的情况下安全地移除该服务账号。

您可以删除默认服务账号,例如 App Engine 默认服务账号Compute Engine 默认服务账号。不过,在决定是否删除默认服务账号时,请注意以下事项:

  • 删除默认服务账号有助于提高部署的安全性。不过,如果没有默认服务账号,相应服务就无法自动部署访问其他Google Cloud 的作业,除非您手动配置新服务账号并为其授予适当的角色。
  • 如需重新创建默认服务账号,您必须停用并重新启用相应 API,这可能会破坏您现有的部署。如果您不使用默认服务账号,我们建议您停用它们。

在删除默认服务账号之前,建议先验证您是否在部署中使用该账号。如需详细了解可用于验证服务账号使用情况的工具,请参阅用于了解服务账号使用情况的工具

限制服务账号权限

服务账号是主账号,可以获得对任何其他类型的主账号等资源的访问权限。但是,服务账号通常比其他类型的主账号具有更大的访问权限,可以访问更多资源。此外,当您向应用添加功能时,其服务账号往往会随着时间的推移获得越来越多的访问权限。您可能还会忘记撤消不再需要的访问权限。

不对默认服务账号使用自动角色授予功能

当您首次在 Google Cloud 项目中启用某些 Google Cloud 服务的 API 时,这些服务会创建默认服务账号。根据您的组织政策配置,这些服务账号可能会自动获得 Google Cloud 项目的 Editor 角色 (roles/editor),这样他们可以读取和修改 Google Cloud 项目中的所有资源。授予此角色是为了方便您使用,但这对于服务正常工作并不是必需的:如需访问 Google Cloud 项目中的资源, Google Cloud 服务使用服务代理,而不是默认服务账号。

如需防止默认服务账号自动被授予 Editor 角色,请为您的组织启用停用默认服务账号的自动 IAM 授权 (constraints/iam.automaticIamGrantsForDefaultServiceAccounts) 限制条件。如需将该限制条件应用于多个 Google Cloud 项目,请在文件夹或组织节点上配置该限制条件。应用该限制条件不会从现有默认服务账号中移除 Editor 角色。

如果您应用此限制条件,则新项目中的默认服务账号将无权访问您的 Google Cloud 资源。您必须为默认服务账号授予适当的角色,以便对您的资源进行访问。

将服务账号关联到虚拟机实例时,请勿依赖于访问权限范围

将服务账号附加到虚拟机实例后,您可以指定一个或多个访问权限范围。通过访问权限范围,您可以限制虚拟机可以访问的服务。除了允许政策外,还会应用这些限制。

访问权限范围的粒度较粗。例如,使用 https://www.googleapis.com/auth/devstorage.read_only 范围,您可以限制对 Cloud Storage 只读操作的访问权限,但不能限制对特定存储桶的访问权限。因此,访问权限范围不是适合精细允许政策的替代项。

创建一个专用服务账号并使用精细允许政策来限制服务账号可以访问的资源,而不是依赖于访问权限范围。

如果特定项目、文件夹或组织中的所有当前和未来的服务账号都具有相同的要求,那么您可以使用服务账号主账号集向它们授予角色,而不是使用自定义群组。

如需了解详情,请参阅使用 Google 群组的最佳实践

避免使用全网域授权

全网域授权功能使服务账号能够模拟 Cloud Identity 或 Google Workspace 账号中的任何用户。通过全网域授权功能,服务账号可以在 Google Workspace 和 Cloud Identity 中执行特定管理任务,也可以从 Google Cloud外部访问不支持服务账号的 Google API。

全网域授权功能不会限制服务账号模拟特定用户,但它允许模拟 Cloud Identity 或 Google Workspace 账号中的任何用户(包括超级用户)。因此,允许服务账号使用全网域授权功能可能会使服务账号很容易成为提升权限攻击的目标。

如果您可以直接通过服务账号或使用 OAuth 权限请求流程来完成任务,请避免使用全网域授权功能。例如,如果您需要使用 Google 云端硬盘存储文件,可以直接使用服务账号将文件上传到共享云端硬盘,也可以使用 OAuth 2.0 权限请求流程代表用户上传文件。

如果您无法避免使用全网域授权功能,请限制服务账号可以使用的 OAuth 范围集。虽然 OAuth 范围不会限制服务账号可以模拟的用户,但会限制服务账号可以访问的用户数据类型。

请注意,服务账号无法直接拥有 Google Workspace 中的资源。如果您需要使用 Google 云端硬盘存储文件,而不是使用存储桶,请直接将文件上传到共享云端硬盘、使用 OAuth 2.0 权限请求流程代表用户操作或是使用全网域授权。

使用 Service Account Credentials API 临时提升权限

有些应用仅在特定时间或在特定情况下需要特定资源的访问权限。例如:

  • 应用在启动期间可能需要配置数据的访问权限,但初始化后可能会不需要此访问权限。
  • 监管者应用可能会定期启动后台作业,其中每个作业都有不同的访问权限要求。

在这种情况下,使用单个服务账号并授予其对所有资源的访问权限违反了最小权限原则。这是因为在任何时间点,应用拥有访问权限的资源数量都可能超过实际所需的。

为帮助确保应用的不同部分仅有权访问所需的资源,请使用 Service Account Credentials API 临时提升权限:

  • 为应用或用例的每个部分创建专用服务账号,并只授予服务账号对必要资源的访问权限。
  • 创建另一个充当监管者的服务账号。向监管者服务账号授予其他服务账号的 Service Account Token Creator 角色,以便它可以为这些服务账号请求短期访问令牌。
  • 拆分应用,使应用的一部分充当令牌代理,并且仅让应用的这一部分使用监管者服务账号。
  • 使用令牌代理向应用的其他部分签发短期有效的服务账号。

如需创建短期有效凭据方面的帮助,请参阅为服务账号创建短期有效凭据

使用凭据访问边界来缩小访问令牌权限范围

Google 访问令牌是不记名令牌,这意味着其使用与任何特定应用都无关。如果您的应用将访问令牌传递给其他应用,则其他应用可以按照与您的应用相同的方式来使用令牌。同样,如果访问令牌泄露给不法分子,则他们可以使用该令牌来获取访问权限。

由于访问令牌是不记名令牌,因此您必须防止它们泄露或显示给未经授权方。您可以限制泄露的访问令牌授予访问权限的资源,从而限制该泄露的访问令牌可能造成的潜在损害。此过程称为缩小权限范围。

每当您将访问令牌传递给其他应用或您的应用的不同组件时,使用凭据访问边界可缩小访问令牌权限范围。设置访问边界,以便令牌能够授予足够的对所需资源的访问权限,但不能授予更多。

使用角色建议来确定未使用的权限

首次部署应用时,您可能不确定该应用实际需要的角色和权限。因此,您为该应用的服务账号授予的权限可能在数量上会超出其实际需要的权限。

同样,应用的访问权限要求可能会随着时间的推移而变化,并且您可能不需要最初授予的某些角色和权限。

使用角色建议来确定应用实际使用的权限以及可能未使用的权限。调整受影响资源的允许政策,以确保仅授予应用实际需要的访问权限。

使用横向移动数据分析来限制横向移动

横向移动是指一个项目中的服务账号有权模拟另一个项目中的服务账号。例如,服务账号可能是在项目 A 中创建的,但具有在项目 B 中模拟服务账号的权限。

这些权限可能导致项目之间发生一系列模拟,这些模拟会为主账号授予对资源的意外访问权限。例如,主账号可以模拟项目 A 中的服务账号,然后使用该服务账号来模拟项目 B 中的服务账号。如果项目 B 中的服务账号有权模拟组织中其他项目中的其他服务账号,则主账号可以继续使用服务账号模拟从项目移动到项目,从而在移动时获取权限。

Recommender 提供了横向移动数据分析,可帮助您缓解此问题。横向移动数据分析识别允许一个项目中的服务账号模拟另一个项目中的服务账号的角色。如需了解如何直接查看和管理横向移动数据分析,请参阅管理横向移动数据分析

一些横向移动数据分析与角色建议相关联。您可以应用这些建议,以减少项目间的横向移动。如需了解具体方法,请参阅查看和应用建议

防范提权威胁

未被授予任何角色、无法访问任何资源且未与任何防火墙规则关联的服务账号通常价值有限。为服务账号授予对资源的访问权限之后,服务账号的价值会提高:该服务账号对您更为有用,但它也会更容易成为提升权限攻击的目标。

例如,假设一个对包含敏感信息的 Cloud Storage 存储桶具有完整访问权限的服务账号。在这种情况下,该服务账号实际上与 Cloud Storage 存储桶本身一样重要。不法分子可能会尝试控制该服务账号,而不是试图直接访问存储桶。如果该尝试成功,不法分子可能会通过模拟该服务账号来提升其权限,进而能够访问存储桶中的敏感信息。

涉及服务账号的提升权限方法通常分为以下几个类别:

  • 以服务账号身份进行身份验证:您可能无意中向用户授予模拟服务账号或为服务账号创建服务账号密钥的权限。如果服务账号比用户本身的权限更高,则用户可以以服务账号的身份进行身份验证,以提升其权限并获取用户以其他方式无法访问的资源的访问权限。

  • 使用具有关联服务账号的资源:如果用户有权访问和修改已关联服务账号的 CI/CD 流水线、虚拟机实例或其他自动化系统,则他们或许可以使用这些资源关联的服务账号执行操作。因此,即使他们无权模拟服务账号,他们仍然可以使用服务账号的权限来执行他们无权执行的操作。

    例如,如果用户具有对 Compute Engine 虚拟机实例的 SSH 访问权限,则可以对实例运行代码,以访问实例的关联服务账号可以访问的任何资源。

  • 允许政策、群组或自定义角色修改:无权访问特权服务账号的用户可能仍有权修改服务账号、所属的Google Cloud 项目或文件夹的允许政策。然后,用户可以扩展其中一个允许政策,以(直接或间接)向自己授予以服务账号身份进行身份验证的权限。

以下部分介绍了保护服务账号免遭提升权限威胁的最佳做法。

避免让用户以权限高于用户自身的服务账号身份进行身份验证

通过模拟服务账号,用户可以获取对服务账号可访问的部分或全部资源的访问权限。如果服务账号具有的访问权限比用户广泛,则服务账号的权限实际上高于用户。

授予用户模拟权限更高的服务账号的权限是一种特意让用户提升其权限的方式,这类似于在 Linux 上使用 sudo 工具或在 Windows 上使用进程提升。除非您面对必须临时提升权限的情况,否则最好避免让用户模拟权限更高的服务账号。

用户还可以通过将服务账号与资源关联并在该资源上运行代码来间接获取该服务账号的权限。以这种方式运行代码不是服务账号模拟,因为它只涉及一个经过身份验证的身份(服务账号的身份)。但是,它可以为用户分配他们以其他方式无法获得的访问权限。

使用户能够模拟服务账号或将服务账号与资源关联的权限包括:

  • iam.serviceAccounts.getAccessToken
  • iam.serviceAccounts.getOpenIdToken
  • iam.serviceAccounts.actAs
  • iam.serviceAccounts.implicitDelegation
  • iam.serviceAccounts.signBlob
  • iam.serviceAccounts.signJwt
  • iam.serviceAccountKeys.create
  • deploymentmanager.deployments.create
  • cloudbuild.builds.create

具有其中一些权限的角色包括(但不限于):

  • Owner (roles/owner)
  • Editor (roles/editor)
  • Service Account User (roles/iam.serviceAccountUser)
  • Service Account Token Creator (roles/iam.serviceAccountTokenCreator)
  • Service Account Key Admin (roles/iam.serviceAccountKeyAdmin)
  • Service Account Admin (roles/iam.serviceAccountAdmin)
  • Workload Identity User (roles/iam.workloadIdentityUser)
  • Deployment Manager Editor (roles/deploymentmanager.editor)
  • Cloud Build Editor (roles/cloudbuild.builds.editor)

在将上述任何角色分配给用户之前,请先问自己:

  • 用户通过模拟服务账号可以获取当前 Google Cloud 项目内部和外部的哪些资源的访问权限?
  • 此访问权限级别是否合理?
  • 是否有足够的保护措施来控制用户在哪些情况下可以模拟服务账号?

如果无法确认所有问题,请勿分配角色。而是应考虑为用户提供其他权限较低的服务账号。

避免让用户更改权限更高的服务账号的允许政策

服务账号的允许政策会捕获允许使用或模拟服务账号的用户。拥有特定服务账号的 iam.serviceAccounts.setIamPolicy 权限的用户可以修改或扩展允许政策。包含该权限的角色包括:

  • Owner (roles/owner)
  • Security Admin (roles/iam.securityAdmin)
  • Service Account Admin (roles/iam.serviceAccountAdmin)

具有 iam.serviceAccounts.setIamPolicy 权限的角色可让用户完全控制服务账号:

  • 该用户可以授予自己模拟服务账号的权限,这样便有权访问与服务账号相同的资源。
  • 该用户可以向其他用户授予相同或类似的对该服务账号的访问级别:

在将上述任何角色分配给用户之前,请考虑用户通过模拟服务账号可以获取当前 Google Cloud 项目内部和外部的哪些资源的访问权限。如果服务账号具有的访问权限超出用户,请勿允许用户更改服务账号的允许政策

不允许用户创建或上传服务账号密钥。

借助服务账号密钥,应用或用户可以以服务账号的身份进行身份验证。与其他形式的服务账号模拟不同,使用服务账号密钥不需要以前任何形式的身份验证,即拥有服务账号密钥的任何人都可以使用该密钥。

使用服务账号密钥进行身份验证的实际效果与服务账号模拟的效果类似。如果用户有权访问服务账号密钥,或有权创建新服务账号密钥,则该用户可以以服务账号的身份进行身份验证并访问该服务账号可以访问的所有资源。

创建上传服务账号密钥需要 iam.serviceAccountKeys.create 权限,Service Account Key Admin (roles/iam.serviceAccountKeyAdmin) 和 Service Account Key Editor (roles/editor) 角色具有此权限。

为用户分配包含 iam.serviceAccountKeys.create 权限的任何角色之前,请考虑这样做之后用户可以通过模拟服务账号来获取当前Google Cloud 项目内部和外部哪些资源的访问权限。不允许用户为比其拥有更多权限的服务账号创建服务账号密钥。

如果您的 Google Cloud 项目完全不需要服务账号密钥,请对 Google Cloud 项目或所属文件夹应用