GitHub Actions:无密钥认证与发布

验证:未记录
适用版本
未记录
验证范围
尚未登记验证记录。

GitHub OIDC → GCP STS → CI 身份 → 目标资源。

推荐做法#

  • Provider 条件限定仓库数字 ID、分支、Environment 与 workflow_ref,保护工作流修改权。
  • CI 发布身份与 runtime SA 分开;只给所需资源权限,避免长期 SA key。
  • 正向发布与其他分支/仓库拒绝分别验证;镜像使用批准的 digest。

常用操作#

替换为实际资源;本段不依赖 lab.env。

PROJECT_ID='REPLACE_PROJECT_ID'
POOL='REPLACE_POOL'
PROVIDER='REPLACE_PROVIDER'

gcloud iam workload-identity-pools providers describe "$PROVIDER" \
  --project="$PROJECT_ID" --location=global --workload-identity-pool="$POOL"

容易踩的坑#

  • id-token: write 只允许申请 GitHub OIDC token,不授予 GCP 写权。

禁用 Provider 只阻止新的身份交换,不保证已签发 token 立即失效。 身份被冒用时还要撤相关资源权限、停止受影响流水线,并复测实际资源访问。

配置说明与完整示例(按需展开)

前置:Cloud Run仍存在 lab-hello 与 lab-run,镜像仓库有 v1/v2;你有一个可运行 Actions 的 GitHub 仓库。工作流手动触发,只更新已有服务镜像,不创建生产资源。

1. 建立受限信任#

从 GitHub API 或仓库资料获取组织/个人和仓库的数字 ID。数字 ID 比可重用的名称更适合约束信任。

做过 IAP Web 实验时,先关闭 IAP、恢复 Invoker IAM check;本篇使用 Cloud Run ID token 认证,ingress 和调用者权限须满足上述前置条件。

source lab.env
export GH_REPOSITORY='替换为OWNER/REPO'
export GH_OWNER_ID='替换为数字owner ID'
export GH_REPO_ID='替换为数字repository ID'
export CI_SA="lab-ci@${PROJECT_ID}.iam.gserviceaccount.com"
export PROJECT_NUMBER=$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')
gcloud services enable sts.googleapis.com iamcredentials.googleapis.com
gcloud iam service-accounts create lab-ci
gcloud iam workload-identity-pools create github --location=global --display-name='GitHub lab'
gcloud iam workload-identity-pools providers create-oidc github --location=global --workload-identity-pool=github --issuer-uri=https://token.actions.githubusercontent.com --attribute-mapping='google.subject=assertion.sub,attribute.repository_id=assertion.repository_id' --attribute-condition="assertion.repository_owner_id == '$GH_OWNER_ID' && assertion.repository_id == '$GH_REPO_ID' && assertion.ref == 'refs/heads/main' && assertion.sub == 'repo:$GH_REPOSITORY:environment:production' && assertion.event_name == 'workflow_dispatch' && assertion.workflow_ref == '$GH_REPOSITORY/.github/workflows/deploy.yml@refs/heads/main'"
gcloud iam service-accounts add-iam-policy-binding "$CI_SA" --role=roles/iam.workloadIdentityUser --member="principalSet://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/github/attribute.repository_id/$GH_REPO_ID"
gcloud run services add-iam-policy-binding lab-hello --region="$REGION" --member="serviceAccount:$CI_SA" --role=roles/run.developer
gcloud iam service-accounts add-iam-policy-binding "lab-run@$PROJECT_ID.iam.gserviceaccount.com" --member="serviceAccount:$CI_SA" --role=roles/iam.serviceAccountUser
gcloud artifacts repositories add-iam-policy-binding lab-images --location="$REGION" --member="serviceAccount:$CI_SA" --role=roles/artifactregistry.reader
gcloud projects add-iam-policy-binding "$PROJECT_ID" --member="serviceAccount:$CI_SA" --role=roles/serviceusage.serviceUsageConsumer

信任链:GitHub OIDC → STS → 限定仓库/分支 → impersonate CI SA → Cloud Run。不要信任整个 GitHub issuer 而不限制组织/仓库。官方部署流水线配置

2. 手动发布#

先建立 production Environment,仅允许 main 部署,按团队要求启用可用的审核保护。保护工作流及主分支修改权限;声明匹配并不能防止有权改 main 的人替换发布代码。

在该 Environment 添加 Variables:GCP_PROJECT、GCP_REGION、GCP_PROJECT_NUMBER、GCP_IMAGE_DIGEST(已通过验收的 sha256:... 镜像摘要)。固定路径创建 .github/workflows/deploy.yml,改文件名时同步更新 provider 的 workflow_ref 条件:

name: Deploy GCP lab
on:
  workflow_dispatch:
permissions: {}
jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      id-token: write
    steps:
    - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
      with:
        persist-credentials: false
    - uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
      with:
        workload_identity_provider: projects/${{ vars.GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github/providers/github
        service_account: lab-ci@${{ vars.GCP_PROJECT }}.iam.gserviceaccount.com
    - uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3
    - shell: bash
      run: |
        set -euo pipefail
        [[ "$IMAGE_DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]
        gcloud run services update lab-hello --project="$PROJECT_ID" --region="$REGION" --image="$REGION-docker.pkg.dev/$PROJECT_ID/lab-images/hello@$IMAGE_DIGEST"
      env:
        PROJECT_ID: ${{ vars.GCP_PROJECT }}
        REGION: ${{ vars.GCP_REGION }}
        IMAGE_DIGEST: ${{ vars.GCP_IMAGE_DIGEST }}

Actions 已固定 commit SHA,更新时审核上游变更。发布角色与运行时 lab-run 分离;run.developer 允许修改服务配置,不是“只能改镜像”的自定义权限。能修改获批工作流的身份也能使用发布权,需要仓库保护与审计。认证 Action

3. 权限验收与紧急撤权#

验收 预期
main + production + 指定工作流手动发布 镜像摘要更新成功,运行时 SA 保持为 lab-run,业务探针通过
其他分支请求 production Environment 阻止;若其规则遗漏,provider 的 ref 条件仍须拒绝
main 的另一个测试工作流请求同一身份 workflow_ref 不匹配,身份交换失败
其他仓库或无 production 环境 repository_id/sub 不匹配,不能 impersonate 发布用的 SA
同一 CI 身份读取未授权测试 Secret 明确 PERMISSION_DENIED,不能把网络/令牌过期算作权限验证

负向资源测试使用 Secret Manager 中的合成密钥,输出重定向到 /dev/null。保留 Principal、资源、错误类别和时间,不打印身份令牌。首次配置或撤权需考虑传播时间。

可选的拒绝测试:先由管理员完成合成 Secret 实验,在上述已认证的同一 Job 增加以下 step(不能在管理员终端替代执行):

    - name: Verify secret access is denied
      shell: bash
      env:
        PROJECT_ID: ${{ vars.GCP_PROJECT }}
      run: |
        set -euo pipefail
        umask 077
        trap 'rm -f denied.err' EXIT
        if gcloud secrets versions access 1 --secret=lab-secret-other --project="$PROJECT_ID" >/dev/null 2>denied.err; then
          echo 'FAIL: CI identity can read unrelated secret'; exit 1
        fi
        grep -q 'PERMISSION_DENIED' denied.err
        grep -q 'secretmanager.versions.access' denied.err
        echo 'PASS: secret access denied'

管理员必须事先确认该版本存在、API 已启用;仍需用审计事件确认拒绝 Principal 为 lab-ci,不能把不存在资源上的模糊错误作为最终证据。

紧急关闭本专用 provider 的新身份交换:

gcloud iam workload-identity-pools providers update-oidc github --workload-identity-pool=github --location=global --disabled --project="$PROJECT_ID"

不要据此认定已有凭据立即无效;如果 SA 已被冒用,同时撤销该 SA 的受影响资源授权(命令见清理),停止受影响流水线,并对已签发会话重测实际资源。已部署的应用不会因此自动回滚;若运行时也受影响,需另行隔离与换凭证。

调查与修复完成后,经原发布流程恢复授权,使用相同命令的 --no-disabled 恢复 provider,再重跑正反验证。共享 pool/provider 必须先评估其他工作负载影响。federated identity 管理。

清理#

删除工作流,再删除 pool;删除 SA 前撤销其资源授权:

gcloud run services remove-iam-policy-binding lab-hello --region="$REGION" --member="serviceAccount:$CI_SA" --role=roles/run.developer
gcloud artifacts repositories remove-iam-policy-binding lab-images --location="$REGION" --member="serviceAccount:$CI_SA" --role=roles/artifactregistry.reader
gcloud iam service-accounts remove-iam-policy-binding "lab-run@$PROJECT_ID.iam.gserviceaccount.com" --member="serviceAccount:$CI_SA" --role=roles/iam.serviceAccountUser
gcloud projects remove-iam-policy-binding "$PROJECT_ID" --member="serviceAccount:$CI_SA" --role=roles/serviceusage.serviceUsageConsumer
gcloud iam workload-identity-pools delete github --location=global
gcloud iam service-accounts delete "$CI_SA"

搜索文档

输入关键词,搜索所有文档。

↑ ↓ 选择 · Enter 打开 · Esc 关闭