Operações da API do DUT — registrando dispositivos e o worker de polling¶
Leia no seu idioma: English · Português · Español
Status do escopo (pós-congelamento de escopo 2026-05-10) — Ver ARCHITECTURE.md para os 37 MÓDULOs canônicos + 7 Test Kinds + arquitetura de safety DOM/CPOS/PIE-PA. ADRs 0014, 0019-0025 cobrem adições pós-Freeze. Este é o guia voltado ao operador para a integração com a API do DUT. Leia
DUT_API_INTEGRATION.pt-BR.mdprimeiro para entender a arquitetura; leiaAPI_FEATURE_CATALOG.pt-BR.mdpara saber quais funcionalidades a integração com a API destrava. Este documento explica como de fato USÁ-LA: como registrar um dispositivo, como o worker de polling se comporta, como disparar um snapshot manual e como fazer debug.
Worker de polling — o que ele faz¶
Uma vez que um dispositivo é registrado, um worker de polling dentro do pod do dashboard o consulta automaticamente em uma cadência. O worker:
- Roda a cada 30 segundos (intervalo de varredura — não significa que cada dispositivo seja consultado a cada 30s)
- Seleciona dispositivos onde
last_poll_at + poll_interval_seconds < now()OUlast_poll_at IS NULL - Para cada dispositivo elegível:
- Decifra a senha usando
DUT_CRED_ENC_KEY - Instancia o adapter apropriado (Cisco FTD, Cisco Nexus, Cisco UCS, Fortinet)
- Chama o método
collectAll*()do adapter - Persiste cada snapshot em
dut_api_snapshotscom SHA-256 - Atualiza os campos
last_poll_at,last_poll_status,last_poll_errorda linha do dispositivo - Seguro com múltiplas réplicas via advisory lock do Postgres: se dois pods do dashboard estiverem rodando, apenas um faz polling por vez
Desabilitando o worker¶
# Set the env var in the dashboard's K8s Deployment:
DUT_API_POLLER_DISABLE=1
O worker registra um aviso de desabilitação único na inicialização e retorna. Snapshots manuais via endpoint da API continuam funcionando.
Intervalo de polling padrão¶
poll_interval_seconds tem valor padrão de 300 (5 min) no momento do registro. Override por dispositivo é permitido (mínimo 30 s). Valores recomendados:
| Caso de uso | Intervalo |
|---|---|
| Refresh de inventário em background | 300 s (padrão) |
| Engajamento ativo, dashboard "DUT Live State" | 60 s |
| Execuções de baseline forense (SOAK longo) | 30 s |
| Momento de pre-flight check (one-shot, não periódico) | use o endpoint POST /snapshot em vez disso |
Setup único — chave de criptografia¶
As credenciais são armazenadas criptografadas com AES-256-GCM usando DUT_CRED_ENC_KEY (env var). Antes de registrar o primeiro dispositivo, provisione a chave:
# 1. Generate the key
KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
# 2. Provision via K8s Secret
kubectl create secret generic tlsstress-dut-cred \
--from-literal=DUT_CRED_ENC_KEY="$KEY" \
--namespace web-agents
# 3. Wire to the dashboard Deployment env
kubectl patch deployment dashboard --namespace web-agents \
--patch='{"spec":{"template":{"spec":{"containers":[{"name":"dashboard","env":[{"name":"DUT_CRED_ENC_KEY","valueFrom":{"secretKeyRef":{"name":"tlsstress-dut-cred","key":"DUT_CRED_ENC_KEY"}}}]}]}}}}'
# 4. Restart the dashboard to pick up the env var
kubectl rollout restart deployment dashboard --namespace web-agents
API — registrando dispositivos¶
Todos os endpoints exigem autenticação de admin (a mesma autenticação que o restante da UI de admin usa).
Listar vendors + dispositivos¶
GET /api/admin/dut/devices
Resposta:
{
"devices": [
{ "id": "...", "hostname": "ftd-1.lab", "vendor": "cisco-ftd", "lastPollStatus": "ok", ... }
],
"vendors": [
{ "key": "cisco-ftd", "displayName": "Cisco FTD (FDM API)", "available": true },
{ "key": "cisco-nexus", "displayName": "Cisco Nexus 9000 (NX-API DME)", "available": true },
{ "key": "cisco-ucs-cimc", "displayName": "Cisco UCS C-Series CIMC", "available": true },
{ "key": "fortinet-fortigate", "displayName": "Fortinet FortiGate (REST v2)", "available": true },
{ "key": "palo-alto-panos", "displayName": "Palo Alto (not yet implemented)","available": false }
]
}
Registrar um Cisco FTD¶
curl -X POST "https://dashboard.example/api/admin/dut/devices" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"hostname": "ftd-1.lab.example.com",
"vendor": "cisco-ftd",
"deviceRole": "ngfw",
"baseUrl": "https://10.10.10.1",
"username": "admin",
"password": "<the-ftd-password>",
"tlsVerifyMode": "self-signed",
"pollIntervalSeconds": 300,
"notes": "Cisco FTD 7.4 on FPR1010 — primary in HA pair"
}'
# {"ok": true, "deviceId": "..."}
Registrar um Fortinet FortiGate (com API token)¶
curl -X POST "https://dashboard.example/api/admin/dut/devices" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{
"hostname": "fortigate-1.lab.example.com",
"vendor": "fortinet-fortigate",
"deviceRole": "ngfw",
"baseUrl": "https://10.10.10.5",
"username": "apitoken",
"password": "<the-api-token>",
"tlsVerifyMode": "strict",
"pollIntervalSeconds": 300
}'
Nota: definir username como o literal apitoken sinaliza ao adapter que use autenticação por API-token (Bearer); caso contrário, ele cai para autenticação de sessão baseada em cookie.
Registrar um Cisco Nexus 9000¶
# Pre-requisite on the switch:
# configure terminal
# feature nxapi
# nxapi https port 443
# nxapi use-vrf management
# end
curl -X POST "https://dashboard.example/api/admin/dut/devices" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{
"hostname": "nexus-1.lab.example.com",
"vendor": "cisco-nexus",
"deviceRole": "switch",
"baseUrl": "https://10.10.10.10",
"username": "admin",
"password": "<password>",
"tlsVerifyMode": "self-signed",
"pollIntervalSeconds": 300
}'
Testar a conexão¶
curl -X POST "https://dashboard.example/api/admin/dut/devices/<id>/test" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# {"ok": true, "detail": "auth OK; CPU monitor reachable in 142ms", "latencyMs": 142}
Se ok: false, o campo detail informa o que falhou (auth, TLS, timeout, erro HTTP).
Disparar um snapshot manual¶
Para inspeção ad-hoc ou imediatamente antes de uma execução crítica:
curl -X POST "https://dashboard.example/api/admin/dut/devices/<id>/snapshot" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# {
# "ok": true,
# "lastPollStatus": "ok",
# "lastPollError": "collected 7 snapshot(s)",
# "lastPollAt": "2026-05-06T14:35:00.000Z"
# }
Isto chama pollOneDevice() de forma síncrona; a resposta confirma quantos snapshots foram coletados.
Navegar pelos snapshots¶
# Latest 100 snapshots across all devices
curl "https://dashboard.example/api/admin/dut/snapshots" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Filter by device + endpoint label
curl "https://dashboard.example/api/admin/dut/snapshots?deviceId=...&endpointLabel=decrypt_policy" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Snapshots for a specific test run
curl "https://dashboard.example/api/admin/dut/snapshots?testRunExecutionId=<uuid>" \
-H "Authorization: Bearer $ADMIN_TOKEN"
A resposta inclui payloadSha256 para cada um — o hash ancora os anexos do Test Run Report.
Atualizar um dispositivo¶
# Disable polling temporarily
curl -X PATCH "https://dashboard.example/api/admin/dut/devices/<id>" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"enabled": false}'
# Rotate the password
curl -X PATCH "https://dashboard.example/api/admin/dut/devices/<id>" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"password": "<new-password>"}'
# Change polling cadence
curl -X PATCH "https://dashboard.example/api/admin/dut/devices/<id>" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{"pollIntervalSeconds": 60}'
Excluir um dispositivo¶
curl -X DELETE "https://dashboard.example/api/admin/dut/devices/<id>" \
-H "Authorization: Bearer $ADMIN_TOKEN"
O cascade também exclui todos os snapshots históricos do dispositivo (FK ON DELETE CASCADE na migration).
Debugging¶
O poller não está pegando novos dispositivos¶
- Verifique
kubectl logs deployment/dashboard | grep '\[dut-api/poller\]'procurando a linha de startup - Verifique se
DUT_API_POLLER_DISABLEnão está setado como1 - Verifique se
DUT_CRED_ENC_KEYestá provisionado (veja o setup único acima)
Dispositivo mostra last_poll_status: auth_failed¶
- Teste a conexão manualmente com o endpoint
/test - Verifique se o usuário de API do dispositivo tem as permissões corretas (FTD: API user role; Nexus: NX-API habilitado no profile do usuário; Fortinet: API admin profile)
- Cheque os logs do lado do dispositivo (ex.: FTD
/var/log/audit.log)
Dispositivo mostra last_poll_status: tls_error¶
- Defina
tlsVerifyMode: 'self-signed'para dispositivos com certificados self-signed (o padrãostrictos rejeita) - Para self-signed, o SHA-256 do cert é fixado (pinned) na primeira conexão; se o cert rotacionar, retornam erros de TLS — o operador deve limpar
pinned_cert_sha256para aceitar o novo cert
Snapshots param de chegar mas nenhum erro é logado¶
- Verifique contenção do advisory lock do Postgres: se dois pods do dashboard estão competindo, apenas um faz polling. Confirme replicas=1 OU aceite que os snapshots cheguem na metade da cadência configurada (cada pod pega o lock metade do tempo).
- Cheque o espaço em disco do volume do Postgres —
dut_api_snapshotscresce em modo append-only.
Modelo de segurança¶
- Credenciais em repouso: AES-256-GCM em BYTEA do Postgres. A chave é a env var
DUT_CRED_ENC_KEY(32 bytes), armazenada em K8s Secret. - Credenciais em trânsito (dashboard → dispositivo): HTTPS.
tlsVerifyModeé por dispositivo (strict / self-signed / insecure). - Credenciais em memória: apenas durante um ciclo de polling; limpas após
disconnect(). - Os endpoints da API exigem autenticação de admin (a mesma do restante da UI de admin).
- Os endpoints Test Connection / Manual Snapshot são ações de write — auth de admin é re-checada por chamada.
Performance¶
A cadência padrão (1 dispositivo a cada 5 min) gera aproximadamente:
- 7 snapshots por FTD por poll → 84 snapshots/hora/dispositivo
- 6 snapshots por Nexus por poll → 72 snapshots/hora/dispositivo
- 8 snapshots por UCS por poll → 96 snapshots/hora/dispositivo
Para um lab típico de 4 dispositivos (1 NGFW + 1 switch + 2 UCS), espere ~350 snapshots/hora, ~8400/dia, ~250k/mês. A ~250 bytes por snapshot (com payload), isso dá ~63 MB/mês — trivial para o Postgres.
Para uma cadência agressiva de 30 s no mesmo lab: ~3.5k snapshots/hora, ~84k/dia, ~2.5M/mês, ~625 MB/mês — ainda tranquilo, mas vale uma política de retention para dut_api_snapshots.
Retention¶
Hoje não há retention automático. Operadores que queiram podar snapshots antigos:
DELETE FROM dut_api_snapshots
WHERE collected_at < now() - interval '30 days';
Rode isso como um CronJob do Postgres se seu lab gerar volume alto.
Futuro: um campo configurável dut_api_snapshot_retention_days por dispositivo + prune automático (escopo do PR-D).
Relacionados¶
DUT_API_INTEGRATION.pt-BR.md— arquiteturaAPI_FEATURE_CATALOG.pt-BR.md— quais funcionalidades a API destrava (45 catalogadas)SYSLOG_OPERATIONS.pt-BR.md— guia de operações do segundo pilarUSAGE_POLICY.pt-BR.md— restrições de licença se aplicam