使用为其他服务返回的 JWT 时,Cloud Run 服务请求总是失败并返回 401。相同的请求适用于为最终用户返回的 JWT
Posted
技术标签:
【中文标题】使用为其他服务返回的 JWT 时,Cloud Run 服务请求总是失败并返回 401。相同的请求适用于为最终用户返回的 JWT【英文标题】:Cloud Run Service requests always fail with 401 when using JWT returned for another service. The same request works with JWT returned for end-user 【发布时间】:2021-10-01 12:24:31 【问题描述】:我有一个由多个 Cloud Run 服务 组成的应用。每项服务仅对一组特定的其他服务开放。
假设我有 2 个 URL 为 https://api-service-123.run.app
和 https://data-provider-service-123.run.app
的服务。第一个服务还有一个自定义 URL https://api.my.domain
。
api-service 需要访问 data-provider-service。因此,在documentation 之后,我创建了两个按服务用户管理的服务帐户 api-service-account@domain 和 data-provider-service-account@domain。我将 api-service-account@domain 添加到具有 Cloud Run Invoker 角色的 data-provider-service 中。
现在,我无法使用从 Google 返回的 ID 令牌访问帐户。我尝试了几种方法。首先,我使用了来自码头的稍微调整的代码
export const getToken = async (url: string, targetAUD?: string) =>
const auth = new GoogleAuth();
const request = async () =>
if (!targetAUD)
targetAUD = new URL(url).origin;
console.info(`request $url with target audience $targetAUD`);
const client = await auth.getIdTokenClient(targetAUD);
const res = await client.request(url);
console.info(res.data);
return res.data;
try
return await request();
catch (err)
console.error(err);
当上面的函数被调用为getToken('http://data-provider-service-123.run.app');
时,请求失败并返回401 Unauthorized。
我还尝试将其称为getToken('https://data-provider-service-123.run.app');
并得到响应403 Forbidden。以下条目将添加到每个此类请求的 data-provider-service 日志中
httpRequest: 9insertId: "60fcb7e0000c12346fe37579"
logName: "projects/my-project/logs/run.googleapis.com%2Frequests"
receiveTimestamp: "2021-07-25T01:01:20.806589957Z"
resource: 2
severity: "WARNING"
textPayload: "The request was not authorized to invoke this service. Read more at
https://cloud.google.com/run/docs/securing/authenticating"
timestamp: "2021-07-25T01:01:20.800918Z"
trace: "projects/my-project/traces/25b3c13890aad01234829711d4e9f25"
我还尝试通过运行curl "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=http://data-provider-servive-123.run.app" \ -H "Metadata-Flavor: Google"
从计算元数据服务器(也是文档中宣传的一种方式)获取和使用 JWT。
当我在 Cloud Shell 中执行此代码时,我得到的 JWT 看起来像这样
"iss": "accounts.google.com",
"azp": "123456789-9r9s1c4alg36erliucho9t52n12n6abc.apps.googleusercontent.com",
"aud": "123456789-9r9s1c4alg36erliucho9t52n12n6abc.apps.googleusercontent.com",
"sub": "106907196154567890477",
"email": "my_email@gmail.com",
"email_verified": true,
"at_hash": "dQpctkQE2Sy1as1qv_w",
"iat": 1627173901,
"exp": 1627177501,
"jti": "448d660269d4c7816ae3zd45wrt89sb9f166452dce"
然后我使用邮递员向https://data-provider-service/get-data
发出请求,标题为Authorization: "Bearer <the_jwt_from_above>"
,一切工作正常
但是,如果我从 api-service
发出相同的请求,则返回的 JWT 是
"aud": "http://data-provider-service-123.run.app",
"azp": "111866132465987679716",
"email": "api-service-account@domain",
"email_verified": true,
"exp": 1627179383,
"iat": 1627175783,
"iss": "https://accounts.google.com",
"sub": "111866132465987679716"
如果我向邮递员发出相同的请求并将此 JWT 放入 Authorization 标头中,则会返回 401 Unauthorized。
这周我一直在尝试解决这个问题。我反复检查了权限,重新部署了几次服务,但没有任何帮助。
【问题讨论】:
问题是不正确的aud
(观众)。将工作令牌与失败的令牌进行比较。
@JohnHanley,我不确定,但我猜工作令牌中的受众是指整个项目的范围,因为我在拥有所有者权限的同时从 Shell 获得它。实际上,我想知道为什么工作的 JWT 的 aud 比要求的更广泛,但这是另一个问题。我猜来自 docs 的这句话支持了我的想法“获取一个 Google 签名的 ID 令牌,并将受众声明 (aud) 设置为接收服务的 URL。”
工作身份令牌的aud
是"aud": "http://data-provider-service-123.run.app",
。你注意到它代表什么了吗?那就是您正在调用的服务。如果观众错了,权限被拒绝。
@JohnHanley,反过来,从壳牌获得的工作令牌是123456789-9r9s1c4alg36erliucho9t52n12n6abc.apps.googleusercontent.com
。并且不工作令牌的 aud 匹配 data-provider-service,但它返回 401。
将您的问题简化为 a) 显示代码; b) 显示解码的令牌; c) 使用curl
等工具使用令牌显示确切的调用; d) 显示错误。您的其他有效代码没有帮助。您包含的信息中有问题,这会混淆/掩盖真正的问题。换句话说,从一个我可以重现的简洁问题重新开始。
【参考方案1】:
我在询问computing metadata server时,将目标服务的URL中的http改成了https,看起来问题解决了。使用 google-auth-library 请求这样的令牌仍然失败,并出现 403 错误。
如果解决方案可持续,我会更新该答案。
【讨论】:
以上是关于使用为其他服务返回的 JWT 时,Cloud Run 服务请求总是失败并返回 401。相同的请求适用于为最终用户返回的 JWT的主要内容,如果未能解决你的问题,请参考以下文章
Spring Cloud Security JWT:使用配置服务器/密钥轮换分发公钥
使用 OAuth2 保护服务,JWT 令牌不起作用 Spring Cloud
Spring Cloud微服务安全实战_6-3_jwt认证之网关和服务改造