Google CloudSQLAdmin - 服务帐户没有桶所需的权限。

14

我正在编写一个 Python 函数,使用服务帐户凭据调用 Google CloudSQLAdmin API 将数据库导出到存储桶。

服务帐户已被授予项目所有者权限,并为项目所有者设置了存储桶权限。我们的项目已启用 sqlAdmin API。

Python 代码:

from google.oauth2 import service_account
from googleapiclient.discovery import build
import googleapiclient
import json

def main():
    SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin', 'https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/devstorage.full_control']
    SERVICE_ACCOUNT_FILE = './creds/service-account-credentials.json'
    PROJECT = "[REDACTED]"
    DB_INSTANCE = "[REDACTED]"
    BUCKET_PATH = "gs://[REDACTED]/[REDACTED].sql"
    DATABASES = [REDACTED]
    BODY = { # Database instance export request.
    "exportContext": { # Database instance export context. # Contains details about the export operation.
      "kind": "sql#exportContext", # This is always sql#exportContext.
      "fileType": "SQL", # The file type for the specified uri.
          # SQL: The file contains SQL statements.
          # CSV: The file contains CSV data.
      "uri": BUCKET_PATH, # The path to the file in Google Cloud Storage where the export will be stored. The URI is in the form gs://bucketName/fileName. If the file already exists, the requests succeeds, but the operation fails. If fileType is SQL and the filename ends with .gz, the contents are compressed.
      "databases": DATABASES,
    },
  }

    credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta4', credentials=credentials)
    response = sqladmin.instances().export(project=PROJECT, instance=DB_INSTANCE, body=BODY).execute()
    print(json.dumps(response, sort_keys=True, indent=4))

运行此代码会产生以下错误:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "[REDACTED]/main.py", line 47, in hello_pubsub
    response = sqladmin.instances().export(project=PROJECT, instance=DB_INSTANCE, body=BODY).execute()
  File "/usr/local/lib/python3.7/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/googleapiclient/http.py", line 851, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/sql/v1beta4/projects/[REDACTED]/instances/[REDACTED]/export?alt=json returned "The service account does not have the required permissions for the bucket.">
我已经在两个GCP项目中尝试过,并使用了多个具有不同权限的服务账号。
相关问题: 当从云存储导入CSV到云SQL时,服务帐户访问被拒绝(权限问题?) - 此问题是由于权限不正确造成的,但这里不应该是这种情况,因为账户具有项目所有者权限。

6
我终于明白了 - 谷歌实际上在他们的文档中没有提到,但是每个 SQL 实例都有相应的服务帐户。它使用该服务帐户来导出数据,因此您必须授予该帐户访问目标容器的权限。 - Lucy Nunley
通过控制台(Web GUI)导出时,服务帐户会自动添加到存储桶权限中。如果之前没有以这种方式导出,则需要手动添加该帐户。 - pmiguelpinto
3个回答

41
Google Cloud使用身份验证和访问管理系统来管理资源:IAM。
每个Cloud SQL实例都使用相应的服务帐户具有权限。要查找Cloud SQL服务帐户名称,请转到:
控制台> SQL>实例名称>服务帐户
使用此服务帐户名称,您可以授予存储桶的访问控制权限, 可从以下选项中选择:存储管理员、存储对象管理员、存储对象创建者、存储对象查看器。
遵循最小特权原则,您只需要添加:存储对象创建者,即可允许导出到Cloud Storage存储桶。 文档中包含详细的存储访问角色描述

1
这个回答解决了我的问题。值得那个漂亮的绿色勾勾。 - Chris
1
实际上,为了严格遵循“最小权限原则”,CloudSQL实例导出到GCS存储桶只需要Storage Object Creator(或legacyBucketWriter)权限,这相当于gcloud sql export sql命令。请参见https://cloud.google.com/sql/docs/postgres/import-export/exporting。 - Devy
在授予服务账户访问存储桶权限时,我们应该使用哪种“PRINCIPAL_TYPE”?(您提供的链接) - Ali Sajjad

2
这是我使用的最小权限分配。
请记住,有两个相关的服务帐户。第一个是在配置GCP Cloud SQL实例时自动创建的服务帐户。此服务帐户的名称类似于p746284857472-d3jdkw@gcp-sa-cloud-sql.iam.gserviceaccount.com
第二个服务帐户是您自己创建的; 让我们称这个服务帐户为cloud-sql-export。假设我们的GCP项目ID为my_gcp_project_id
以下是权限:
  1. Create a policy that assigns the storage.admin role to the Cloud SQL service account and assign this policy to the bucket you want to export the data to. Here is the Terraform code:

     data "google_iam_policy" "cloud_sql_bucket_admin" {
       binding {
         role = "roles/storage.admin"
         members = [
           "serviceAccount:p746284857472-d3jdkw@gcp-sa-cloud-sql.iam.gserviceaccount.com",
         ]
       }
     }
    
     resource "google_storage_bucket_iam_policy" "mypolicy" {
       bucket      = "mybucketname"
       policy_data = data.google_iam_policy.cloud_sql_bucket_admin.policy_data
     }
    
  2. For the other permissions I created a role and assigned that role to the custom cloud-sql-export service account:

     resource "google_project_iam_custom_role" "cloudsql-export-bucket-role" {
       role_id     = "cloudsqlExportBucketRole"
       title       = "Cloud SQL Export to Bucket Role"
       description = "Export from Cloud SQL and write to buckets"
       permissions = ["cloudsql.instances.get", "cloudsql.instances.export"]
     }
     resource "google_project_iam_member" "cloudsql_archive_role_bind" {
       project = "my_gcp_project_id"
       role    = "projects/my_gcp_project_id/roles/cloudsqlExportBucketRole"
       member  = "serviceAccount:cloud-sql-export@my_gcp_project_id.iam.gserviceaccount.com"
     }
    
(这是对Lucy Nunley在原问题中的评论的扩展。)

OP 没有提到使用 Terraform 的事情吗? - leberknecht

1
略微偏题,但应该提到:错误消息
(gcloud.sql.import.csv) HTTPError 403: 服务帐户没有桶所需的权限。
如果文件在存储桶中不存在或者您使用了通配符(如gcloud sql import csv fails with permissions error when import file contains wildcard中所述),也会出现此错误。因此,在花费数小时调试权限之前,先仔细检查文件是否真的存在(在我的情况下,清理计划同时运行,我的测试文件被删除了...)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接