Pular para o conteúdo principal

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)

ProviderIssuerSub exemplo
GitHub Actionshttps://token.actions.githubusercontent.comrepo:org/repo:ref:refs/heads/main
GitLab CIhttps://gitlab.comproject_path:group/project:ref_type:branch:ref:main
CircleCIhttps://oidc.circleci.com/org/<ORG_ID>org/<ORG_ID>/project/<P_ID>/user/<U_ID>
Jenkinsconfigurável (plugin OIDC Provider)http://<host>/job/<name>/
Bambooconfigurável (plugin OIDC)bamboo:plan:<PLAN_KEY>
Customlivre (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)

  1. Conexão precisa ter oidc_enabled=true + ≥ 1 pattern + ≥ 1 provider autorizado
  2. Decodifica JWT (sem verificar) e extrai claim iss
  3. Procura cloud_integrations cujo credentials.issuer === iss, filtrado pela whitelist oidc_provider_ids da conexão (vazio = permissivo)
  4. Busca chave pública no JWKS do provider (cache 5min via jwks-rsa)
  5. Valida alg (RS256/384/512), iss, aud (se setado), exp, iat (≤ 10 min)
  6. Match sub contra oidc_subject_patterns (wildcard * dentro de 1 segmento)
  7. Checa IP allowlist da conexão
  8. Emite TLS + SIG em transação (FOR UPDATE) + audit federated_token_issued
  9. 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_patterns restritivo — sempre filtre por repositório, branch e environment.
  • AppConnections separadas por ambientedeploy-staging, deploy-producao.
  • TTL curto — 1 dia é suficiente para deploys; jobs longos podem ir até 7 dias.
  • iat window 10min — id_tokens com timestamp antigo são rejeitados.
  • Audit trail — configure alerta para federated_token_issued fora dos repositórios esperados.