配置基于资源的访问权限

本主题介绍如何在允许政策中使用条件角色绑定来管理对特定资源的访问权限。通过在条件表达式中使用资源特性,您可以根据资源名称、资源类型和/或 Google Cloud 服务,在授权时仅授予角色绑定访问权限中的某个子集。

准备工作

  • 请参阅 Identity and Access Management (IAM) Conditions 概览,了解 IAM 条件角色绑定的基础知识。
  • 查看可在条件表达式中使用的资源特性
  • 资源名称特性可控制对以下 Google Cloud 服务的访问权限:
    • Apigee
    • 应用集成
    • 备份和灾难恢复服务
    • BigQuery
    • BigQuery Reservation API
    • Bigtable
    • Binary Authorization
    • Cloud Deploy
    • Cloud Key Management Service
    • Cloud Logging
    • Cloud SQL
    • Cloud Storage
    • Compute Engine
    • Dataform
    • Google Kubernetes Engine
    • 集成连接器
    • Google Cloud Managed Service for Apache Kafka
    • Pub/Sub Lite
    • Secret Manager
    • Spanner

所需的角色

如需获得管理条件角色绑定所需的权限,请让您的管理员为您授予以下 IAM 角色:

  • 项目的 Project IAM Admin (roles/resourcemanager.projectIamAdmin)(如需管理对项目的访问权限)
  • 文件夹的 Folder Admin (roles/resourcemanager.folderAdmin)(如需管理对文件夹的访问权限)
  • 组织的 Organization Admin (roles/resourcemanager.organizationAdmin)(如需管理对项目、文件夹和组织的访问权限)
  • 您要管理对其资源的访问权限的项目、文件夹或组织的 Security Admin (roles/iam.securityAdmin)(如需管理对几乎所有 Google Cloud 资源的访问权限)

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

这些预定义角色包含管理条件角色绑定所需的权限。如需查看所需的确切权限,请展开所需权限部分:

所需权限

必须拥有以下权限才能管理条件角色绑定:

  • 如需管理项目访问权限:
    • 针对项目的 resourcemanager.projects.getIamPolicy 权限
    • 针对项目的 resourcemanager.projects.setIamPolicy 权限
  • 如需管理文件夹访问权限:
    • 针对文件夹的 resourcemanager.folders.getIamPolicy 权限
    • 针对文件夹的 resourcemanager.folders.setIamPolicy 权限
  • 如需管理组织访问权限:
    • 针对组织的 resourcemanager.organizations.getIamPolicy 权限
    • 针对组织的 resourcemanager.organizations.setIamPolicy 权限

您也可以使用自定义角色或其他预定义角色来获取这些权限。

根据资源名称前缀授予对一组资源的访问权限

条件角色绑定可用于向主账号授予对资源名称与某个前缀匹配的资源的访问权限,例如名称以特定字符串开头的 Compute Engine 虚拟机实例。资源名称前缀通常用于对用于特定功能或具有特定属性的资源进行分组。

请考虑以下示例:软件公司 ExampleCo 在可能处理敏感医疗保健数据的特定虚拟机实例上运行工作负载。其他非敏感工作负载必须在同一个项目中运行,且 ExampleCo 希望确保其开发者对处理敏感数据的虚拟机实例拥有有限的访问权限。为实现此目的,数据敏感型虚拟机实例名称中使用了 sensitiveAccess 前缀,其他虚拟机实例名称中则使用 devAccess 前缀。随后,他们使用条件角色绑定来确保开发者可以在使用普通 devAccess 虚拟机实例时保持高效,但不向其授予对 sensitiveAccess 虚拟机实例的访问权限。

虽然仅使用 resource.name 条件特性可以管理访问权限,不过使用 resource.typeresource.service 特性的情况也很常见。使用这些附加特性时,您可以降低一个条件对于名称相似但类型不同的资源的访问权限产生影响的可能性。 本部分中的示例将 resource.nameresource.type 特性同时使用来控制访问权限。

要根据名称前缀授予对一个项目中的 Compute Engine 磁盘和实例的访问权限,请按照以下说明操作:

控制台

  1. 在 Google Cloud 控制台中,前往 IAM 页面。

    转到 IAM 页面

  2. 从主账号列表中,找到所需主账号,然后点击 按钮。

  3. 修改权限面板中,找到要为其配置条件的目标角色。然后在 IAM 条件(可选)下,点击添加 IAM 条件

  4. 修改条件面板中,输入条件的标题和选填性说明。

  5. 您可以使用条件构建器条件编辑器添加条件表达式。条件构建器提供一个交互式界面,用于选择所需的条件类型、运算符以及有关表达式的其他适用详情。条件编辑器提供基于文本的界面,可使用 CEL 语法手动输入表达式。

    条件构建器

    1. 点击添加下拉菜单,然后点击已分组条件
    2. 条件类型下拉菜单中,依次选择资源 > 类型
    3. 运算符下拉菜单中,选择
    4. 资源类型下拉列表中,选择 compute.googleapis.com/Disk
    5. 点击刚刚输入的条件正下方的第一个添加按钮,为表达式添加另一个子句。
    6. 条件类型下拉菜单中,依次选择资源 > 名称
    7. 运算符下拉菜单中,选择开头为
    8. 字段中,以适当的格式输入资源名称,例如,为名称以 devAccess 开头的磁盘输入 projects/project-123/zones/us-central1-a/disks/devAccess
    9. 点击各个条件类型左侧的,确保这两个子句均为 true。
    10. 点击保存按钮正上方的添加按钮,添加另一组条件。
    11. 条件类型下拉菜单中,依次选择资源 > 类型
    12. 运算符下拉菜单中,选择
    13. 资源类型下拉列表中,选择 compute.googleapis.com/Instance
    14. 点击刚刚输入的条件正下方的第一个添加按钮,为表达式添加另一个子句。
    15. 条件类型下拉菜单中,依次选择资源 > 名称
    16. 运算符下拉菜单中,选择开头为
    17. 字段中,以适当的格式输入资源名称,例如,为名称以 devAccess 开头的实例输入 projects/project-123/zones/us-central1-a/instances/devAccess
    18. 点击各个条件类型左侧的,确保这两个子句均为 true。
    19. 点击保存按钮正上方的添加按钮,添加第三组条件。
    20. 要确保此条件不会影响其他资源,请同时添加以下子句:在条件类型下拉菜单中,依次选择资源 > 类型
    21. 运算符下拉菜单中,选择不是
    22. 资源类型下拉列表中,选择 compute.googleapis.com/Disk
    23. 点击刚刚输入的条件正下方的第一个添加按钮,为表达式添加另一个子句。
    24. 条件类型下拉菜单中,依次选择资源 > 类型
    25. 运算符下拉菜单中,选择不是
    26. 资源类型下拉列表中,选择 compute.googleapis.com/Instance
    27. 点击各个条件类型左侧的,确保这两个子句均为 true。

      完成后,条件构建器应如下所示:

    28. 点击保存以应用条件。

    29. 关闭修改条件面板后,在修改权限面板中再次点击保存,以更新允许政策。

    条件编辑器

    1. 点击条件编辑器标签页并输入以下表达式:

      (resource.type == "compute.googleapis.com/Disk" &&
      resource.name.startsWith("projects/project-123/regions/us-central1/disks/devAccess")) ||
      (resource.type == "compute.googleapis.com/Instance" &&
      resource.name.startsWith("projects/project-123/zones/us-central1-a/instances/devAccess")) ||
      (resource.type != "compute.googleapis.com/Disk" &&
      resource.type != "compute.googleapis.com/Instance")
    2. 输入表达式后,您可以视需要选择对 CEL 语法执行 lint 请求,方法是点击右上方文本框上方的运行 Linter

    3. 点击保存以应用条件。

    4. 关闭修改条件面板后,在修改权限面板中再次点击保存,以更新允许政策。

gcloud

允许政策是使用读取-修改-写入模式设置的。

执行 gcloud projects get-iam-policy 命令以获取项目的当前允许政策。在以下示例中,将允许政策的 JSON 版本下载到磁盘上的路径中。

命令:

gcloud projects get-iam-policy project-id --format=json > filepath

下载允许政策的 JSON 格式:

{
  "bindings": [
    {
      "members": [
        "user:my-user@example.com"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "group:my-group@example.com"
      ],
      "role": "roles/compute.instanceAdmin"
    }
  ],
  "etag": "BwWKmjvelug=",
  "version": 1
}

要配置带有资源名称前缀条件的允许政策,请添加以下突出显示的条件表达式。gcloud CLI 会自动更新版本:

{
  "bindings": [
    {
      "members": [
        "user:my-user@example.com"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "group:my-group@example.com"
      ],
      "role": "roles/compute.instanceAdmin",
      "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
      }
    }
  ],
  "etag": "BwWKmjvelug=",
  "version": 3
}

接下来,通过执行 gcloud projects set-iam-policy 命令设置新的允许政策:

gcloud projects set-iam-policy project-id filepath

新的条件角色绑定会通过以下方式授予群组权限:

  • 仅当资源名称以 devAccess 开头时,才授予所有磁盘和实例权限

  • 对于所有其他资源类型,均授予 Instance Admin 角色的所有其他权限

REST

使用读取-修改-写入模式以允许访问特定资源。

首先,读取项目的允许政策

Resource Manager API 的 projects.getIamPolicy 方法可获取项目的允许政策。

在使用任何请求数据之前,请先进行以下替换:

  • PROJECT_ID:您的 Google Cloud 项目 ID。项目 ID 是字母数字字符串,例如 my-project
  • POLICY_VERSION:要返回的政策版本。请求应指定最新的政策版本,即政策版本 3。如需了解详情,请参阅在获取政策时指定政策版本

HTTP 方法和网址:

POST https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID:getIamPolicy

请求 JSON 正文:

{
  "options": {
    "requestedPolicyVersion": POLICY_VERSION
  }
}

如需发送您的请求,请展开以下选项之一:

您应该收到类似以下内容的 JSON 响应:

{
  "version": 1,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "role": "roles/owner",
      "members": [
        "user:my-user@example.com
      ]
    },
    {
      "members": [
        "group:my-group@example.com"
      ],
      "role": "roles/compute.instanceAdmin"
    }
  ]
}

接下来,修改允许政策以使其允许访问特定资源。请务必将 version 字段更改为值 3

{
  "version": 3,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "role": "roles/owner",
      "members": [
        "user:my-user@example.com"
      ]
    },
    {
      "role": "roles/compute.instanceAdmin",
      "members": [
        "group:my-group@example.com"
      ],
      "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
      }
    }
  ]
}

最后,写入更新后的允许政策

Resource Manager API 的 projects.setIamPolicy 方法会将请求中的允许政策设置为项目的新允许政策。

在使用任何请求数据之前,请先进行以下替换:

  • PROJECT_ID:您的 Google Cloud 项目 ID。项目 ID 是字母数字字符串,例如 my-project

HTTP 方法和网址:

POST https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID:setIamPolicy

请求 JSON 正文:

{
  "policy": {
    "version": 3,
    "etag": "BwWKmjvelug=",
    "bindings": [
      {
        "role": "roles/owner",
        "members": [
          "user:my-user@example.com"
        ]
      },
      {
        "role": "roles/compute.instanceAdmin",
        "members": [
          "group:my-group@example.com"
        ],
        "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
        }
      }
    ]
  }
}

如需发送您的请求,请展开以下选项之一:

响应包含更新后的允许政策。


从资源名称中提取值

前面的示例展示了资源名称或资源名称的开头部分与其他值之间的逻辑比较(结果为布尔值)。但在某些情况下,您可能需要将某个值与资源名称中不在名称开头的特定部分进行比较。

您可以使用 extract() 函数并指定提取模板,以字符串形式提取资源名称的相关部分。如有必要,您可以将提取的字符串转换为其他类型,例如时间戳。从资源名称中提取值后,您可以将提取的值与其他值进行比较。

以下示例展示了使用 extract() 函数的条件表达式。如需详细了解 extract() 函数,请参阅 IAM Conditions 特性参考文档

示例:匹配过去 30 天内的订单

假设您将订单信息存储在多个 Cloud Storage 存储桶中,并且每个存储桶中的对象均按日期进行组织。典型的对象名称可能类似于以下示例:

projects/_/buckets/acme-orders-aaa/objects/data_lake/orders/order_date=2019-11-03/aef87g87ae0876

您希望允许主账号访问过去 30 天内的任何订单。以下条件与这些订单的 Cloud Storage 对象匹配。它使用 duration()date() 函数从请求时间中减去 30 天(2,592,000 秒),然后将该时间戳与订单日期进行比较:

resource.type == 'storage.googleapis.com/Object' &&
  request.time - duration('2592000s') < date(resource.name.extract('/order_date={date_str}/'))

如需详细了解 date()duration() 函数,请参阅日期/时间特性参考文档

示例:匹配任何位置的 Compute Engine 虚拟机

假设您想向名称以 dev- 开头的任何 Compute Engine 虚拟机的主账号授予项目级角色,而不管虚拟机的位置如何。您还希望主账号能够将该角色用于所有其他资源类型。

虚拟机的资源名称的格式类似于 projects/project-id/zones/zone-id/instances/instance-name。对于实例名称以字符串 dev- 开头的虚拟机以及除虚拟机以外的所有资源类型,以下条件的计算结果为 true

resource.type != 'compute.googleapis.com/Instance' ||
  resource.name.extract('/instances/{name}').startsWith('dev-')

大括号中的文本标识为执行比较而从资源名称中提取的部分。在此示例中,提取模板提取第一处出现的字符串 /instances/ 后的所有字符。

基于资源的条件的重要使用注意事项

添加基于资源的条件时,请务必考虑条件将如何影响主账号的权限。

自定义角色

请考虑以下示例,该示例涉及到自定义角色。管理员想要创建一个自定义角色来授予创建虚拟机实例的访问权限,但只允许用户使用名称前缀为 staging 的资源名称在项目中创建虚拟机实例,并使用相同名称前缀的磁盘。

要实现此目的,请确保授予的角色包含创建虚拟机的必要权限,即与磁盘和实例资源类型相关的权限。然后,确保条件表达式检查磁盘和实例的资源名称。 除了这两种类型之外,不授予角色中的其他任何权限。

以下条件表达式将导致意外行为。在 Compute Engine 虚拟机上运行操作的权限被禁止:

resource.type == 'compute.googleapis.com/Disk' &&
 resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')

以下条件表达式同时包括了磁盘和实例,并将根据这两种类型的资源名称管理访问权限:

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging'))

以下条件表达式同时包括了磁盘和实例,并将根据这两种类型的资源名称管理访问权限。对于任何其他资源类型,无论资源名称如何,条件表达式都会授予向其授予该角色:

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging')) ||
 (resource.type != 'compute.googleapis.com/Disk' &&
  resource.type != 'compute.googleapis.com/Instance')

仅父级权限

在 Google Cloud 的资源层次结构中,影响子资源的角色中的某些权限只能在父级强制执行。例如,要列出 Cloud KMS 的加密密钥,必须授予用户对包含加密密钥的密钥环的 cloudkms.cryptokeys.list 权限,而不是对密钥本身的权限。这些类型的权限称为仅父级权限,仅适用于 list 操作。

要在使用条件时正确授予对 *.*.list 权限的访问权限,条件表达式应该根据要列出的目标资源的父资源类型,设置 resource.serviceresource.type 特性。

请参考以下示例:使用上述 Compute Engine 示例,以下表达式禁止访问 compute.disks.listcompute.instances.list 权限,因为选中此权限的资源的 resource.type 特性的值为 cloudresourcemanager.googleapis.com/Project

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess'))

通常,这些 list 权限与对于资源的常规操作的其他权限一起授予。在这种情况下,要扩大授权范围,您可以仅扩展 cloudresourcemanager.googleapis.com/Project 类型的范围,也可以将范围扩展到实例或磁盘类型权限以外的所有其他权限。

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
 resource.type == 'cloudresourcemanager.googleapis.com/Project'

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
 (resource.type != 'compute.googleapis.com/Disk' &&
  resource.type != 'compute.googleapis.com/Instance')