我正在尝试调用已部署到 Google Cloud Run 的服务。这是一个简单的“hello world”服务,但具有“需要身份验证”作为身份验证选项。
我尝试使用工作流身份联合从 AWS EC2 实例调用它。 我已按照此处的说明操作https://cloud.google.com/iam/docs/workload-identity-federation-with-other-clouds (而且我还发现this很有帮助),并且我已经证明它成功地模拟了服务帐户,因为从 EC2 实例运行的以下代码可以成功列出 GCP 存储桶:
const {GoogleAuth} = require('google-auth-library');
async function main() {
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform'
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
console.log('projectId = ', projectId)
// List all buckets in a project.
const url = `https://storage.googleapis.com/storage/v1/b?project=${projectId}`;
const res = await client.request({ url });
console.log(res.data);
}
main().catch(console.error);
但是,当我尝试使用以下代码调用我的 Cloud Run 服务时:
const {GoogleAuth} = require('google-auth-library');
async function main() {
const targetAudience = 'https://my-cloud-run-service.a.run.app/'
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
targetAudience: targetAudience
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
console.log('projectId = ', projectId)
// it's fine up to here
const url = targetAudience;
// this fails with HTTP error 401 unauthorised
const response = await client.request({url});
console.log(response);
}
main().catch(console.error);
然后失败:
GaxiosError:
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>401 Unauthorized</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Unauthorized</h1>
<h2>Your client does not have permission to the requested URL <code>/</code>.</h2>
<h2></h2>
</body></html>
at Gaxios._request (/home/ubuntu/trader2/node_modules/gaxios/build/src/gaxios.js:141:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async AwsClient.requestAsync (/home/ubuntu/trader2/node_modules/google-auth-library/build/src/auth/baseexternalclient.js:246:24)
at async main (/home/ubuntu/trader2/testAuth.js:15:22)
现在,如果我将
GOOGLE_APPLICATION_CREDENTIALS
设置为服务帐户密钥本身,而不是在授予服务帐户访问工作流联合身份时下载的文件,并调用 getIdTokenClient
而不是 getClient
,那么它就可以正常工作。
这是可以接受的,因为只要我保护服务帐户密钥文件,那么它仍然足够安全。但如果可能的话,我只是想让推荐的方法起作用 - 令人沮丧的是,我一定缺少一块拼图!
有谁知道我可能错过了配置步骤,需要测试什么,或者我的代码是否有问题?
非常感谢任何帮助
成功 - 我已经使用下面的代码让它工作了。
非常感谢@JohnHanley - 您向我提供了有关使用访问令牌交换 OIDC 访问令牌的信息、Google 服务和调用用户管理的云运行服务之间的授权要求之间的差异以及用于获取OIDC 代币恰好提供了拼图中缺失的一块。再次感谢,非常感谢。
但是我确实像@guillaume-blaquiere 所说的那样,如果这是图书馆的一部分那就太好了。
const {GoogleAuth} = require('google-auth-library');
const axios = require('axios')
async function main() {
const targetAudience = 'https://my-cloud-run-service-url.run.app/'
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
targetAudience: targetAudience
});
const client = await auth.getClient();
const accessTokenResponse = await client.getAccessToken();
const accessToken = accessTokenResponse?.token;
if(!accessToken) {
throw new Error("accessToken not present");
}
const serviceAccountEmail = client.getServiceAccountEmail()
const getServiceTokenData = {
audience: targetAudience
}
const getServiceTokenHeaders = {
'Content-Type': 'text/json',
'Authorization': `Bearer ${accessToken}`
}
const getServiceTokenConfig = {
headers: getServiceTokenHeaders
}
const getServiceTokenResponse = await axios.post(
`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${serviceAccountEmail}:generateIdToken`,
getServiceTokenData, getServiceTokenConfig
)
const idToken = getServiceTokenResponse.data.token;
const resp = await axios.get(targetAudience, {
headers: {
'Authorization': `Bearer ${idToken}`
}
})
console.log(resp.data)
}
main().catch(console.error);