服务账号使用最佳实践

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

服务账号与普通用户账号存在以下区别:

  • 服务账号没有密码,不能用于基于浏览器的登录。
  • 服务账号作为属于 Google Cloud 项目的资源创建和管理。相比之下,用户在 Cloud Identity 或 Google Workspace 账号中进行管理。
  • 这些账号专用于 Google Cloud。相比之下,在 Cloud Identity 或 Google Workspace 中管理的用户可跨多个 Google 产品和服务开展工作。
  • 它们即是资源,又是主账号
    • 作为主账号,服务账号可以被授予对资源(例如 Cloud Storage 存储桶)的访问权限。
    • 作为资源,服务账号可以被访问,并且可能会被其他主账号(例如用户或群组)模拟

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

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

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

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

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

选择何时使用服务账号

并非每种场景都需要服务账号来访问 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 或 GKE Enterprise 中运行容器?
    1. 如果是,请使用适用于 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 绑定,最好不要立即删除服务账号。如果不再需要某个服务账号,请在过一段时间后再删除。

切勿删除默认服务账号,例如 App EngineCompute Engine 默认服务账号。这些服务账号只能通过停用并重新启用相应 API 来重新创建,这可能会破坏您现有的部署。如果您不使用默认服务账号,停用它们即可。

限制服务账号权限

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

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

当您首次在 Google Cloud 项目中启用 API 时,某些 Google Cloud 服务会创建默认服务账号。根据您的组织政策配置,这些服务账号可能会自动获得 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 只读操作的访问权限,但不能限制对特定存储桶的访问权限。因此,访问权限范围不是适合精细允许政策的替代项。

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

避免使用群组向服务账号授予资源访问权限

在组织中,多个员工履行类似或重叠的工作职责是很常见,因此需要类似的资源访问权限。借助群组,您可以利用这些类似的访问权限来减少管理开销。

服务账号应供应用使用。多个应用履行相同的职责,因此访问权限要求相同或类似是很少见的情况。相反,应用往往是唯一的,并且每个应用需要访问的资源通常都各不相同。

使用群组为服务账号授予对资源的访问权限可能会导致一些不良后果:

  • 群组数激增,每个群组只包含一个或几个服务账号。
  • 权限蔓延:随着时间的推移,群组被授予越来越多的资源的访问权限,但每个群组成员只需要一部分资源的访问权限。

除非群组的用途明确定义,否则最好避免使用群组;而是直接为服务账号授予所需的资源访问权限。

避免使用全网域授权

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

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

如果您可以直接通过服务账号或使用 OAuth 同意流程来完成任务,请避免使用全网域授权功能。

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

应用可能需要敏感或个人用户数据的访问权限。此类数据示例包括用户的邮箱或日历、存储在 Google 云端硬盘中的文档或包含敏感数据的 BigQuery 数据集。

如果应用执行无人参与的后台任务(例如编制索引或数据泄露防护 (DLP) 扫描),或者最终用户未使用 Google 身份进行身份验证,则使用服务账号访问用户数据是合适的做法。在应用代表最终用户执行操作的所有其他场景中,最好避免使用服务账号。

使用 OAuth 同意流程请求最终用户的同意,而不是使用服务账号访问用户数据(可能会转换主账号)。然后,让应用以最终用户的身份进行操作。使用 OAuth(而非服务账号)有助于确保:

  • 用户可以查看将授予应用对哪些资源的访问权限,并且可以明确表示或拒绝同意。
  • 用户可以随时在我的账号页面上撤消同意。
  • 您无需拥有一个可以不受限制地访问所有用户数据的服务账号。

通过允许应用使用最终用户凭据,您可以延迟对 Google Cloud API 的权限检查。这种方法限制了因编码错误(混淆代理问题)而导致意外公开用户不应有权访问的数据的风险。

使用 IAM Credentials API 提升临时权限

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

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

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

为帮助确保应用的不同部分仅有权访问所需的资源,请使用 IAM 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 项目或所属文件夹应用停用服务账号密钥创建功能停用服务账号密钥上传功能组织政策限制条件。这些限制条件会阻止所有用户创建和上传服务账号密钥,包括具有服务账号的 iam.serviceAccountKeys.create 权限的用户。

请勿在 Google Cloud 项目或文件夹级层向服务账号授予访问权限

服务账号属于资源,是资源层次结构的一部分。因此,您可以在以下任何级层管理服务账号的访问权限:

  • 单个服务账号
  • 所属的 Google Cloud 项目
  • Google Cloud 项目祖先实体中的文件夹
  • 组织节点

在 Google Cloud 项目级层或资源层次结构的更高级层管理访问权限有助于减少管理开销,但也可能会导致过度授权。例如,如果您为用户授予 Google Cloud 项目中的 Service Account Token Creator 角色,则用户可以模拟 Google Cloud 项目中的任何服务账号。能够模拟任何服务账号意味着用户可能会获取这些服务账号可以访问的所有资源的访问权限,包括该 Google Cloud 项目外部的资源。

为避免此类过度授权,请勿在 Google Cloud 项目或文件夹级层管理服务账号的访问权限;而是要分别管理每个服务账号的访问权限。

请勿从关联了特权服务账号的计算资源上受保护程度较低的来源运行代码

当您将服务账号附加到计算资源(例如虚拟机实例或 Cloud Run 应用)后,在该资源上运行的进程可以使用元数据服务器来请求访问令牌和 ID 令牌。这些令牌允许进程以服务账号身份进行身份验证并代表其访问资源。

默认情况下,并非只有特定进程或用户才能访问元数据服务器。相反,在计算资源上执行的任何代码都可以访问元数据服务器并获取访问令牌。此类代码可能包括:

  • 应用的代码。
  • 由最终用户提交的代码(如果您的应用允许任何服务器端脚本评估)。
  • 从远程源代码库读取的代码(如果计算资源属于 CI/CD 系统)。
  • 由 Cloud Storage 存储桶提供的启动和关停脚本
  • 由虚拟机管理器分配的客机政策

如果代码是由用户提交的,或者是从远程存储位置读取的,则您必须确保它可信,并且远程存储位置至少与关联的服务账号一样受保护。如果与服务账号相比,远程存储位置受到妥善保护的效果欠佳,则不法分子可能可以提升其权限。不法分子可以通过将使用服务账号权限的恶意代码注入到该位置来实现此目的。

限制对关联了特权服务账号的虚拟机的 shell 访问权限

一些计算资源支持交互式访问,并使用户能够获取对系统的 shell 访问权限。例如:

  • Compute Engine 可让您使用 SSH 或 RDP 登录虚拟机实例。
  • Google Kubernetes Engine 可让您使用 kubectl exec 在 Kubernetes 容器中运行命令或启动 shell。

如果虚拟机实例关联了特权服务账号,则任何对系统具有 shell 访问权限的用户都可以以该服务账号的身份进行身份验证和访问资源。为防止用户滥用此功能来提升其权限,您必须确保 shell 访问权限至少与关联的服务账号一样受保护。

对于 Linux 实例,您可以使用 OS Login 强制 SSH 访问权限比对关联的服务账号的访问权限更具限制性:如需连接到启用了 OS Login 的虚拟机实例,用户不仅必须有权使用 OS Login,而且还必须具有对关联的服务账号的 iam.serviceAccounts.actAs 权限。

相同级别的访问权限控制不适用于使用基于元数据的密钥的虚拟机实例或 Windows 实例:将 SSH 密钥发布到元数据请求 Windows 凭据需要虚拟机实例元数据的访问权限和关联服务账号的 iam.serviceAccounts.actAs 权限。但是,发布 SSH 密钥或获得 Windows 凭据后,后续登录不需要进行任何其他 IAM 权限检查。

同样,如果虚拟机实例使用自定义 Linux 可插入式身份验证模块进行身份验证,或者虚拟机实例是 Active Directory 域的成员,则无权以服务账号身份进行身份验证的用户可能有权登录。如需了解详情,请参阅在 Google Cloud 上运行 Active Directory 的最佳做法

特别是对于不使用 OS Login 的虚拟机实例,请考虑通过 Identity-Aware Proxy 控制 shell 访问权限。仅向应被允许以虚拟机实例关联的服务账号身份进行身份验证的用户授予 IAP-Secured Tunnel User 角色 (roles/iap.tunnelResourceAccessor)。

将元数据服务器访问权限仅限于所选用户和进程

将服务账号关联到虚拟机实例后,部署在虚拟机上的工作负载可以访问元数据服务器来请求服务账号的令牌。默认情况下,对元数据服务器的访问权限不仅限于虚拟机上的任何特定进程或用户:即使是以低权限用户身份运行的进程(例如,Linux 上的 nobody 或 Windows 上的 LocalService),也可以拥有对元数据服务器的完整访问权限,并且可以获取服务账号的令牌。

如需将元数据服务器访问权限仅限于特定用户,请配置客机操作系统的主机防火墙,以仅允许这些用户打开与元数据服务器的出站连接。

在 Linux 上,您可以使用 --uid-owner--gid-owner 选项来设置仅应用于特定用户或群组的 iptables 规则。在 Windows 上,您可以使用 Set-NetFirewallSecurityFilter 命令来自定义防火墙规则,以将该规则应用于选定的用户或群组。

防范信息泄露威胁

避免在服务账号电子邮件地址中披露机密信息

如需授予服务账号对另一个 Google Cloud 项目中的资源的访问权限,您可以向该资源的允许政策添加角色绑定。与资源本身一样,允许政策是另一个 Google Cloud 项目的一部分,因此其可见性也由另一个 Google Cloud 项目控制。

查看允许政策通常不被认为是一种特权操作。许多角色都具有所需的 *.getIamPolicy 权限,包括基本的 Viewer 角色。

可以查看允许政策的用户也可以查看已被授予该资源访问权限的主账号的电子邮件地址。对于服务账号,电子邮件地址可向不法分子提供提示。

例如,允许政策可能包含针对电子邮件地址为 jenkins@deployment-project-123.iam.gserviceaccount.com 的服务账号的绑定。对于不法分子,此电子邮件地址不仅会显示 ID 为 deployment-project-123 的 Google Cloud 项目,还会显示 Google Cloud 项目运行 Jenkins 服务器。选择更通用的名称(如 deployer@deployment-project-123.iam.gserviceaccount.com),您可以避免披露有关 deployment-project-123 中运行的软件类型的信息。

如果您向服务账号授予访问控制不太严格的 Google Cloud 项目中的资源(例如沙盒或开发 Google Cloud 项目)的访问权限,请确保服务账号的电子邮件地址不会披露任何信息。尤其是请勿披露机密信息或可能为攻击者提供提示的信息。

防范不可否认性威胁

每当您发现影响 Google Cloud 中某项资源的可疑活动时,Cloud Audit Logs 是非常重要的信息来源,可了解该活动发生的时间以及参与的用户。

每当 Cloud Audit Logs 指示服务账号已执行某活动时,仅凭此信息可能不足以重建完整的事件链:您还必须能够了解哪个用户或应用导致服务账号执行此活动。

此部分包含最佳做法,可帮助您保留不可否的审核跟踪记录。

只有在没有可行的替代方案时,才使用服务账号密钥

如果您无法使用更安全的身份验证方法,则可能需要为应用创建服务账号密钥。但是,使用服务账号密钥进行身份验证会产生不可否认的威胁。Cloud Audit Logs 会在服务账号修改资源时创建日志,但如果该服务账号使用服务账号密钥进行身份验证,则没有可靠的方法可以知道谁使用了密钥。相比之下,通过使用用户凭据模拟服务账号来以服务账号身份进行身份验证,系统会记录充当服务账号的主账号。

我们建议通过将停用服务账号密钥创建组织政策限制条件应用于 Google Cloud 项目或所属的文件夹,防止创建服务账号密钥。如果在任何建议的替代方案都无法处理的场景下,您必须使用服务账号密钥,请为政策限制条件授予例外,使其范围尽可能具体,并查看管理服务账号密钥的最佳做法

为 IAM API 启用数据访问日志

为了帮助您识别和了解服务账号模拟场景,Cloud Engine 等服务在 Cloud Audit Logs 中包含 serviceAccountDelegationInfo 部分。本部分指示了服务账号是否被模拟以及被哪个用户模拟

并非所有服务都在其 Cloud Audit Logs 中包含模拟详细信息。如需记录所有模拟事件,您还必须为以下 API 启用数据访问日志

  • 包含服务账号的所有 Google Cloud 项目中的 Identity and Access Management (IAM) API
  • 包含工作负载身份池的所有 Google Cloud 项目中的 Security Token Service API

启用这些日志,可确保每次在用户请求服务账号的访问令牌或 ID 令牌时,都会向 Cloud Audit Logs 中添加一个条目。

确保 CI/CD 历史记录可与 Cloud Audit Logs 相关联

在成功验证代码更改并批准部署后,CI/CD 系统通常会使用服务账号执行部署。通常,CI/CD 系统会保留进行部署的事件的历史记录。此历史记录可能包含相应代码审核、提交和流水线运行的 ID,以及有关部署批准者的信息。

如果部署修改了 Google Cloud 中的任何资源,则相关资源的 Cloud Audit Logs 会跟踪这些更改。Cloud Audit Logs 包含有关发起更改的用户或服务账号的信息。但是,在由 CI/CD 系统触发的部署中,服务账号本身通常不足以重建引起更改的整个事件链。

如需在 CI/CD 系统和 Google Cloud 中建立一致的审核跟踪,您必须确保 Cloud Audit Logs 记录可与 CI/CD 系统历史记录中的事件相关联。如果您在 Cloud Audit Logs 中遇到意外事件,则可以使用这种关联性来确定更改是否确实由 CI/CD 系统执行、为何执行以及由谁批准。

用于建立 Cloud Audit Logs 记录与 CI/CD 系统历史记录中事件之间关联的方法包括:

  • 记录 CI/CD 流水线每次运行时执行的 API 请求。
  • 每当 API 返回操作 ID 时,在 CI/CD 系统日志中记录该 ID。
  • 向 API 请求添加 X-Goog-Request-Reason HTTP 标头并传递 CI/CD 流水线运行的 ID。如果您指定请求原因,Terraform 可以自动添加此标头。

    或者,将信息嵌入到 User-Agent 标头中,以将其捕获到 Cloud Audit Logs 中。

为帮助确保不可否认性,请配置日志文件并提交历史记录,使其是不可变的,并且不法分子无法追溯性地隐藏其跟踪记录。

为应用的单个用户创建自定义日志条目

对于用户使用自定义身份验证方案进行身份验证,然后间接访问 Google Cloud 应用的应用,服务账号也适用。这些应用可以确认用户已经过身份验证和授权,然后使用服务账号向 Google Cloud 服务和资源进行身份验证。但是,Cloud Audit Logs 将记录服务账号访问了资源,而不是哪位用户在使用您的应用。

为了帮助将访问追溯到用户,请设计应用逻辑,在每次用户访问资源时写入自定义日志条目,并将自定义日志条目与 Cloud Audit Logs 关联。

后续步骤