GitHub Actions中使用gsutil报错:无法为已完成作业签发ID_TOKEN
我配置了如下GitHub Actions工作流步骤:
jobs: build-and-test: runs-on: self-hosted permissions: contents: read id-token: write steps: - name: Check out uses: actions/checkout@v3 - id: auth uses: "google-github-actions/auth@v1" with: workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} token_format: "access_token"
这些步骤执行无报错,但后续执行以下步骤时失败:
- name: Download test data run: | mkdir -pv ${MY_VAR} gsutil -m rsync -r gs://some-bucket/some-folder ${MY_VAR}
报错信息如下:
google.auth.exceptions.RefreshError: ( 'Unable to retrieve Identity Pool subject token', '{ "$id":"1", "innerException":null, "message":"Can\'t issue ID_TOKEN for job in \'Completed\' state.", "typeName":"GitHub.Actions.Runtime.WebApi.CannotGenerateIdTokenException, GitHub.Actions.Runtime.WebApi, Version=14.0.0.0, Culture=neutral, PublicKeyToken=null", "typeKey":"CannotGenerateIdTokenException", "errorCode":0, "eventId":3000 }' )
我已确认secrets.GCP_WORKLOAD_IDENTITY_PROVIDER和secrets.GCP_SERVICE_ACCOUNT的值正确,查阅过官方排障文档,且手动使用相同服务账号执行gsutil命令可正常运行。
我的初步猜想:可能存在之前执行google-github-actions/auth时遗留的有效密钥,该密钥对应已完成的旧工作流运行导致失效。如何验证该猜想?为何auth步骤生成新密钥后,gsutil仍使用旧密钥?
一、验证遗留密钥猜想的方法
检查runner环境变量
在Download test data步骤前新增输出环境变量的步骤,重点查看GCP认证相关变量:- name: Print GCP auth env vars run: | echo "GOOGLE_APPLICATION_CREDENTIALS: $GOOGLE_APPLICATION_CREDENTIALS" echo "GOOGLE_ACCESS_TOKEN: $GOOGLE_ACCESS_TOKEN" # 查看凭据文件内容(如果存在) if [ -f "$GOOGLE_APPLICATION_CREDENTIALS" ]; then cat "$GOOGLE_APPLICATION_CREDENTIALS"; fi如果输出的凭据文件内容包含旧工作流标识,或
GOOGLE_ACCESS_TOKEN是之前运行生成的,即可证明存在遗留密钥。查看runner本地存储
登录自托管runner所在机器,检查GCP默认凭据存储路径(通常为~/.config/gcloud/),查看credentials或application_default_credentials.json文件的修改时间和内容。若文件修改时间早于当前工作流启动时间,说明是遗留的旧密钥。隔离测试
使用全新的自托管runner执行该工作流,若执行成功,说明原runner存在遗留认证数据;若仍失败,再排查其他问题。
二、gsutil使用旧密钥的原因
自托管runner环境未自动重置
GitHub Actions自托管runner不会在每次工作流结束后清理环境,之前运行时google-github-actions/auth生成的认证文件(如写入~/.config/gcloud/的文件)会被保留,gsutil默认优先读取本地存储的旧凭据。环境变量优先级问题
若GOOGLE_APPLICATION_CREDENTIALS指向旧凭据文件且文件存在,gsutil会优先使用该文件中的凭据,而非当前工作流生成的新access token。此外,runner上已配置的gcloud默认账号也会覆盖临时认证。token_format参数的影响
你设置了token_format: "access_token",该参数仅生成临时access token注入环境变量,但不会覆盖本地已有的gcloud凭据文件。当旧凭据对应的工作流已完成,gsutil尝试刷新旧凭据时,就会触发无法生成ID_TOKEN的错误。
内容的提问来源于stack exchange,提问作者stianlagstad

