使用Workload Identity Federation调用Google Cloud Run服务遇401授权错误
使用AWS工作负载身份联盟调用Google Cloud Run服务时出现401未授权错误
问题背景
已通过AWS工作负载身份联盟成功 impersonate GCP服务账号,可正常调用GCS API列出存储桶,但调用开启了「require authentication」的Cloud Run服务时返回401未授权。使用服务账号密钥文件配合getIdTokenClient可正常访问,需排查工作负载身份联盟方式的问题。
正常工作的GCS调用代码
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) const url = targetAudience; // 此处返回401 Unauthorized 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)
解决方案与排查步骤
1. 修正目标受众URL格式
Cloud Run的目标受众URL必须不带末尾斜杠,Google ID令牌验证会严格匹配aud字段,末尾斜杠会导致令牌受众与服务期望不匹配,触发401。正确格式应为:
const targetAudience = 'https://my-cloud-run-service.a.run.app';
2. 显式获取ID令牌客户端
getClient()默认生成的是适用于GCP标准API的OAuth2客户端,而Cloud Run需要**ID令牌(ID Token)**而非访问令牌(Access Token)。需显式调用getIdTokenClient()获取专用客户端:
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' }); // 显式获取ID令牌客户端 const client = await auth.getIdTokenClient(targetAudience); const projectId = await auth.getProjectId(); console.log('projectId = ', projectId) const url = targetAudience; const response = await client.request({url}); console.log(response); } main().catch(console.error);
3. 验证服务账号权限
确认被impersonate的GCP服务账号拥有roles/run.invoker权限,可通过gcloud命令验证:
gcloud projects get-iam-policy YOUR_PROJECT_ID --filter="bindings.members:serviceAccount:YOUR_SERVICE_ACCOUNT@YOUR_PROJECT_ID.iam.gserviceaccount.com" --format="value(bindings.role)"
确保输出包含roles/run.invoker。
4. 检查令牌有效性
临时解码ID令牌,验证aud字段是否与Cloud Run URL完全一致,同时确认权限字段:
const jwtDecode = require('jwt-decode'); // 在获取客户端后添加以下代码 const token = await client.idTokenProvider.fetchIdToken(targetAudience); console.log(jwtDecode(token));
重点检查aud(受众)和permissions字段是否符合预期。
5. 确认身份池配置
检查工作负载身份池的属性映射是否正确,确保AWS角色的断言被正确映射到GCP身份属性,且身份池权限允许AWS角色impersonate目标服务账号。
内容的提问来源于stack exchange,提问作者BenTaylor
相关产品推荐
相关产品推荐

