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 tradicional | Com SSH CA |
|---|---|
| Cada user tem chave SSH em cada host | Chave única + cert efêmero TTL ≤ 24h |
authorized_keys em cada host atualizado manualmente | TrustedUserCAKeys aponta para chave pública da CA |
Usuário desligado → caça authorized_keys em N hosts | Cert expira sozinho; KRL revoga em segundos |
| Rotação de chave SSH = exercício de paciência | Rotação imediata via CV |
| Auditoria espalhada em logs SSH | Auditoria 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-keygensubprocess — bibliotecas Node.js (sshpk) não suportam principals e extensions OpenSSH cert format - Lazy-bootstrap — CA é gerada na primeira chamada de
/ssh/ca/initpor 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-commandem roles produção — limita o que cert permite executar (/usr/local/bin/safe-deployem 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_issuedfora de horário ou para principals incomuns.