谷歌云存储 Google Cloud Storage (GCS) 是谷歌云上的对象存储产品,向所有用户提供低延时、高吞吐、易用并安全的存储服务。很多客户会利用GCS丰富的功能实现各种复杂的场景,包括针对不同部门用户的精细化访问权限管理。比如下面几个场景:

  • 只允许用户通过合法的应用程序访问存储桶

  • 在存储桶级别授予用户权限

  • 允许用户创建存储桶,但不允许其访问未授权的桶

  • 用户创建的存储桶,自动获得该桶的读写删改权限,但不允许其访问其它未授权的桶

本文介绍一些方法和步骤,可以实现以上需求,做到精细化的权限管理。其中对于用户创建桶后自动授权用户读写该桶,使用了基于条件的 IAM 配置和日志触发云函数授权两种办法——前者配置简单但是有一定限制,后者使用没有限制但是配置稍微复杂。两种方法可以根据实际需要选用。


授权服务账号管理存储桶

基于桶名规则授权用户管理桶

使用云函数自动授权用户管理桶


授权服务账号管理存储桶

针对只允许用户通过合法应用程序访问存储桶这个需求,在谷歌云可以使用服务账号 Service Account 来针对应用程序进行基于IAM服务的授权和基于 OAuth  2.0协议的鉴权,确保安全性。

首先创建一个 Service Account,用于创建存储桶,并管理其创建的存储桶。创建时,对第2步和第3步略过,直接保存。

创建完成后,下载该服务账号的密钥,用于本地访问 GCS 接口。

选择 JSON 格式,点击确认后 key 文件会下载到本地。在 ~/Downloads/youzhi-lab-ff782c45a4ca.json 文件。

本文中创建的服务账号 ID 为:

gcs-manager-test@youzhi-lab.iam.gserviceaccount.com

配置好 gcloud 命令的 project ID 等环境配置。并在命令行中用 gcloud 工具使用该服务账号及其 Key 作为访问 API 的身份验证。

gcloud auth activate-service-account gcs-manager-test@youzhi-lab.iam.gserviceaccount.com \\
--key-file=/Users/eugeneyu/Downloads/youzhi-lab-929004117269.json

现在尝试列表一个已有的存储桶,可以看到结果失败。这是正常的,因为现在还没有对该服务账号授权。

$ gsutil ls gs://youzhi-lab
AccessDeniedException: 403 gcs-manager-test@youzhi-lab.iam.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket.

可以在已有的存储桶权限配置页面,添加此服务账号,并授予相应的桶级别角色,比如对象查看角色。

此时再尝试列表这个桶,可以看到结果成功,因为授权生效。

$ gsutil ls gs://youzhi-lab
gs://youzhi-lab/1184.mp3
gs://youzhi-lab/19.m4a
gs://youzhi-lab/WechatIMG13-1.png
gs://youzhi-lab/WechatIMG13-2.png
gs://youzhi-lab/WechatIMG13-4.png
...

如果尝试写入对象则会失败,因为上面步骤并没有授予创建对象的角色或权限。

$ gsutil cp ./test.py gs://youzhi-lab/
Copying file://./test.py [Content-Type=text/x-python]...
AccessDeniedException: 403 gcs-manager-test@youzhi-lab.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object.

如果尝试查看其它未授权的桶,也会失败。

$ gsutil ls gs://youzhi-lab-bak
AccessDeniedException: 403 gcs-manager-test@youzhi-lab.iam.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket.

如果尝试创建新桶,也会失败,因为没有创建桶的权限。

$ gsutil mb -p youzhi-lab gs://youzhi-lab-bucket-create-test-1
Creating gs://youzhi-lab-bucket-create-test-1/...
AccessDeniedException: 403 gcs-manager-test@youzhi-lab.iam.gserviceaccount.com does not have storage.buckets.create access to the Google Cloud project.


基于桶名规则授权用户管理桶

下面授权这个服务账号创建桶,并管理和操作所创建的桶。

首先在 IAM->Roles 菜单创建一个自定义角色,为其分配桶的创建和删除权限(permission)。


然后在 IAM->IAM 菜单页选择添加用户,选择上面创建的服务账号 ID。并对其授予两个角色。

  1. 上面创建的自定义 Storage Bucket Creator 角色——用于创建和删除桶

  2. 系统自带的 Storage Object Admin 角色——用于操作桶里的对象


给两个角色分配创建条件,限制其能创建和操作的桶的名字,做到权限范围隔离。比如讲这个服务账号只授予研发团队使用,那么在条件里限制其只能创建和操作桶名以“youzhi-lab-eng-”开头的桶及其里面的对象。条件配置如下所示。

配置好并保存后,可以测试效果。

首先创建一个“youzhi-lab-eng-”开头名字的桶,结果成功。

$ gsutil mb -p youzhi-lab -l asia-southeast1 -b on gs://youzhi-lab-eng-bucket-create-test-2
Creating gs://youzhi-lab-eng-bucket-create-test-2/...

往这个桶里添加对象,结果成功。

$ gsutil cp ./test.py gs://youzhi-lab-eng-bucket-create-test-2/
Copying file://./test.py [Content-Type=text/x-python]...
/ [1 files][  971.0 B/  971.0 B]
Operation completed over 1 objects/971.0 B.

对这个桶做列表操作,结果成功。

$ gsutil ls gs://youzhi-lab-eng-bucket-create-test-2
gs://youzhi-lab-eng-bucket-create-test-2/test.py

对一个并没有以 “youzhi-lab-eng-” 开头的桶做操作,结果失败。

$ gsutil cp ./test.py gs://youzhi-lab-bucket-create-test-1/
Copying file://./test.py [Content-Type=text/x-python]...
AccessDeniedException: 403 gcs-manager-test@youzhi-lab.iam.gserviceaccount.com does not have storage.objects.create access to the Google Cloud Storage object.

创建一个没有以 “youzhi-lab-eng-” 开头的桶,结果失败。

$ gsutil mb -p youzhi-lab -l asia-southeast1 -b on gs://youzhi-lab-bucket-create-test-1
Creating gs://youzhi-lab-bucket-create-test-1/...
AccessDeniedException: 403 gcs-manager-test@youzhi-lab.iam.gserviceaccount.com does not have storage.buckets.create access to the Google Cloud project.


使用云函数自动授权用户管理桶

下面步骤介绍如何使用基于日志触发云函数,自动将新建桶的操作权限赋予创建桶的用户。其事件顺序为:

  1. 用户 A 创建存储桶

  2. GCS 生成创建桶的日志

  3. 基于预先配置的日志路由规则,该创建桶日志被投送到 Pub/Sub 的 Topic

  4. Pub/Sub 的 Subscription 触发云函数,此时 Subscription 使用服务账号 B

  5. 云函数调用 GCS 的 API,对用户 A 进行授权,此时云函数使用服务账号 C

为了以上事件中的操作有足够权限,首先需要对用到的服务账号授权。本例 B 和 C 都使用示例项目的缺省 GCE 服务账号247839977271-compute@developer.gserviceaccount.com。以下步骤是对这个服务账号授权。

首先授权服务账号 B 触发云函数的权限。在 IAM 中对其做如下授权。

然后对服务账号 C 授权,使其可以调用 GCS 的 API 执行桶权限信息的获取和配置操作。

现在创建一个日志路由规则。在日志服务中,点击 CREATE SINK。

在 Sink 的配置中,设置 Sink 名称为 gcs-bucket-create-sink。

在 Sink 的目标配置中选择 Pub/Sub Topic。此时可以创建一个新的 Topic。

给 Topic 起名为 gcs-bucket-create-log-topic。其它配置不用变动。

在日志筛选配置中,填入以下表达式,选择创建存储桶并成功的日志。

点击 NEXT 并点击 CREATE SINK 按钮。

下面创建一个云函数。

进行如下配置

  • Environment: 2nd gen

  • Function name: grant-bucket-permissions-to-creator

  • Region: asia-southeast1

点击 ADD EVENTARC TRIGGER 按钮

在 Eventarc trigger 页面做如下配置

  • Event provider: Cloud Pub/Sub

  • Event: google.cloud.pubsub.topic.v1.messagePublished

  • Select a Cloud Pub/Sub topic: project/youzhi-lab/topics/gcs-bucket-create-log-topic

  • Region: asia-southeast1

  • Service account: 选择一个拥有项目级别 Bucket Admin 权限的服务账号,本例为 Compute Engine 缺省服务账号

在代码配置中,选择 Python 3.9运行时,并设置入口函数为 grant_permissions。

在 main.py 的代码窗口,填入以下代码。

import base64
import json
import os
import functions_framework

from google.cloud import storage

# Triggered from a message on a Cloud Pub/Sub topic.
@functions_framework.cloud_event
def grant_permissions(cloud_event):
  log_text = base64.b64decode(cloud_event.data[
"message"]["data"])
  log_json = json.loads(log_text)

  creator_email = log_json[
'protoPayload']['authenticationInfo']['principalEmail']
  principle_type =
"user"
 
if creator_email.endswith("gserviceaccount.com"):
      principle_type =
"serviceAccount"
  creator_user = principle_type +
":" + creator_email

  created_bucket_name = log_json[
'protoPayload']['resourceName']
  created_bucket_name = os.path.basename(created_bucket_name)

  role_object_admin =
"roles/storage.objectAdmin"
  role_storage_admin =
"roles/storage.admin"

  add_bucket_iam_member(created_bucket_name, role_storage_admin, creator_user)

def add_bucket_iam_member(bucket_name, role, member):
   
"""Add a new member to an IAM Policy"""
   
# bucket_name = "your-bucket-name"
   
# role = "IAM role, e.g., roles/storage.objectViewer"
   
# member = "IAM identity, e.g., user: name@example.com"

    storage_client = storage.Client()
    bucket = storage_client.bucket(bucket_name)

    policy = bucket.get_iam_policy(requested_policy_version=
3)

    policy.bindings.append({
"role": role, "members": {member}})

    bucket.set_iam_policy(policy)

    print(
f"Added {member} with role {role} to {bucket_name}.")

选择 requirements.txt 文件,并在其代码窗口填入以下代码。

functions-framework==3.*
google-cloud-storage

点击 DEPLOY 创建部署函数。

在函数部署完成后,进行测试。可以用服务账号创建一个新的存储桶。

$ gsutil mb -p youzhi-lab gs://youzhi-lab-bucket-create-test-20220615-01
Creating gs://youzhi-lab-bucket-create-test-20220615-01/...

执行完成后查看上述云函数的执行日志,可以看到有函数执行并成功授权的日志。

此时再往存储桶里写文件,可以成功,证明上述配置有效。

$ gsutil cp ./test.py gs://youzhi-lab-bucket-create-test-20220615-01
Copying file://./test.py [Content-Type=text/x-python]...
- [1 files][  1.4 KiB/  1.4 KiB]
Operation completed over 1 objects/1.4 KiB.


相关推荐