You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cloud Scheduler使用错误服务账号认证问题求助

问题:Cloud Scheduler调用Cloud Run返回403,令牌邮箱与配置的服务账号不符

我在Cloud Run上部署了应用,并创建了Cloud Scheduler任务调用该应用的端点。我创建了一个服务账号用于Cloud Scheduler的令牌认证,且多次确认任务配置中已选中正确的服务账号。

应用中配置了用于拦截未授权请求的中间件(简化版本):

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "google.golang.org/api/oauth2/v2"
    "net/http"
    "strings"
)

func ForceCloudScheduler(c *gin.Context) {
    if c.Request.UserAgent() != "Google-Cloud-Scheduler" {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }

    // 验证Google Cloud Scheduler请求
    token := c.GetHeader("Authorization")
    if token == "" {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }
    idToken := strings.Split(token, "Bearer ")[0]

    authenticator, err := oauth2.NewService(c)
    if err != nil {
        _ = c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to acquire authenticator: %w", err))
        return
    }
    info, err := authenticator.Tokeninfo().IdToken(idToken).Do()
    if err != nil {
        _ = c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to retrieve token information: %w", err))
        return
    }

    // 此处验证失败
    if info.Email != "agora-job-scheduler-account@agoradesecrivains.iam.gserviceaccount.com" {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }

    c.Next()
}

运行任务时请求返回403错误,日志显示令牌负载中的邮箱为:

{
    "audience": "107655128939031897672",
    "email": "970356934135-compute@developer.gserviceaccount.com",
    "expires_in": 1644,
    "issued_to": "107655128939031897672",
    "scope": "openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
    "user_id": "107655128939031897672",
    "verified_email": true
}

该邮箱与我在Cloud Scheduler中设置的服务账号邮箱不符,请问这是预期情况吗?如何修复?


解答

这不是预期情况,问题核心及修复方案如下:

1. 令牌提取逻辑错误

你的代码中分割Authorization头时使用strings.Split(token, "Bearer ")[0]会拿到无效值。Authorization头格式为Bearer <令牌>,分割后数组的索引0是空字符串,索引1才是真实令牌。错误的令牌会导致验证流程返回默认服务账号的信息,也就是日志里的compute@developer.gserviceaccount.com。

修复这一行代码:

// 替换原提取逻辑,增加长度校验避免数组越界
parts := strings.Split(token, "Bearer ")
if len(parts) != 2 {
    c.AbortWithStatus(http.StatusForbidden)
    return
}
idToken := parts[1]

2. 补充完整的令牌验证逻辑

除了修复令牌提取,建议补充以下验证步骤,提升请求合法性校验的严谨性:

  • 验证令牌签发者(iss)为Google官方签发地址:https://accounts.google.com或accounts.google.com
  • 验证令牌受众(aud)匹配你的Cloud Run服务对应的客户端ID(可从Cloud Run服务详情页获取,或直接使用日志中显示的audience值确认)

修改后的完整中间件示例:

func ForceCloudScheduler(c *gin.Context) {
    if c.Request.UserAgent() != "Google-Cloud-Scheduler" {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }

    token := c.GetHeader("Authorization")
    if token == "" {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }

    // 修复令牌提取逻辑
    parts := strings.Split(token, "Bearer ")
    if len(parts) != 2 {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }
    idToken := parts[1]

    authenticator, err := oauth2.NewService(c)
    if err != nil {
        _ = c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to acquire authenticator: %w", err))
        return
    }
    info, err := authenticator.Tokeninfo().IdToken(idToken).Do()
    if err != nil {
        _ = c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to retrieve token information: %w", err))
        return
    }

    // 验证签发者合法性
    if info.Issuer != "https://accounts.google.com" && info.Issuer != "accounts.google.com" {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }

    // 验证受众匹配Cloud Run服务(替换为你的服务对应客户端ID)
    expectedAudience := "107655128939031897672"
    if info.Audience != expectedAudience {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }

    // 验证服务账号邮箱
    if info.Email != "agora-job-scheduler-account@agoradesecrivains.iam.gserviceaccount.com" {
        c.AbortWithStatus(http.StatusForbidden)
        return
    }

    c.Next()
}

3. 确认Cloud Scheduler任务配置

最后再次检查任务的核心配置:

  • 确认任务选择的服务账号为agora-job-scheduler-account@agoradesecrivains.iam.gserviceaccount.com
  • 认证方法设置为OAuth令牌,且受众填写你的Cloud Run服务完整URL(如https://your-service-abc.a.run.app)
  • 确保该服务账号拥有roles/run.invoker权限,允许调用目标Cloud Run服务

内容的提问来源于stack exchange,提问作者KawaLo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 04:55:18