Secret Manager访问权限问题:跨项目调用BigQuery遇403错误
问题分析与解决方案
核心问题定位
你遇到的secretmanager.versions.access权限错误,根源是Project-A中Cloud Run服务使用的默认身份(服务账号)没有访问Project-B Secret Manager的权限,而非Secret中存储的服务账号权限不足。
当前代码执行逻辑是:先调用access_secret_version访问Project-B的Secret,这一步使用的是Cloud Run的默认服务账号身份,而非你存在Secret里的那个账号——只有这一步权限通过后,才能拿到密钥去访问BigQuery。
步骤1:给Cloud Run的服务账号添加权限
- 找到Project-A中Cloud Run使用的服务账号:
- 默认情况下,Cloud Run使用的是
[Project-A的项目编号]-compute@developer.gserviceaccount.com - 也可以在Cloud Run控制台的「修订版本」→「权限」标签页查看当前使用的服务账号
- 默认情况下,Cloud Run使用的是
- 切换到Project-B的IAM控制台,给上述服务账号添加
Secret Manager Secret Accessor角色(roles/secretmanager.secretAccessor)
步骤2:修正代码逻辑错误
你的代码里直接把Secret的JSON内容赋值给GOOGLE_APPLICATION_CREDENTIALS是错误的,这个环境变量要求的是密钥文件的路径,不是JSON文本内容。需要把Secret内容写入临时文件后再指定路径:
import os import tempfile from google.cloud import secretmanager from flask import Flask app = Flask(__name__) def access_secret_version(project_id, secret_id, version_id): client = secretmanager.SecretManagerServiceClient() name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}" response = client.access_secret_version(request={"name": name}) return response.payload.data.decode("UTF-8") @app.route('/projects/random-page') def random_page(): # 获取Secret中的服务账号JSON sa_json = access_secret_version("project-b", "stockdata-secret", "1") # 将JSON写入临时文件 with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f: f.write(sa_json) temp_file_path = f.name # 设置环境变量指向临时文件 os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = temp_file_path # 后续执行BigQuery操作... # 操作完成后删除临时文件(可选,Cloud Run实例销毁时会自动清理) os.unlink(temp_file_path) return "页面内容"
更优方案:使用Workload Identity(推荐)
如果不需要保留旧的密钥文件方式,建议用Workload Identity直接授权,无需存储密钥:
- 在Project-A中创建Workload Identity池和提供者,关联Cloud Run服务
- 在Project-B中给Project-A的Cloud Run服务账号添加BigQuery相关角色(比如
BigQuery Data Viewer+BigQuery Job User) - Cloud Run代码无需处理密钥,直接使用默认身份访问Project-B的BigQuery,更安全且减少运维成本
内容的提问来源于stack exchange,提问作者user20977191
相关产品推荐
相关产品推荐

