Pular para o conteúdo principal

GitHub Actions com OIDC Federation

Configure deploy seguro a partir do GitHub Actions sem armazenar tokens estáticos do CipherVault como secrets do repositório. O workflow apresenta o JWT OIDC do próprio GitHub, o CipherVault valida e devolve um token efêmero.

Pré-requisitos

  • Repositório GitHub.
  • Vault producao criado no CipherVault.
  • Permissão admin no tenant CipherVault.

1. Registrar o provedor OIDC

curl -X POST https://api.ciphervault.com.br/v1/oidc/providers \
-H "Authorization: Bearer $CIPHERVAULT_TOKEN" \
-d '{
"name": "github-acme",
"issuer": "https://token.actions.githubusercontent.com",
"audience": "https://ciphervault.com.br",
"claims_required": {
"repository_owner": "acme-corp"
}
}'

claims_required impede que workflows de outras orgs/forks consigam trocar tokens.

2. Criar policy para o deploy

curl -X POST https://api.ciphervault.com.br/v1/policies \
-H "Authorization: Bearer $CIPHERVAULT_TOKEN" \
-d '{
"name": "deploy-billing-prod",
"document": {
"version": "2025-01-01",
"statement": [
{
"sid": "read-billing-secrets",
"effect": "allow",
"action": ["secrets:read"],
"resource": "vault/producao/api/stripe/*"
},
{
"sid": "read-db",
"effect": "allow",
"action": ["secrets:read"],
"resource": "vault/producao/db/postgres/billing-master"
}
]
}
}'

3. Criar a role federada

curl -X POST https://api.ciphervault.com.br/v1/oidc/roles \
-H "Authorization: Bearer $CIPHERVAULT_TOKEN" \
-d '{
"name": "deploy-billing-prod",
"provider": "github-acme",
"trust": {
"repository": "acme-corp/billing-api",
"ref": "refs/heads/main",
"environment": "production"
},
"policies": ["deploy-billing-prod"],
"ttl_seconds": 900
}'

A combinação repository + ref + environment garante que apenas pushes para main no repo billing-api com environment production (com sua proteção de approvals do GitHub) recebem o token.

4. Workflow

.github/workflows/deploy-prod.yml:

name: Deploy production

on:
push:
branches: [main]

permissions:
id-token: write # necessário para OIDC
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
environment: production # exige approvals
steps:
- uses: actions/checkout@v4

- name: Login no CipherVault via OIDC
id: cv
uses: ciphervault/oidc-action@v1
with:
role: deploy-billing-prod
vault: producao
paths: |
api/stripe/secret_key
api/stripe/webhook_secret
db/postgres/billing-master
mask-outputs: true

- name: Deploy
run: ./deploy.sh
env:
STRIPE_KEY: ${{ steps.cv.outputs.api_stripe_secret_key }}
STRIPE_WEBHOOK: ${{ steps.cv.outputs.api_stripe_webhook_secret }}
DB_PASSWORD: ${{ steps.cv.outputs.db_postgres_billing_master }}

Pontos importantes:

  • id-token: write — sem isso, o GitHub não emite JWT OIDC.
  • environment: production — invoca proteções (approvals, branch policies).
  • mask-outputs: true — outputs são automaticamente mascarados nos logs.

5. Verificar auditoria

Cada execução fica registrada:

curl "https://api.ciphervault.com.br/v1/audit?\
action=oidc:exchange&\
context.role=deploy-billing-prod" \
-H "Authorization: Bearer $CIPHERVAULT_TOKEN"

Retorna eventos com workflow_id, commit_sha, actor (quem disparou), repository e environment.

6. Adicione o CI/CD Scanner

No mesmo repositório, adicione check de secrets em PRs:

# .github/workflows/secrets-scan.yml
name: secrets-scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ciphervault/scanner-action@v1
with:
fail-on: high
report-to-cv: true

Configure como required check em Settings → Branches → main.

Troubleshooting

403 INVALID_OIDC_CLAIM

O JWT do GitHub não bate com claims_required. Verifique:

  • repository (org + repo)
  • ref (refs/heads/main, refs/tags/v1.0.0)
  • environment (definido no workflow + GitHub Environment criado)

403 ROLE_NOT_TRUSTED

A role existe mas o trust não casa com o JWT. Use o decoder em https://api.ciphervault.com.br/v1/oidc/inspect (autenticado) para ver o JWT decodificado e ajustar.

Token chega vazio em job de fork

PRs de forks não recebem id-token: write por segurança do GitHub — isso é esperado. Use pull_request_target apenas com extremo cuidado, ou restrinja o deploy a push na branch principal.