Skip to content

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.md primeiro para entender a arquitetura; leia API_FEATURE_CATALOG.pt-BR.md para 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:

  1. Roda a cada 30 segundos (intervalo de varredura — não significa que cada dispositivo seja consultado a cada 30s)
  2. Seleciona dispositivos onde last_poll_at + poll_interval_seconds < now() OU last_poll_at IS NULL
  3. Para cada dispositivo elegível:
  4. Decifra a senha usando DUT_CRED_ENC_KEY
  5. Instancia o adapter apropriado (Cisco FTD, Cisco Nexus, Cisco UCS, Fortinet)
  6. Chama o método collectAll*() do adapter
  7. Persiste cada snapshot em dut_api_snapshots com SHA-256
  8. Atualiza os campos last_poll_at, last_poll_status, last_poll_error da linha do dispositivo
  9. 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.

# 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_DISABLE não está setado como 1
  • Verifique se DUT_CRED_ENC_KEY está 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ão strict os 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_sha256 para 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_snapshots cresce 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