Uma pipeline do GitHub Actions não precisa guardar uma chave JSON de service account para fazer deploy no Cloud Run. O caminho mais controlável é usar OpenID Connect (OIDC) para que cada execução peça uma credencial curta, enquanto o Google Cloud decide se aquele repositório pode assumir a identidade de deploy.

O ponto que costuma ser perdido é este: id-token: write permite que o workflow peça um token OIDC, mas não dá acesso ao Google Cloud. O acesso nasce da combinação entre a condição do Workload Identity Provider, o binding que liga o repositório ao service account e os papéis IAM desse service account. A introdução ao Google Cloud Run explica o modelo da plataforma; aqui o foco é o caminho entre CI e deploy.

Depois da autenticação, a escolha do ciclo de vida ainda importa. O comparativo de faturamento por solicitações ou instâncias no Cloud Run ajuda a separar o deploy da decisão sobre como o serviço roda.

Os comandos e o YAML abaixo são ilustrativos. Não usei um projeto Google Cloud, credenciais ou uma pipeline real para escrever este artigo. A configuração deve ser aplicada primeiro em um projeto de teste e conferida com os nomes e políticas da sua organização.

Diagrama mostra o GitHub Actions trocando um token OIDC por acesso restrito ao Cloud Run, sem chave JSON.

Resposta curta

  • Crie um Workload Identity Pool e um provider OIDC que aceite apenas o repositório esperado.
  • Dê ao service account de deploy o binding roles/iam.workloadIdentityUser para esse repositório.
  • Conceda os papéis de Cloud Run, Artifact Registry e Service Account User no escopo necessário.
  • No workflow, use id-token: write, autentique com google-github-actions/auth e só depois execute deploy-cloudrun.
  • Verifique o repositório, a revisão e a URL publicada. O fato de o token existir não prova que o deploy está autorizado.

O que muda quando você troca a chave por OIDC?

Com uma chave JSON, o GitHub Secrets guarda uma credencial de longa duração que a pipeline lê. Com OIDC, o GitHub Actions pede um JWT para aquela execução e o Google Cloud avalia o token por meio de um Workload Identity Provider. O guia do GitHub sobre OIDC em provedores de nuvem descreve essa sequência em termos gerais: o provedor precisa confiar no emissor, o workflow precisa ter a permissão para pedir o token e a política precisa restringir quem pode receber acesso.

O Google Cloud explica a federação de identidade como uma troca entre o token assinado pelo IdP, o Security Token Service e o IAM. Para este caso, pense em quatro fronteiras:

  1. O GitHub emite um token para a execução.
  2. O provider verifica que o token veio de um repositório aceito.
  3. O service account autoriza aquela identidade federada a agir em seu nome.
  4. Os papéis do service account limitam o que o deploy pode fazer.

Uma condição que aceita apenas a organização é melhor que nenhuma condição, mas ainda pode permitir mais repositórios do que você pretendia. Se o deploy pertence a um único repositório, restrinja o provider ao valor completo de ORG/REPO e repita essa restrição no binding IAM.

Crie um provider restrito ao repositório

Você precisa de um projeto, do número desse projeto, de um pool, de um provider OIDC e de um service account que será o deployer. O README atual da action google-github-actions/auth recomenda mapear as claims que serão usadas nas condições e alerta para sempre adicionar uma condição de atributo ao provider.

Defina os valores no seu terminal. Eles são placeholders, não nomes que você deve copiar literalmente:

export PROJECT_ID="seu-projeto"
export PROJECT_NUMBER="123456789012"
export GITHUB_REPOSITORY="sua-org/seu-repo"
export POOL_ID="github"
export PROVIDER_ID="seu-repo"
export DEPLOYER_SA="github-deployer@${PROJECT_ID}.iam.gserviceaccount.com"

Crie o service account e o pool:

gcloud iam service-accounts create github-deployer \
  --project="${PROJECT_ID}" \
  --display-name="GitHub Actions Cloud Run deployer"

gcloud iam workload-identity-pools create "${POOL_ID}" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --display-name="GitHub Actions pool"

Agora crie o provider. O mapeamento deixa attribute.repository disponível para o binding IAM. A condição usa a claim repository para impedir que outro repositório da plataforma entre no pool:

gcloud iam workload-identity-pools providers create-oidc "${PROVIDER_ID}" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --workload-identity-pool="${POOL_ID}" \
  --display-name="GitHub Actions provider" \
  --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner,attribute.ref=assertion.ref" \
  --attribute-condition="assertion.repository == '${GITHUB_REPOSITORY}'" \
  --issuer-uri="https://token.actions.githubusercontent.com"

A expressão é deliberadamente estreita. Se você quiser permitir vários repositórios, liste essa decisão na política e teste cada combinação. Não troque a condição por um curinga só para fazer um erro de autenticação desaparecer.

O provider completo que vai para o workflow tem o número do projeto, o pool e o provider:

projects/123456789012/locations/global/workloadIdentityPools/github/providers/seu-repo

Permita que o repositório use o deployer

A condição do provider controla a entrada no pool. Ainda falta autorizar a identidade federada a impersonar o service account. O binding abaixo usa o repositório mapeado no pool, não toda a organização:

export WORKLOAD_IDENTITY_POOL="projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}"

gcloud iam service-accounts add-iam-policy-binding "${DEPLOYER_SA}" \
  --project="${PROJECT_ID}" \
  --role="roles/iam.workloadIdentityUser" \
  --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL}/attribute.repository/${GITHUB_REPOSITORY}"

O README da action de autenticação mostra esse modelo de federação por meio de service account. A action pode usar federação direta em recursos compatíveis, mas o caminho com um deployer dedicado torna a revisão dos papéis mais clara para uma pipeline de Cloud Run.

Conceda os papéis de deploy no escopo certo

O deployer não precisa ser proprietário do projeto. Para publicar uma imagem que já existe no Artifact Registry em um serviço existente, a documentação atual de papéis IAM do Cloud Run separa estas necessidades:

Recurso Papel usado no exemplo Para que serve
Serviço Cloud Run roles/run.developer Criar ou atualizar o serviço e a revisão dentro do escopo concedido
Repositório Artifact Registry roles/artifactregistry.reader Ler a imagem que será implantada
Service account de runtime roles/iam.serviceAccountUser Permitir que o deployer use a identidade escolhida pelo serviço

O papel Service Account User merece atenção. Ele permite agir como a identidade de runtime. Se essa identidade tem permissões amplas, o impacto de um workflow comprometido também cresce. Use um service account de runtime separado e dê a ele apenas os papéis que a aplicação precisa.

Os comandos de concessão dependem de nomes de serviço, região e repositório. Um esqueleto de referência é:

export SERVICE="seu-servico"
export REGION="southamerica-east1"
export GAR_LOCATION="southamerica-east1"
export REPOSITORY="containers"
export RUNTIME_SA="cloud-run-runtime@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud run services add-iam-policy-binding "${SERVICE}" \
  --project="${PROJECT_ID}" \
  --region="${REGION}" \
  --member="serviceAccount:${DEPLOYER_SA}" \
  --role="roles/run.developer"

gcloud artifacts repositories add-iam-policy-binding "${REPOSITORY}" \
  --project="${PROJECT_ID}" \
  --location="${GAR_LOCATION}" \
  --member="serviceAccount:${DEPLOYER_SA}" \
  --role="roles/artifactregistry.reader"

gcloud iam service-accounts add-iam-policy-binding "${RUNTIME_SA}" \
  --project="${PROJECT_ID}" \
  --member="serviceAccount:${DEPLOYER_SA}" \
  --role="roles/iam.serviceAccountUser"

Se a pipeline também fizer push da imagem, o deployer precisa de uma permissão de escrita no repositório. Essa é uma mudança de escopo, não um detalhe do OIDC. Separe o papel de publicação do papel de runtime e não dê artifactregistry.admin apenas porque o primeiro teste falhou.

Escreva o workflow do GitHub Actions

O workflow precisa declarar id-token: write, fazer checkout antes da autenticação e passar o provider e o service account para a action. A action deploy-cloudrun documenta o uso conjunto de auth@v3 e deploy-cloudrun@v3. O exemplo abaixo usa checkout@v7, auth@v3 e deploy-cloudrun@v3, versões mostradas nas documentações atuais consultadas em 11/11/2026.

name: Deploy Cloud Run

on:
  push:
    branches: [main]

env:
  PROJECT_ID: $
  REGION: $
  SERVICE: $
  IMAGE: $

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write

    steps:
      - name: Check out the repository
        uses: actions/checkout@v7

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v3
        with:
          project_id: $
          workload_identity_provider: $
          service_account: $

      - name: Deploy the existing image
        id: deploy
        uses: google-github-actions/deploy-cloudrun@v3
        with:
          project_id: $
          region: $
          service: $
          image: $

      - name: Check the deployed service
        run: curl --fail --silent --show-error "$/health"

GCP_WIF_PROVIDER deve conter o identificador completo, e não apenas o nome github. GCP_DEPLOYER_SERVICE_ACCOUNT deve apontar para o service account que recebeu o binding. Uma variável trocada pode produzir uma mensagem genérica de permissão, então registre esses valores no inventário da pipeline sem imprimir tokens ou credenciais.

O workflow assume que $ já aponta para uma imagem existente no Artifact Registry. Se o mesmo job também fizer build e push, autentique o Docker com um access token temporário e conceda roles/artifactregistry.writer ao deployer. A documentação do docker/login-action mostra esse padrão com oauth2accesstoken; não use uma chave JSON de registry como atalho.

O arquivo gerado pela action de autenticação também merece uma regra no repositório:

gha-creds-*.json

O README de google-github-actions/auth recomenda ignorar esse padrão quando a action cria o arquivo de credenciais para ferramentas posteriores. O arquivo é temporário, mas uma regra no .gitignore reduz o risco de empacotar o artefato em uma imagem ou commit.

Como verificar o deploy sem confiar no verde da pipeline?

Faça a verificação em camadas. Um job verde prova que os passos terminaram sem erro, não que o serviço recebeu a imagem certa nem que o endpoint principal responde.

  1. Confirme a identidade e o escopo. Revise a organização, o repositório e o branch que a condição do provider aceita. id-token: write sozinho não concede nenhum papel no Google Cloud.
  2. Confirme a revisão. Depois do deploy, compare a imagem e o digest exibidos por gcloud run services describe com a imagem que a pipeline calculou para aquele commit.
  3. Teste um endpoint seguro. Use um health check que não escreva no banco nem dispare uma fila. Se o serviço é privado, configure a autenticação do teste sem transformar o serviço em público.
  4. Confira os logs. Um curl que responde 200 não prova que uma dependência importante está saudável. Observe startup, autorização, leitura da imagem e o caminho principal da aplicação.
  5. Prove o rollback. A página de deploy do Cloud Run expõe a URL do serviço como output. Registre a revisão anterior e saiba qual comando ou workflow a restaura antes de promover uma mudança maior. Para a decisão de tráfego, veja também o guia de deploy canário no Cloud Run.

Essa sequência separa autenticação, autorização, entrega do artefato e comportamento da aplicação. Quando tudo é reduzido a “o job passou”, a primeira falha fica difícil de localizar.

Diagnóstico dos erros mais comuns

O provider rejeita o token

Confira o issuer https://token.actions.githubusercontent.com, o mapeamento de assertion.repository e a condição do provider. Um repositório renomeado, um fork ou um workflow disparado em outro contexto pode não satisfazer a expressão. Não remova a condição para testar: corrija a claim que deveria ser aceita.

O token é emitido, mas o service account é negado

Isso normalmente indica que o principal do pool não tem roles/iam.workloadIdentityUser no service account, ou que o principalSet usa o número do projeto, pool ou atributo errado. Compare o identificador completo do pool com o valor do binding e confirme que o caminho do atributo usa exatamente attribute.repository/ORG/REPO.

O deployer não pode atualizar o serviço

O service account autenticado pode ser válido e ainda não ter o papel no recurso certo. Verifique roles/run.developer no serviço ou projeto, roles/artifactregistry.reader no repositório da imagem e roles/iam.serviceAccountUser na identidade de runtime. A action não substitui esses papéis.

A imagem existe, mas o Cloud Run não consegue usá-la

Separe quem publica a imagem de quem executa o container. O deployer precisa ler o repositório durante o deploy. A identidade usada em runtime precisa ter os acessos da aplicação, mas não deve receber a chave do deployer. Se o erro só aparece em uma região, confira também o nome completo do repositório e o projeto da imagem.

O exemplo funciona manualmente, mas falha no GitHub

O terminal local usa a identidade da sua sessão. O workflow usa o principal federado e a condição do provider. Refaça o diagnóstico com o repositório, branch, environment e service account reais da execução. Não coloque a chave local em um secret apenas para confirmar que os papéis estão corretos, pois isso mascara o problema que a migração queria eliminar.

Quando este modelo não é suficiente?

OIDC elimina a chave JSON armazenada, mas não elimina a necessidade de uma política de confiança. Um provider amplo, um service account com permissões administrativas ou um workflow que aceita código não revisado continua sendo um risco.

Se o job faz build, push, deploy, migração e alteração de tráfego, considere separar essas operações em jobs com permissões distintas. Se a organização exige aprovação antes da produção, use um environment protegido e associe a condição de deploy a esse fluxo. Se o recurso não aceita a identidade federada diretamente, use a federação por service account com o binding explícito e documente a exceção.

Também não confunda a identidade do deployer com a identidade do container. A primeira publica a revisão. A segunda existe quando o serviço roda e acessa Pub/Sub, Secret Manager, Cloud SQL ou outro recurso. Misturar as duas torna o rollback e a investigação de incidentes mais difíceis.

O ganho do deploy sem chave não está em trocar um bloco YAML. Está em fazer o Google Cloud responder a uma pergunta específica antes de cada publicação: este repositório, neste contexto, pode assumir esta identidade para fazer exatamente este deploy? Se a resposta não estiver expressa na condição e nos papéis, a pipeline ainda depende de confiança implícita.