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
- 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. - No job (
permissions: id-token: write), o workflow pede ao GitHub um token OIDC cuja audiência é a URL da API e o envia paraPOST /v1/auth/oidccom o id do app. - 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
jtido token como usado (um token do GitHub vale uma vez); e devolve o token curto. - 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
| Campo | Significado |
|---|---|
subjectPattern | Padrã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. |
workflowRef | Opcional. 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. |
environment | Opcional. O ambiente do GitHub precisa ser exatamente este. |
scopes | builds: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_requestepull_request_targetsão recusados mesmo com uma regra que case. Opull_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.
workflowReflimita 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.