Fase 9 (segurança e produção): - GET /api/metrics: endpoint Prometheus com métricas reais (chamadas, agentes, filas, CPS por campanha), protegido por permissão - scripts/backup.sh, restore.sh, healthcheck.sh, install.sh, update.sh — testados contra o ambiente real (backup.sh e healthcheck.sh rodados de verdade; install.sh/update.sh validados por inspeção, ambiente atual já provisionado) - POST /api/agent-console/dispose: aplica disposição de chamada de verdade (lacuna deixada aberta desde a Fase 6), com ações CALLBACK (agenda retorno) e DO_NOT_CALL (suprime automaticamente) - apps/dialer-worker/src/callback-sweep.ts: reativa leads com callback vencido; GET /api/callbacks para consulta - apps/dialer-worker/src/wrap-up-sweep.ts: transição automática WRAP_UP -> AVAILABLE + despausa real na fila do Asterisk. Exigiu corrigir main.ts para conectar ao AMI mesmo em DIALER_SIMULATION=true (DIALER_SIMULATION deve impedir só originação de chamada, não ações administrativas de fila) - nftables revisado (sem alterações necessárias) - Documentação completa: INSTALL, OPERATIONS, BACKUP_RESTORE, SECURITY, DATABASE, API, ASTERISK, OPENSIPS (não implementado, motivo documentado), TROUBLESHOOTING - README.md e CHANGELOG.md reescritos Fase 10 (testes e aceite): - Quality gate completo executado: build/typecheck (7 workspaces), lint, 46 testes unitários, docker compose config/ps, healthcheck — tudo verde - Aceite de segurança (seção 92): 13 itens verificados ao vivo contra o sistema real, não só por inspeção de código - Aceite Asterisk (seção 93): os 5 comandos executados e documentados, comunicação API->AMI->Asterisk validada - docs/RELATORIO_FINAL.md: relatório final no formato da seção 96 Todos os fixtures de teste desta fase foram removidos/desativados ao final. Credenciais de acesso entregues separadamente em CREDENCIAIS.txt (fora do git, nunca versionado).
5.6 KiB
5.6 KiB
B2BCall — Segurança
Autenticação
- Senhas com Argon2id (
argon2lib), nunca bcrypt/md5/sha. - JWT de acesso (curta duração,
JWT_ACCESS_TTL) + refresh token opaco (hash SHA-256 armazenado, nunca o token em texto puro) com rotação: a cada uso, o refresh antigo é revogado e um novo par é emitido. - Cookies
HttpOnly,SameSite=Lax,Securequando HTTPS estiver ativo.SameSite=Laxé a mitigação primária de CSRF (o navegador não envia o cookie em requisições cross-site que não sejam navegação de topo) — não há token CSRF de double-submit adicional. Suficiente para o modelo de ameaça atual (rede privada, sem terceiros hospedando conteúdo que chame a API); reavaliar se o frontend algum dia rodar em uma origem publicamente diferente da API. - Rate limiting progressivo de login por IP via Redis
(
LoginThrottleService): backoff exponencial a cada bloco de tentativas falhas, nunca bloqueio permanente sem reset. - Resposta de "esqueci minha senha" sempre genérica, independente de o e-mail existir (evita enumeração de usuários).
- Troca de senha revoga todas as sessões ativas do usuário.
Autorização (RBAC)
User→UserRole→Role→RolePermission→Permission.- Catálogo de permissões centralizado (
packages/shared/src/permissions.ts) — nenhuma checagem de permissão usa string solta espalhada pelo código. PermissionsGuardglobal, aplicado via@RequirePermissions(...)em cada rota que precisa (rotas sem decorator exigem apenas autenticação).- Ninguém pode alterar os próprios perfis de acesso
(
users.service.ts#update), mesmo tendo permissão de gerenciar usuários — bloqueia auto-elevação mesmo por um super_admin comprometido/mal configurado.
Criptografia de segredos
Trunk.secretEncrypted/Extension.sipPasswordEncrypted: AES-256-GCM (packages/shared/src/secret-crypto.ts), chave mestraSECRETS_MASTER_KEY(.env, nunca no banco, nunca versionada).- Nenhum segredo (senha de tronco, senha de ramal, tokens) é retornado em
GET/PATCH— só uma vez, no momento da criação, quando aplicável.
Auditoria
AuditServiceregistra toda ação sensível (login, criação/edição/exclusão de entidades, disposição de chamada, mudanças de compliance, etc.) comuserId,ipAddress,userAgent, estado antes/depois.- Redação automática de campos sensíveis (
SENSITIVE_KEYS) antes de persistirbefore/after— senha, secret, token nunca aparecem em texto puro no log de auditoria. - Logs estruturados JSON (
nestjs-pino/pino) comrequest_idde correlação — mesma política de redação se aplica.
Rede
- Único ponto exposto à LAN: Nginx, porta 80 (443 quando HTTPS configurado
—
docs/OPERATIONS.md). - Postgres/Redis nunca publicados fora de
127.0.0.1/rede Docker interna. - AMI (5038), ARI (8088/8089), SIP (5060/5061), RTP (10000-20000) bloqueados
da interface LAN por nftables (
infrastructure/nftables/b2bcall.nft), mesmo o Asterisk rodando emnetwork_mode: host. SSH nunca bloqueado. - CORS:
originrestrito a uma allowlist configurável (ALLOWED_ORIGINS),credentials: truesó para essas origens. - Helmet (
@fastify/helmet) ativo:X-Content-Type-Options,X-Frame-Options,Strict-Transport-Security,Cross-Origin-Opener-Policy, etc.
Validação de entrada
- Todo DTO usa
class-validator(ValidationPipeglobal,whitelist: true, forbidNonWhitelisted: true) — payload com campo não esperado é rejeitado, não ignorado silenciosamente. - Toda query ao banco via Prisma (parametrizado) — as únicas duas exceções
(
$queryRawemlead-repository.tseagent-call-binding.ts) usam apenas parâmetros tipados interpolados pelo próprio Prisma ($queryRaw\...${valor}...``), nunca concatenação de string. - Comandos de diagnóstico do Asterisk expostos via API usam allowlist
explícita de comandos (
GET /api/asterisk/diagnostic/allowed-commands) — nunca shell livre nemasterisk -rx "<input do usuário>"sem filtro.
Checklist de aceite de segurança (agente.md seção 92)
| Item | Status |
|---|---|
| Senhas com hash forte (Argon2id) | ✅ |
| Nenhuma senha padrão (bootstrap sempre aleatório) | ✅ |
| RBAC aplicado no backend (não só escondido na UI) | ✅ |
| Agente não eleva a própria permissão | ✅ |
| Segredos de tronco/ramal cifrados em repouso | ✅ |
| Postgres/Redis não expostos à LAN | ✅ |
| AMI/ARI/SIP não expostos à LAN | ✅ (nftables) |
| Rate limiting de login | ✅ |
| Auditoria de ações sensíveis, sem vazar segredos | ✅ |
| Logs estruturados sem credenciais | ✅ |
.env/segredos nunca versionados no git |
✅ |
| Validação de entrada em todos os DTOs | ✅ |
| Sem SQL injection (Prisma parametrizado em toda parte) | ✅ |
| Sem shell injection (allowlist de comandos Asterisk) | ✅ |
| CSRF | ⚠️ Mitigado via SameSite=Lax, sem token dedicado (ver acima) |
| HTTPS | ⚠️ Não configurado nesta fase (rede privada) — ver docs/OPERATIONS.md |
Como reportar/tratar um incidente
- Revogar sessões:
UPDATE sessions SET revoked_at = now() WHERE revoked_at IS NULL;(ou endpoint de logout em massa, se necessário adicionar). - Rotacionar segredos: gerar novos valores com
scripts/generate-secrets.sh, atualizar.env, reiniciarapi. RotacionarSECRETS_MASTER_KEYexige redescriptografar e recriptografar todos osTrunk.secretEncrypted/Extension.sipPasswordEncrypted(não automatizado nesta fase — script futuro se necessário). - Consultar
AuditLogfiltrando poruserId/entityType/janela de tempo para reconstituir o que aconteceu.