Operaciones de la API del DUT — registrando dispositivos y el worker de polling¶
Lee en tu idioma: English · Português · Español
Estado del alcance (post-congelación de alcance 2026-05-10) — Ver ARCHITECTURE.md para los 37 MÓDULOs canónicos + 7 Test Kinds + arquitectura de safety DOM/CPOS/PIE-PA. ADRs 0014, 0019-0025 cubren adiciones post-Freeze. Esta es la guía orientada al operador para la integración con la API del DUT. Lee primero
DUT_API_INTEGRATION.es.mdpara entender la arquitectura; leeAPI_FEATURE_CATALOG.es.mdpara saber qué funcionalidades desbloquea la integración con la API. Este documento explica cómo USARLA realmente: cómo registrar un dispositivo, cómo se comporta el worker de polling, cómo disparar un snapshot manual y cómo depurar.
Worker de polling — qué hace¶
Una vez que un dispositivo está registrado, un worker de polling dentro del pod del dashboard lo consulta automáticamente según una cadencia. El worker:
- Se ejecuta cada 30 segundos (intervalo de barrido — no significa que cada dispositivo sea consultado cada 30s)
- Selecciona dispositivos donde
last_poll_at + poll_interval_seconds < now()Olast_poll_at IS NULL - Para cada dispositivo elegible:
- Descifra la contraseña usando
DUT_CRED_ENC_KEY - Instancia el adapter apropiado (Cisco FTD, Cisco Nexus, Cisco UCS, Fortinet)
- Llama al método
collectAll*()del adapter - Persiste cada snapshot en
dut_api_snapshotscon SHA-256 - Actualiza los campos
last_poll_at,last_poll_status,last_poll_errorde la fila del dispositivo - Seguro con múltiples réplicas mediante advisory lock de Postgres: si dos pods del dashboard están corriendo, solo uno hace polling a la vez
Deshabilitando el worker¶
# Set the env var in the dashboard's K8s Deployment:
DUT_API_POLLER_DISABLE=1
El worker registra un aviso de deshabilitación único en el arranque y retorna. Los snapshots manuales vía endpoint de la API siguen funcionando.
Intervalo de polling por defecto¶
poll_interval_seconds tiene valor por defecto de 300 (5 min) al momento del registro. Se permite override por dispositivo (mínimo 30 s). Valores recomendados:
| Caso de uso | Intervalo |
|---|---|
| Refresh de inventario en background | 300 s (por defecto) |
| Engagement activo, dashboard "DUT Live State" | 60 s |
| Ejecuciones de baseline forense (SOAK largo) | 30 s |
| Momento de pre-flight check (one-shot, no periódico) | usa el endpoint POST /snapshot en su lugar |
Setup único — clave de cifrado¶
Las credenciales se almacenan cifradas con AES-256-GCM usando DUT_CRED_ENC_KEY (env var). Antes de registrar el primer dispositivo, aprovisiona la clave:
# 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 los endpoints requieren autenticación de admin (la misma autenticación que usa el resto de la UI de admin).
Listar vendors + dispositivos¶
GET /api/admin/dut/devices
Respuesta:
{
"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 un 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 un Fortinet FortiGate (con 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 con el literal apitoken le indica al adapter que use autenticación por API-token (Bearer); de lo contrario, recae en autenticación de sesión basada en cookie.
Registrar un 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
}'
Probar la conexión¶
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}
Si ok: false, el campo detail indica qué falló (auth, TLS, timeout, error HTTP).
Disparar un snapshot manual¶
Para inspección ad-hoc o justo antes de una ejecución 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"
# }
Esto llama a pollOneDevice() de forma síncrona; la respuesta confirma cuántos snapshots fueron recolectados.
Navegar por los 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"
La respuesta incluye payloadSha256 para cada uno — el hash ancla los anexos del Test Run Report.
Actualizar un 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}'
Eliminar un dispositivo¶
curl -X DELETE "https://dashboard.example/api/admin/dut/devices/<id>" \
-H "Authorization: Bearer $ADMIN_TOKEN"
El cascade también elimina todos los snapshots históricos del dispositivo (FK ON DELETE CASCADE en la migration).
Debugging¶
El poller no está tomando nuevos dispositivos¶
- Revisa
kubectl logs deployment/dashboard | grep '\[dut-api/poller\]'buscando la línea de startup - Verifica que
DUT_API_POLLER_DISABLEno esté seteado en1 - Verifica que
DUT_CRED_ENC_KEYesté aprovisionada (ver el setup único arriba)
El dispositivo muestra last_poll_status: auth_failed¶
- Prueba la conexión manualmente con el endpoint
/test - Verifica que el usuario de API del dispositivo tenga los permisos correctos (FTD: API user role; Nexus: NX-API habilitado en el profile del usuario; Fortinet: API admin profile)
- Revisa los logs del lado del dispositivo (p.ej. FTD
/var/log/audit.log)
El dispositivo muestra last_poll_status: tls_error¶
- Define
tlsVerifyMode: 'self-signed'para dispositivos con certificados self-signed (elstrictpor defecto los rechaza) - Para self-signed, el SHA-256 del cert queda fijado (pinned) en la primera conexión; si el cert rota, retornan errores de TLS — el operador debe limpiar
pinned_cert_sha256para aceptar el nuevo cert
Los snapshots dejan de llegar pero no hay error logueado¶
- Revisa contención del advisory lock de Postgres: si dos pods del dashboard están compitiendo, solo uno hace polling. Confirma replicas=1 O acepta que los snapshots lleguen al doble del intervalo configurado (cada pod toma el lock la mitad del tiempo).
- Revisa el espacio en disco del volumen de Postgres —
dut_api_snapshotscrece en modo append-only.
Modelo de seguridad¶
- Credenciales en reposo: AES-256-GCM en BYTEA de Postgres. La clave es la env var
DUT_CRED_ENC_KEY(32 bytes), almacenada en K8s Secret. - Credenciales en tránsito (dashboard → dispositivo): HTTPS.
tlsVerifyModees por dispositivo (strict / self-signed / insecure). - Credenciales en memoria: solo durante un ciclo de polling; limpiadas después de
disconnect(). - Los endpoints de la API requieren autenticación de admin (la misma que el resto de la UI de admin).
- Los endpoints Test Connection / Manual Snapshot son acciones de write — la auth de admin se re-verifica en cada llamada.
Performance¶
La cadencia por defecto (1 dispositivo cada 5 min) genera 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 un lab típico de 4 dispositivos (1 NGFW + 1 switch + 2 UCS), espera ~350 snapshots/hora, ~8400/día, ~250k/mes. A ~250 bytes por snapshot (con payload), esto da ~63 MB/mes — trivial para Postgres.
Para una cadencia agresiva de 30 s en el mismo lab: ~3.5k snapshots/hora, ~84k/día, ~2.5M/mes, ~625 MB/mes — todavía está bien, pero vale una política de retention para dut_api_snapshots.
Retention¶
Hoy no hay retention automático. Operadores que quieran podar snapshots antiguos:
DELETE FROM dut_api_snapshots
WHERE collected_at < now() - interval '30 days';
Ejecuta esto como un CronJob de Postgres si tu lab genera alto volumen.
Futuro: un campo configurable dut_api_snapshot_retention_days por dispositivo + prune automático (alcance del PR-D).
Relacionados¶
DUT_API_INTEGRATION.es.md— arquitecturaAPI_FEATURE_CATALOG.es.md— qué funcionalidades desbloquea la API (45 catalogadas)SYSLOG_OPERATIONS.es.md— guía de operaciones del segundo pilarUSAGE_POLICY.es.md— aplican restricciones de licencia