使用来自云函数的计算元数据为服务帐户生成 JWT

Generate JWT for service account using compute metadata from cloud function

我正在尝试从 Google/Firebase 云函数为给定服务帐户 serviceA 生成 JWT。服务帐户 serviceB 正在 运行 启用该功能。

我使用来自 JSON 的帐户密钥让它工作。

鉴于 CF 运行在 Google 云中,我想利用计算元数据而不必将私钥与函数一起存储。

我一直在尝试访问 serviceA 的元数据服务器,而 serviceB 正在执行 CF。我故意不想serviceA到运行的CF。

密码

const request = require('request-promise');
const serviceAccountEmail = 'serviceA@<projectA>.iam.gserviceaccount.com';
const metadataServerTokenURL = `http://metadata/computeMetadata/v1/instance/service-accounts/${serviceAccountEmail}/identity?audience=<audience>`;
const tokenRequestOptions = {
    uri: metadataServerTokenURL,
    headers: {
        'Metadata-Flavor': 'Google'
    }
};
const token = await request(tokenRequestOptions);

错误

我目前收到 404 未找到电子邮件的错误消息

我猜是
a) 我想做的不可能,或者
b) 我缺少 serviceA

的一些 IAM 权限

您可以使用元数据服务器执行此操作,因为它们只能为您的实例加载的服务帐户(在本例中为 serviceB)生成 ID 令牌。

您可以为此使用另一个 API:Service Account Credentials API, especially the generateIdToken method

对于你的情况,你可以这样做(在 python 此处)

import google.auth
from google.auth.transport.requests import AuthorizedSession
import json


# IAP audience is the ClientID of IAP-App-Engine-app in 
# the API->credentials page
# Cloud Function and Cloud Run need the base URL of the service
audience = 'YOUR AUDIENCE'
# #1 Get the default credential to generate the access token
credentials, project_id = google.auth.default(
            scopes='https://www.googleapis.com/auth/cloud-platform')

# #2 To use the current service account email
service_account_email = credentials.service_account_email
# Don't work with user account, so define manually the email
# service_account_email = 'MY SERVICE ACCOUNT EMAIL'
# #3 prepare the call the the service account credentials API
sa_credentials_url =  f'https://iamcredentials.googleapis.com/' \
                      f'v1/projects/-/serviceAccounts/'  \
                      f'{service_account_email}:generateIdToken'
headers = {'Content-Type': 'application/json'}

# Create an AuthorizedSession that includes 
# automatically the access_token based on your credentials
authed_session = AuthorizedSession(credentials)
# Define the audience in the request body
# add the parameter "'includeEmail':true" for IAP access
body = json.dumps({'audience': audience})
# Make the call 
token_response = authed_session.request('POST',sa_credentials_url,
                                        data=body, headers=headers)

jwt = token_response.json()
id_token = jwt['token']

wrote an article on this, this week

我采用了@guillaume blaquiere 的 Typescript 解决方案:

import { GaxiosOptions, Headers } from 'gaxios';
import { GoogleAuth } from 'google-auth-library';

interface TokenRequestResponse {
  token: string
}

const service_account_email = 'MY SERVICE ACCOUNT EMAIL'
const audience: string = 'MY AUDIENCE';
const sa_credentials_url = `https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${service_account_email}:generateIdToken`

const headers: Headers = { 'Content-Type': 'application/json' }
const body = {
  'audience':  audience
};
const options: GaxiosOptions = {
  method: 'POST',
  url: sa_credentials_url,
  body: JSON.stringify(body),
  headers: headers,
};

const auth = new GoogleAuth({
  scopes: 'https://www.googleapis.com/auth/cloud-platform'
});
const client = await auth.getClient();
const tokenResponse = await client.request(options);
const tokenRequestResponse = tokenResponse.data as TokenRequestResponse;
const token = tokenRequestResponse.token;