Pular para o conteúdo principal

SSH Certificate Authority

A partir da v1.7.3, o CipherVault opera como SSH Certificate Authority — emite certificados SSH efêmeros assinados pela CA do tenant, substituindo o modelo authorized_keys distribuído + jumphost.

A v3.0 adicionou suporte a múltiplas CAs por tenant com hierarquia (parent_ca_id), endpoint GET /ssh/cas/:id/chain?format=authorized_keys para distribuir trust chain, e seletor ca_id em roles.

Por que SSH CA

Modelo tradicionalCom SSH CA
Cada user tem chave SSH em cada hostChave única + cert efêmero TTL ≤ 24h
authorized_keys em cada host atualizado manualmenteTrustedUserCAKeys aponta para chave pública da CA
Usuário desligado → caça authorized_keys em N hostsCert expira sozinho; KRL revoga em segundos
Rotação de chave SSH = exercício de paciênciaRotação imediata via CV
Auditoria espalhada em logs SSHAuditoria centralizada em CV

Setup (uma vez por tenant)

1. Inicializar CA

UI: Settings → SSH CA → Inicializar. Ou via API:

curl -X POST https://cv.acme.com.br/ssh/ca/init \
-H "Authorization: Bearer $CV_TOKEN"

Backend gera keypair Ed25519 e armazena chave privada cifrada (KMS envelope encryption). Chave pública fica disponível em GET /ssh/ca (endpoint público).

2. Distribuir chave pública para hosts

curl https://cv.acme.com.br/ssh/ca > /tmp/cv_ssh_ca.pub
sudo cp /tmp/cv_ssh_ca.pub /etc/ssh/cv_ssh_ca.pub
sudo chmod 644 /etc/ssh/cv_ssh_ca.pub

3. Configurar sshd_config

# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/cv_ssh_ca.pub

# Opcional: revocation
RevokedKeys /etc/ssh/cv_ssh_krl
sudo systemctl reload sshd

Roles

Roles definem quem pode pedir certs e com que parâmetros:

curl -X POST https://cv.acme.com.br/ssh/roles \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{
"name": "deploy-prd",
"default_principals": ["deploy"],
"allowed_principals": ["deploy", "release-bot"],
"default_ttl_sec": 3600,
"max_ttl_sec": 14400,
"cert_options": {
"force-command": "/usr/local/bin/safe-deploy",
"source-address": "10.0.0.0/8"
},
"cert_extensions": {
"permit-pty": true,
"permit-port-forwarding": false
}
}'

Cap absoluto: TTL máximo 24h independente de max_ttl_sec.

Assinar chave pública

Via API

curl -X POST https://cv.acme.com.br/ssh/roles/1/sign \
-H "Authorization: Bearer $CV_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"public_key\": \"$(cat ~/.ssh/id_ed25519.pub)\",
\"principals\": [\"deploy\"],
\"ttl\": 3600,
\"reason\": \"Deploy hotfix billing-api PR-1234\"
}" | jq -r .certificate > ~/.ssh/id_ed25519-cert.pub

reason mínimo 5 chars — registrado em audit log.

Via CLI Go cv

cv ssh sign ~/.ssh/id_ed25519.pub \
--role-id 1 \
--reason "Deploy hotfix billing-api PR-1234" \
> ~/.ssh/id_ed25519-cert.pub

Conectar

ssh -i ~/.ssh/id_ed25519 deploy@host-bastion.acme.com.br
# OpenSSH detecta o cert (-cert.pub no mesmo diretório) automaticamente

Inspecionar cert emitido

ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
Type: ssh-ed25519-cert-v01@openssh.com user certificate
Public key: ED25519-CERT SHA256:...
Signing CA: ED25519 SHA256:... (using ssh-ed25519)
Key ID: "user@acme.com.br|reason:deploy hotfix"
Serial: 1234567
Valid: from 2026-05-05T10:00:00 to 2026-05-05T11:00:00
Principals: deploy
Critical Options: force-command "/usr/local/bin/safe-deploy"
source-address "10.0.0.0/8"
Extensions: permit-pty

Revogação (KRL)

# Admin revoga um cert pelo serial
curl -X POST https://cv.acme.com.br/ssh/certs/1234567/revoke \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{ "reason": "Possível compromisso da chave do user X" }'

KRL pública:

curl https://cv.acme.com.br/ssh/krl > /etc/ssh/cv_ssh_krl
sudo systemctl reload sshd

Sugestão: cron 5min em cada host para puxar KRL atualizado.

Implementação

  • Backend usa ssh-keygen subprocess — bibliotecas Node.js (sshpk) não suportam principals e extensions OpenSSH cert format
  • Lazy-bootstrap — CA é gerada na primeira chamada de /ssh/ca/init por tenant
  • Tabelas: ssh_ca (chave privada cifrada por tenant), ssh_roles, ssh_signed_certs (audit + serials)

Endpoints

GET /ssh/ca PEM da chave pública da CA default (público, legacy)
GET /ssh/cas Lista CAs do tenant (v3.0+)
GET /ssh/cas/:id/chain?format=... Chain de trust em authorized_keys ou JSON (v3.0+)
GET /ssh/krl Binário KRL (público)
POST /ssh/ca/init Inicializar CA default (admin, uma vez)
POST /ssh/cas Criar CA adicional com parent_ca_id opcional (v3.0+)
POST /ssh/roles CRUD de roles (admin) — aceita ca_id (v3.0+)
POST /ssh/roles/:id/sign Assinar pubkey (user com permissão)
GET /ssh/certs Listar certs emitidos (audit)
POST /ssh/certs/:serial/revoke

Boas práticas

  • TTL curto por padrão — 1h para deploys, 4h para sessões interativas. Cap 24h é o teto absoluto.
  • force-command em roles produção — limita o que cert permite executar (/usr/local/bin/safe-deploy em vez de shell).
  • source-address — restrinja CIDR de origem em roles de produção.
  • KRL pull cron 5min — propagação rápida de revogação.
  • Não mantenha chave privada do user em laptop — combine com YubiKey ou similar para chave protegida por hardware.
  • Audit cruzado — alerta SIEM para ssh_cert_issued fora de horário ou para principals incomuns.