OIDC Federation para CI/CD
Pipelines CI/CD historicamente armazenam secrets do cofre como variáveis do GitHub/GitLab/Jenkins. Esses tokens vazam em logs, ficam ativos mesmo após pipeline terminar e exigem rotação manual.
O CipherVault elimina esse anti-pattern com OIDC Federation: o
pipeline apresenta o id_token do provedor de CI/CD, o CipherVault valida
e devolve um bundle de certs frescos (TLS + assinatura) válido pelo
TTL configurado. Zero segredo pré-compartilhado.
A partir da v1.3 o modelo foi refatorado em duas responsabilidades separadas, descritas abaixo.
Arquitetura (modelo v1.3)
┌──────────────────────────────────┐
│ cloud_integrations │ ← provider config (1 por tenant)
│ categoria CI/CD Federation │
│ │
│ • issuer │
│ • jwks_url │
│ • audience │
└──────────────┬───────────────────┘
│ vinculação por id
▼
┌──────────────────────────────────┐
│ app_connections │ ← authorization (1 por aplicação)
│ │
│ • oidc_enabled │
│ • oidc_subject_patterns[] │
│ • oidc_provider_ids[] │ (whitelist de provider configs)
└──────────────────────────────────┘
Endpoint público de troca:
POST /app-connections/:id/federated-token
Body: { id_token, days? }
Provedores suportados (presets automáticos)
| Provider | Issuer | Sub exemplo |
|---|---|---|
| GitHub Actions | https://token.actions.githubusercontent.com | repo:org/repo:ref:refs/heads/main |
| GitLab CI | https://gitlab.com | project_path:group/project:ref_type:branch:ref:main |
| CircleCI | https://oidc.circleci.com/org/<ORG_ID> | org/<ORG_ID>/project/<P_ID>/user/<U_ID> |
| Jenkins | configurável (plugin OIDC Provider) | http://<host>/job/<name>/ |
| Bamboo | configurável (plugin OIDC) | bamboo:plan:<PLAN_KEY> |
| Custom | livre (issuer + JWKS URL) | qualquer |
1. Configurar provider (uma vez por tenant)
Como admin, cadastre o provider OIDC em Integrations → CI/CD Federation. Para GitHub Actions, GitLab CI e CircleCI o preset auto-preenche issuer/jwks/audience.
Via API:
curl -X POST https://cv.acme.com.br/cloud-integrations \
-H "Authorization: Bearer $CV_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"category": "ci_cd_federation",
"provider": "oidc_github",
"credentials": {
"issuer": "https://token.actions.githubusercontent.com",
"jwks_url": "https://token.actions.githubusercontent.com/.well-known/jwks",
"audience": "https://cv.acme.com.br"
}
}'
# Validar reachability
curl -X POST https://cv.acme.com.br/cloud-integrations/<id>/test
2. Autorizar AppConnection a federar com o provider
curl -X PUT https://cv.acme.com.br/app-connections/<conn_id>/oidc \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{
"oidc_enabled": true,
"oidc_subject_patterns": [
"repo:acme-corp/billing-api:ref:refs/heads/main",
"repo:acme-corp/billing-api:environment:production"
],
"oidc_provider_ids": [42]
}'
Wildcards * são suportados, mas dentro de 1 segmento (não cruzam
:). oidc_provider_ids vazio = permissivo (qualquer provider OIDC
configurado no tenant).
3. Usar no pipeline
GitHub Actions
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Login no CipherVault via OIDC
id: cv
uses: ciphervault/oidc-action@v1
with:
conn-id: app_01HXY...
base-url: https://cv.acme.com.br
# action obtém id_token, troca por bundle, expõe certs/PEMs em outputs
- name: Buscar secret e fazer deploy
run: ./deploy.sh
env:
STRIPE_KEY: ${{ steps.cv.outputs.api_stripe_secret_key }}
GitLab CI
deploy:
image: ciphervault/oidc-cli:latest
id_tokens:
CV_ID_TOKEN:
aud: https://cv.acme.com.br
script:
- cv-oidc-login --conn app_01HXY... --token-env CV_ID_TOKEN
- ./deploy.sh
Bash genérico
ID_TOKEN=$(...) # do runner CI/CD
curl -X POST https://cv.acme.com.br/app-connections/app_01HXY.../federated-token \
-H "Content-Type: application/json" \
-d "{\"id_token\": \"$ID_TOKEN\", \"days\": 1}"
# Resposta inclui PEMs (TLS + signing) — pipeline escreve em tmpfs e usa mTLS+DPoP
Fluxo backend (lib/federation.js)
- Conexão precisa ter
oidc_enabled=true+ ≥ 1 pattern + ≥ 1 provider autorizado - Decodifica JWT (sem verificar) e extrai claim
iss - Procura
cloud_integrationscujocredentials.issuer === iss, filtrado pela whitelistoidc_provider_idsda conexão (vazio = permissivo) - Busca chave pública no JWKS do provider (cache 5min via
jwks-rsa) - Valida
alg(RS256/384/512),iss,aud(se setado),exp,iat(≤ 10 min) - Match
subcontraoidc_subject_patterns(wildcard*dentro de 1 segmento) - Checa IP allowlist da conexão
- Emite TLS + SIG em transação (
FOR UPDATE) + auditfederated_token_issued - Retorna PEMs em JSON
Endpoints legacy (410 Gone)
A partir de v1.3 os endpoints /app-connections/:id/federation-configs/*
retornam HTTP 410 Gone. UI antigo FederationManager.jsx (428 linhas)
substituído por OidcFederationToggle.jsx simplificado na detail page da
AppConnection.
Boas práticas
oidc_subject_patternsrestritivo — sempre filtre por repositório, branch e environment.- AppConnections separadas por ambiente —
deploy-staging,deploy-producao. - TTL curto — 1 dia é suficiente para deploys; jobs longos podem ir até 7 dias.
iatwindow 10min — id_tokens com timestamp antigo são rejeitados.- Audit trail — configure alerta para
federated_token_issuedfora dos repositórios esperados.