如何使用通过 API 网关到云函数的服务帐户的 JWT 在云函数中初始化 Google BigQuery 客户端
How to initialize a Google BigQuery Client within a Cloud Function using the JWT of a service account passed by API Gateway to the Cloud Function
目标:
我设置了一个 Google API 网关。 API 的后端是一个云函数(用 python 编写)。云函数应该从 Google BigQuery (BQ) 查询数据。为此,我想创建一个 BQ 客户端 (google.cloud.bigquery.Client()
)。 API 应由使用不同服务帐户的不同应用程序访问。服务帐户仅有权访问我的项目中的特定数据集。因此,服务 accounts/applications 应该 只有 能够查询他们有权查询的数据集。因此,云函数中的 BQ 客户端应使用向 API.
发送请求的服务帐户进行初始化
我试过的:
API 由以下 OpenAPI 定义保护,因此需要由服务帐户 SA-EMAIL 签名的 JWT 才能向那里发送请求:
securityDefinitions:
sec-def1:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "SA-EMAIL"
x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/SA-EMAIL"
x-google-audiences: "SERVICE"
对于使用我的云函数的路径,我使用以下后端配置:
x-google-backend:
address: https://PROJECT-ID.cloudfunctions.net/CLOUD-FUNCTION
path_translation: CONSTANT_ADDRESS
因此,在云函数本身中,我从 API 网关获得转发的 JWT 作为 X-Forwarded-Authorization
以及已经验证的 base64url 编码的 JWT 有效载荷作为 X-Apigateway-Api-Userinfo
。
我尝试使用来自 X-Forwarded-Authorization
的 JWT 来获取凭据:
bearer_token = request.headers.get('X-Forwarded-Authorization')
token = bearer_token.split(" ")[1]
cred = google.auth.credentials.Credentials(token)
起初,这似乎在 cred.valid
returns True
之后起作用,但是当尝试使用 google.cloud.bigquery.Client(credentials=cred)
创建客户端时,它 returns 以下内容日志中的错误:
google.auth.exceptions.RefreshError: The credentials do not contain
the necessary fields need to refresh the access token. You must
specify refresh_token, token_uri, client_id, and client_secret.
我对 auth/oauth 没有太多经验,但我认为我没有必要的 tokens/attributes 错误是说我的云函数中缺少可用的。
另外,我不太确定为什么会有 RefreshError
,因为我不想刷新令牌(也不想明确地这样做)并再次使用它(可能是不好的做法?)。
问题:
是否有可能以我尝试过的方式或任何其他方式实现我的目标?
您的目标是获取调用 API 网关的凭据,并在您的 Cloud Functions 中重用它来调用 BigQuery。
遗憾的是,你不能。为什么?因为 API 网关阻止您实现该目标(出于安全原因,这是个好消息)。 JWT 令牌已正确转发到您的 Cloud Functions,但签名部分已被删除(您仅收到 JWT 令牌的 header 和 body)。
安全验证已由 API 网关完成,您必须依赖该验证。
有什么解决办法?
我的解决方案如下:在您收到的截断 JWT 中,您可以获得 body 并获取服务帐户电子邮件。从那里,您可以使用 Cloud Functions 服务帐户来模拟您收到的服务帐户电子邮件。
这样,Cloud Functions 服务帐号只需要模拟这些服务帐号的权限,您保留原始服务帐号提供的权限。
我没有看到其他解决方案可以解决您的问题。
您从 API 网关收到的 JWT 不是 OAuth 访问令牌。因此,JWT 负载部分不是可用于 BigQuery 客户端授权的凭据。
正如 @guillaume blaquiere 指出的那样,有效负载包含身份的电子邮件地址。如果身份是服务帐户,您可以实施该身份的模拟。如果您在 API 网关中使用多个身份,这可能是一个很好的解决方案。如果身份是用户帐户,则您需要实施全域委派。
我建议只使用分配给 Cloud Functions 的服务帐户,并分配适当的角色来初始化 BigQuery 客户端。如果 API 网关提供访问您的 Cloud Function 的授权,则不需要额外的模拟层。
另一种选择是将匹配的服务帐户 JSON 密钥文件存储在 Secret Manager 中,并在需要时提取它以创建 BigQuery 客户端。
目标:
我设置了一个 Google API 网关。 API 的后端是一个云函数(用 python 编写)。云函数应该从 Google BigQuery (BQ) 查询数据。为此,我想创建一个 BQ 客户端 (google.cloud.bigquery.Client()
)。 API 应由使用不同服务帐户的不同应用程序访问。服务帐户仅有权访问我的项目中的特定数据集。因此,服务 accounts/applications 应该 只有 能够查询他们有权查询的数据集。因此,云函数中的 BQ 客户端应使用向 API.
我试过的:
API 由以下 OpenAPI 定义保护,因此需要由服务帐户 SA-EMAIL 签名的 JWT 才能向那里发送请求:
securityDefinitions:
sec-def1:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "SA-EMAIL"
x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/SA-EMAIL"
x-google-audiences: "SERVICE"
对于使用我的云函数的路径,我使用以下后端配置:
x-google-backend:
address: https://PROJECT-ID.cloudfunctions.net/CLOUD-FUNCTION
path_translation: CONSTANT_ADDRESS
因此,在云函数本身中,我从 API 网关获得转发的 JWT 作为 X-Forwarded-Authorization
以及已经验证的 base64url 编码的 JWT 有效载荷作为 X-Apigateway-Api-Userinfo
。
我尝试使用来自 X-Forwarded-Authorization
的 JWT 来获取凭据:
bearer_token = request.headers.get('X-Forwarded-Authorization')
token = bearer_token.split(" ")[1]
cred = google.auth.credentials.Credentials(token)
起初,这似乎在 cred.valid
returns True
之后起作用,但是当尝试使用 google.cloud.bigquery.Client(credentials=cred)
创建客户端时,它 returns 以下内容日志中的错误:
google.auth.exceptions.RefreshError: The credentials do not contain
the necessary fields need to refresh the access token. You must
specify refresh_token, token_uri, client_id, and client_secret.
我对 auth/oauth 没有太多经验,但我认为我没有必要的 tokens/attributes 错误是说我的云函数中缺少可用的。
另外,我不太确定为什么会有 RefreshError
,因为我不想刷新令牌(也不想明确地这样做)并再次使用它(可能是不好的做法?)。
问题:
是否有可能以我尝试过的方式或任何其他方式实现我的目标?
您的目标是获取调用 API 网关的凭据,并在您的 Cloud Functions 中重用它来调用 BigQuery。
遗憾的是,你不能。为什么?因为 API 网关阻止您实现该目标(出于安全原因,这是个好消息)。 JWT 令牌已正确转发到您的 Cloud Functions,但签名部分已被删除(您仅收到 JWT 令牌的 header 和 body)。
安全验证已由 API 网关完成,您必须依赖该验证。
有什么解决办法?
我的解决方案如下:在您收到的截断 JWT 中,您可以获得 body 并获取服务帐户电子邮件。从那里,您可以使用 Cloud Functions 服务帐户来模拟您收到的服务帐户电子邮件。
这样,Cloud Functions 服务帐号只需要模拟这些服务帐号的权限,您保留原始服务帐号提供的权限。
我没有看到其他解决方案可以解决您的问题。
您从 API 网关收到的 JWT 不是 OAuth 访问令牌。因此,JWT 负载部分不是可用于 BigQuery 客户端授权的凭据。
正如 @guillaume blaquiere 指出的那样,有效负载包含身份的电子邮件地址。如果身份是服务帐户,您可以实施该身份的模拟。如果您在 API 网关中使用多个身份,这可能是一个很好的解决方案。如果身份是用户帐户,则您需要实施全域委派。
我建议只使用分配给 Cloud Functions 的服务帐户,并分配适当的角色来初始化 BigQuery 客户端。如果 API 网关提供访问您的 Cloud Function 的授权,则不需要额外的模拟层。
另一种选择是将匹配的服务帐户 JSON 密钥文件存储在 Secret Manager 中,并在需要时提取它以创建 BigQuery 客户端。