SecretManagerServiceClient在谷歌应用引擎中引发403权限被拒绝的CONSUMER_INVALI



背景:

  • 我正试图在python39运行时将Django应用程序部署到谷歌应用引擎(GAE)标准环境中
  • 数据库配置存储在Secret Manager的秘密版本中,类似于谷歌的GAE Django教程(链接)
  • 该应用程序作为用户管理的服务帐户server@myproject.iam.gserviceaccount.com运行,该帐户具有访问机密的适当权限,可以使用gcloud secret versions access进行确认

问题:

  • 在Djangosettings.py模块中,当我尝试使用google.cloud.secretmanager.SecretManagerServiceClient.access_secret_version(...)访问机密时,我得到以下CONSUMER_INVALID错误:
google.api_core.exceptions.PermissionDenied: 403 Permission denied on resource project myproject. [links {
description: "Google developer console API key"
url: "https://console.developers.google.com/project/myproject/apiui/credential"
}
, reason: "CONSUMER_INVALID"
domain: "googleapis.com"
metadata {
key: "service"
value: "secretmanager.googleapis.com"
}
metadata {
key: "consumer"
value: "projects/myproject"
}

我的调试

  • I无法在GAE的外部复制以上的错误;
    • 我可以确认SA可以访问机密:
gcloud secrets versions access latest --secret=server_env --project myproject 
--impersonate-service-account=server@myproject.iam.gserviceaccount.com
WARNING: This command is using service account impersonation. All API calls will be executed as [server@myproject.iam.gserviceaccount.com].
DATABASE_URL='postgres://django:...'
SECRET_KEY='...'
  • 我还确认我在本地运行django应用程序,模拟服务帐户,并进行上述access_secret_version(...)调用

  • 在绝望中,我甚至为项目创建了一个API密钥,并将其硬编码到我的settings.py文件中,而这引发了相同的错误

  • 我已经确认了项目中的以下设置:

    • 应用程序正在使用正确的用户管理SA运行
    • 对CCD_ 8的调用是用正确的SA进行的(即凭证是正确地从GAE环境中提取的)
    • 该项目已启用secretmanager.googleapis.com服务,并且已启用计费,并且计费帐户处于活动状态

如果您对配置或方法有任何建议来帮助调试,我将不胜感激

相关代码段

app.yaml

service_account: server@myproject.iam.gserviceaccount.com
runtime: python39
handlers:
# This configures Google App Engine to serve the files in the app's static
# directory.
- url: /_static
static_dir: _static/
# This handler routes all requests not caught above to your main app. It is
# required when static routes are defined, but can be omitted (along with
# the entire handlers section) when there are no static files defined.
- url: /.*
script: auto
env_variables:
...
inbound_services:
- mail
- mail_bounce
app_engine_apis: true

服务帐户创建&权限

  • SA是用Terraform创建的,如下所示
  • (SA没有角色roles/secretmanager.secretAccessor,但直接在机密本身上具有IAM绑定)
resource "google_service_account" "frontend_server" {
project      = google_project.project.project_id
account_id   = "server"
display_name = "Frontend Server Service Account"
}
resource "google_project_iam_member" "frontend_server" {
depends_on = [
google_service_account.frontend_server,
]
for_each = toset([
"roles/appengine.serviceAgent",
"roles/cloudsql.client",
"roles/cloudsql.instanceUser",
"roles/secretmanager.viewer",
"roles/storage.objectViewer",
])
project  = google_project.project.project_id
role     = each.key
member   = "serviceAccount:${google_service_account.frontend_server.email}"
}

Djangosettings.py

应用程序settings.py的相关部分如下所示;CCD_ 14提升

import logging
import environ
from google.cloud import secretmanager
import google.auth
# Load secrets from secret manager; the client is auth'd by SA IAM policies
credentials, project = google.auth.default(
scopes=['https://www.googleapis.com/auth/cloud-platform']
)
secretmanager_client = secretmanager.SecretManagerServiceClient(credentials=credentials)
# Load the database connection string into the environment
secrets = [
f"projects/{GOOGLE_CLOUD_PROJECT}/secrets/server_env/versions/latest",
]
for name in secrets:
try:
logging.info(f"Reading secret {name} into django settings module...")
payload = secretmanager_client.access_secret_version(name=name).payload.data.decode("UTF-8")
env.read_env(io.StringIO(payload))
except Exception as e:
logging.error(f"Encountered error when accessing secret {name}: {e}")
logging.error(f"Client credentials during error: {secretmanager_client._transport._credentials.__dict__}")
raise e from None

叹气。这是一个严重的app.yaml文件中难以阅读的拼写错误。这个项目有一个助记符子串,里面有非常相似的字母,我打错了,就是看不见。

FWIW,如果有人遇到类似的错误,你至少可以避免这一个错误来源:

  • 我通过app.yaml文件传递了一个项目前缀字符串和一个环境字符串,然后settings.py文件将这些字符串连接起来,以创建项目
  • 当运行gcloud app deploy时,(正确的)串联项目字符串也存在于我的shell$GOOGLE_CLOUD_PROJECT变量中,因此使用正确的项目id进行部署是正确的
  • 然而,我删除了settings.py中的串联代码,转而使用GAE(docs)中始终存在的GOOGLE_CLOUD_PROJECTenv变量

TLDR:DRY很好。。

您授予的角色不正确。看看那个文档页。

  • 秘密查看器角色允许您查看秘密和版本,但不能查看内容
  • 秘密访问者角色允许您访问秘密版本的内容

相关内容

  • 没有找到相关文章

最新更新