Pular para o conteúdo principal

OIDC para CI

Em vez de guardar uma chave de API (mg_live_…) nos secrets do seu repositório, um job do GitHub Actions troca o seu próprio token OIDC por um token da Moatgator que dura 10 minutos. Não há nada de longa duração para vazar, e cada troca fica registrada na trilha de auditoria da sua organização.

Como funciona​

  1. No console (ou com POST /v1/apps/{id}/oidc-rules), o app ganha uma regra de confiança: qual repositório do GitHub e qual branch, tag ou ambiente podem pedir acesso, e quais escopos recebem. Sem regra, ninguém entra.
  2. No job (permissions: id-token: write), o workflow pede ao GitHub um token OIDC cuja audiência é a URL da API e o envia para POST /v1/auth/oidc com o id do app.
  3. A API verifica a assinatura (chaves do GitHub, com cache e rotação), o emissor, a audiência, a validade e as claims do repositório; confere a regra; marca o jti do token como usado (um token do GitHub vale uma vez); e devolve o token curto.
  4. O token curto vai em Authorization: Bearer … na API pública v1, com os limites do plano do app.

O CLI e a GitHub Action fazem tudo isso por você. Uma versão manual:

permissions:
id-token: write
contents: read
steps:
- name: Moatgator token
run: |
OIDC=$(curl -sH "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.moatgator.com" | jq -r .value)
TOKEN=$(curl -s https://api.moatgator.com/v1/auth/oidc \
-H 'content-type: application/json' \
-d "{\"token\":\"$OIDC\",\"appId\":\"$MOATGATOR_APP_ID\"}" | jq -r .accessToken)
echo "::add-mask::$TOKEN"
echo "MOATGATOR_TOKEN=$TOKEN" >> "$GITHUB_ENV"

Regras de confiança​

CampoSignificado
subjectPatternPadrão para o sub do token: repo:acme/app:ref:refs/heads/main, repo:acme/app:environment:production, repo:acme/app:ref:refs/tags/v*. O * casa com qualquer texto sem dois-pontos.
workflowRefOpcional. O arquivo de workflow que pode pedir um token (job_workflow_ref), por exemplo acme/app/.github/workflows/release.yml@refs/heads/main. Outro workflow do mesmo repositório não consegue trocar.
environmentOpcional. O ambiente do GitHub precisa ser exatamente este.
scopesbuilds:write, builds:read, scans:read, threats:read. Nunca events:ingest nem attest:verify: esses pertencem a aparelhos, não ao CI.

São recusados na criação: um dono de repositório curinga (repo:*/app…), um nome de repositório que seja só * e qualquer padrão sem uma restrição real de ref, ambiente ou workflow (repo:acme/app:* aceitaria até pull requests). Quando várias regras casam, os escopos são a união. No máximo 20 regras por app.

Barreiras extras​

  • Eventos de pull request nunca trocam token. pull_request e pull_request_target são recusados mesmo com uma regra que case. O pull_request_target é o perigoso: ele roda o código de um fork com o subject do branch base, o que casaria com a sua regra de produção.
  • O subject precisa pertencer ao repositório que o token declara, então subjects customizados não servem para se passar por outro repositório.
  • workflowRef limita a troca a um único arquivo de workflow.

O que a API confere no token do GitHub​

Apenas RS256 e ES256 (o algoritmo é conferido contra o tipo da chave, então none, HS256 e confusão de algoritmo são recusados); kid obrigatório; chaves RSA de pelo menos 2048 bits; iss conhecido; aud exato; exp, iat e jti obrigatórios; no máximo 1 hora de vida; 60 s de tolerância de relógio; cada jti visto uma só vez.

Recusas e auditoria​

Toda recusa devolve o mesmo 401 (app desconhecido, sem regra, repositório fora da regra, expirado, repetido…), para um atacante não descobrir quais apps têm regras. O motivo real (not_trusted, expired, replayed, bad_signature…) vai para a trilha de auditoria como oidc.denied, com o sub e nunca o token. Trocas bem-sucedidas são oidc.exchanged; criar e apagar regras são oidc.rule_created e oidc.rule_deleted.

Revogação​

  • Apagar a regra corta o acesso de tokens já emitidos, na hora (a regra é conferida a cada uso).
  • Revogar a chave de assinatura da Moatgator invalida todos os tokens curtos.
  • O token expira sozinho em 10 minutos.

Limites atuais​

  • Só o GitHub Actions (https://token.actions.githubusercontent.com) é suportado. Os outros provedores de CI usam chaves de API por enquanto.
  • Foi testado contra emissores com chaves geradas, não contra o serviço real do GitHub.
  • O token curto só é aceito pela API pública v1; o console continua usando sessões.
  • Os limites de taxa são por regra: todos os jobs que compartilham uma regra compartilham o mesmo balde.