Resumen — qué hace
Diagnóstico de seguridad ejecuta un análisis de rastros de rootkit más un análisis de fortificación del sistema en los servidores registrados, puntúa cada resultado con una puntuación de 0 a 100 y una calificación de A a F, y muestra en una sola pantalla la postura de seguridad de toda la flota. Cada comprobación lleva una explicación de qué/por qué/cómo corregirlo, y todo hallazgo que sea un fallo cuya severidad es crítica (fail + crit) se promueve automáticamente a un caso de Resolución de problemas (deduplicado y resuelto automáticamente en cuanto la comprobación vuelve a pasar).
Piezas principales:
- Sonda
infr_seccheck_probe.cjs— un script de Node que se ejecuta localmente y en solo lectura en el host de destino (sin credenciales). Realiza 36 comprobaciones y emite JSON. - Catálogo canónico
App\Support\SecurityCheckbook— es el propietario de las 9 categorías, las claves de comprobación, los pesos y las severidades (independiente del idioma, seguro para caché). El texto explicativo vive enresources/lang/{ko,en}/seccheck.php. - Motor
App\Services\Security\DiagnosticsService— ingesta → puntuación → promoción/resolución de casos → agregación del panel. - Almacenamiento
infr_sec_scan(migración000766) — una fila = un análisis de un host;findingsse guarda en línea como JSON.
Ejecutar la sonda como root habilita comprobaciones privilegiadas como
/etc/shadow,sudoersysshd -T. Cuando no es root, las comprobaciones privilegiadas se degradan askipy quedan excluidas del denominador de la puntuación (sin penalización).
Cuándo usarlo
- Validación de la fortificación de referencia antes de admitir un servidor nuevo en producción.
- Triaje rápido de rastros de rootkits o puertas traseras en un host sospechoso (procesos ocultos, inyección en
ld.so.preload, NIC en modo promiscuo, etc.). - Un ritmo recurrente de cumplimiento: una sonda diaria por cron que envía las puntuaciones mediante el reportero de la API para acumular una tendencia de forma automática.
- Preparación para auditorías: ver de un vistazo qué hosts ejecutan un sistema operativo sin soporte (EOL), tienen actualizaciones de seguridad sin aplicar o permiten el inicio de sesión SSH como root.
Disposición
| Área | Contenido |
|---|---|
| Franja de KPI | Puntuación media de la flota · número de hosts analizados · casos críticos abiertos · total de comprobaciones fallidas · hora del último análisis |
| Distribución de calificaciones | Histograma del número de hosts A/B/C/D/F |
| Puntuaciones por categoría | Consolidado de las 9 categorías (media de la flota, orden del catálogo, color de tono) |
| Tendencia de la puntuación | Los últimos 60 análisis en orden cronológico |
| Hosts por calificación | Cuadrícula de tarjetas de host — ordenadas primero por la puntuación más baja (la más arriesgada). Cada tarjeta muestra la calificación, los recuentos pass/warn/fail/crit, el origen de entrada y la hora del análisis. Clic → modal de informe |
| Modal de informe | Puntuación y calificación globales + resumen (SO/kernel) + detalle por comprobación (qué/por qué/corrección/evidencia) + enlace al caso promovido |
Barra de herramientas superior: [Ejecutar análisis] (ejecución remota en varios servidores) · [Pegar resultado] · Actualizar · Interruptor de actualización automática · Pantalla completa.
Cómo usarlo
Cuatro vías de entrada
infr_sec_scan.source clasifica cómo se recibió un análisis:
| source | Punto de entrada | Método |
|---|---|---|
emulate |
Modal [Ejecutar análisis] | Seleccione instancias → despliegue y ejecute la sonda de forma remota. Un pegado o una aplicación de emulación remota que lleve un instance_id también se registra como emulate |
paste |
Modal [Pegar resultado] | Pegue el JSON de la sonda generado directamente en el destino, sin vinculación a una instancia |
api |
POST /api/v1/security/scan |
El cron de cada host ejecuta la sonda → envía mediante una clave de API (la vía del reportero) |
manual |
Alternativa | Valor por defecto cuando el valor de source no es válido |
1) Ejecución remota en varios servidores — sondeo asíncrono
Una ejecución completa síncrona superaría el tiempo de ejecución de PHP al abarcar muchos hosts, así que el flujo es registrar → worker que se lanza y se olvida → sondear.
- [Ejecutar análisis] → marque las instancias de destino (cada una indica si es alcanzable o inalcanzable).
POST /security/diagnostics/run→ crea un registro de progreso y devuelverun_idde inmediato (Cache, TTL de 30 min).- El front end llama a
POST /security/diagnostics/run-worker/{runId}sin esperar → el worker recorre todos los hosts de forma secuencial bajoset_time_limit(0)eignore_user_abort(que falle un host no detiene al resto). - El front end sondea
GET /security/diagnostics/run-status/{runId}para actualizar el progreso (done/total/ok).
Una instancia marcada como inalcanzable no coincide con ningún nodo del recolector
run_report, por lo que no puede ejecutarse de forma remota. Ejecute la sonda directamente en el host y envíela mediante [Pegar resultado]. Consulte Emulación remota para el cableado de la ejecución remota.
2) Pegado / ingesta de JSON
Ejecute la sonda en el host de destino y pegue su JSON.
# En el host de destino (recomendado: root)
node infr_seccheck_probe.cjs --min
--min emite JSON en una sola línea. Forma de la salida:
{
"host": "web-01",
"generated_at": "2026-08-13T03:30:00Z",
"os": "Rocky Linux 9.3",
"kernel": "5.14.0-362.el9",
"root": true,
"checks": [
{ "key": "ssh_root_login", "category": "ssh", "status": "fail", "severity": "crit", "detail": "PermitRootLogin yes", "evidence": "/etc/ssh/sshd_config:32" },
{ "key": "svc_firewall", "category": "services", "status": "pass", "severity": "crit", "detail": "firewalld active" }
],
"summary": {}
}
Lo recibe POST /security/diagnostics/ingest. Vincular una instancia registra source=emulate; dejarla sin vincular registra source=paste.
3) Reportero de la API (automatización)
El cron de cada host ejecuta la sonda y envía el resultado a InfraGuard. Emita la clave de API en Administración ▸ Claves API; la clave debe llevar el ámbito scan.write.
30 3 * * * node /opt/infr/infr_seccheck_probe.cjs --min | curl -sS -X POST https://infraguard.example.com/api/v1/security/scan -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" --data @-
Respuesta (201):
{ "ok": true, "scan_id": 812, "host": "web-01", "score": 74, "grade": "C" }
Puntuación — SecurityCheckbook
La puntuación es una proporción ponderada del peso y el estado de cada comprobación.
Factor de estado (STATUS_FACTOR) — solo cuentan estos; skip/info quedan excluidos del denominador:
| Estado | Factor |
|---|---|
pass |
1.0 |
warn |
0.6 |
fail |
0.0 |
skip · info |
excluidos |
Multiplicador de severidad (SEV_MUL) — cuanto más grave sea la implicación de un fallo, más mueve la puntuación:
| fail_severity | Multiplicador |
|---|---|
crit |
1.6 |
warn |
1.0 |
info |
0.5 |
Peso por comprobación w = check_weight(1–3) × severity_multiplier; puntuación global = round(100 × Σ(w × status_factor) / Σw). Las puntuaciones por categoría usan la misma fórmula dentro de cada categoría.
Calificación y tono:
| Puntuación | Calificación | Tono |
|---|---|---|
| ≥ 90 | A | good |
| ≥ 80 | B | good |
| ≥ 70 | C | warn |
| ≥ 60 | D | warn |
| < 60 | F | crit |
(Las calificaciones son A/B/C/D/F; el tono es good ≥ 80 · warn ≥ 60 · crit < 60.)
9 categorías · 36 comprobaciones
Cada categoría tiene un peso de presentación y un icono; la puntuación real usa el peso por comprobación × multiplicador de severidad descrito arriba.
| Categoría | Etiqueta | Comprobaciones | Comprobaciones destacadas (implicación en caso de fallo) |
|---|---|---|---|
rootkit |
Rootkits / malware | 6 | rk_known_files·rk_ld_preload·rk_hidden_proc (crit) |
patch |
Parches / actualizaciones | 3 | pt_security_updates·pt_eol_os (crit) |
ssh |
Configuración de SSH | 5 | ssh_root_login·ssh_empty_pass (crit) |
accounts |
Cuentas / privilegios | 5 | acc_uid0·acc_empty_pass (crit) |
network |
Exposición de red | 3 | net_legacy_services·net_db_exposed (crit) |
filesystem |
Permisos del sistema de archivos | 5 | fs_shadow_perm (crit)·fs_world_writable·fs_suid (warn) |
hardening |
Fortificación del sistema | 5 | hd_selinux·hd_aslr·hd_syncookies (warn) |
services |
Servicios / cortafuegos | 2 | svc_firewall (crit)·svc_unnecessary (info) |
logging |
Auditoría / registro | 2 | log_auditd·log_syslog (warn) |
Las claves desconocidas se absorben en la categoría other. El texto «qué / por qué / corrección» de cada comprobación se renderiza en el modal de informe a partir de seccheck.check.*.
Hallazgos críticos → promoción a Resolución de problemas
En cada ingesta, reconcileCases() reconcilia los casos del host.
- Solo se promueven los hallazgos con
status = failYseverity = crit. Clave de deduplicación: unfingerprintinterno. - Si existe un caso abierto, incrementa
occurrence_count++ylast_occurred_at; en caso contrario crea un caso nuevo:infr_incident(area=security,source=detection,entity_type=seccheck,entity_key=host,severity=crit,status=open) más uninfr_incident_event(kind=created). - Resolución automática: un caso previamente abierto que en este análisis ya no es fail+crit (corregido o no detectado) pasa a
status=resolvedconresolved_aty una nota de resolución automática.
Las comprobaciones promovidas aparecen en el modal de informe con un distintivo «promovido a resolución» y un enlace [Ver caso]. Los casos se trabajan en el tablero de Seguridad ▸ Resolución de problemas.
Eliminar un análisis (
POST /security/diagnostics/delete) no elimina los casos que promovió: son independientes.
Fuentes de datos
| Fuente | Papel |
|---|---|
infr_sec_scan (000766) |
Resultados de los análisis (uno por host). JSON de score·grade·pass/warn/fail/crit/total_count·category_scores·summary·findings. Índices (host,scanned_at)·(instance_id,scanned_at)·grade·score |
infr_instance |
Instancias de destino (resuelve host → instance_id por alias/hostname) |
infr_incident / infr_incident_event |
Casos críticos promovidos y su cronología |
infr_api_key |
Autenticación del reportero de la API (ámbito scan.write) |
| Cache | Agregado del panel en caché durante 20 s (infr:sec:diag:build) · registro de progreso de la ejecución asíncrona (TTL de 1800 s) |
infr_seccheck_probe.cjs |
La sonda de origen (local, solo lectura) |
- Disciplina de agregación: dentro de los 500 análisis más recientes, conserve solo el más reciente por host (deduplicación) → listado de la flota, media, consolidado por categoría, tendencia y número de críticos abiertos.
- La categoría y la severidad de la sonda son solo pistas; el servidor las renormaliza como canónicas mediante
SecurityCheckbook.
Acceso
- Lectura: requiere acceso al área de rol de Seguridad (en el escalón actual, staff = 5 / admin = 10; demo = 1 no ve el área de Seguridad). Permite ver el panel y los informes.
- Escritura (ejecutar / ingerir / eliminar): requiere
infr_user_level >= 2en la sesión (canWrite()). Por debajo de ese nivel,run·ingest·deletedevuelven 403 (seccheck.toast.forbidden). - Reportero de la API: el middleware
infr.apikeymás una clave con el ámbitoscan.write; en caso contrario, 403forbidden_scope. - Auditoría: toda escritura se registra en
infr_audit_log—sec.diag_run(número de destinos),sec.diag_ingest(scan_id·score·source),sec.diag_delete(id del análisis). Revíselo en el Registro de auditoría.
Para el modelo de niveles en general (middleware infr.level:N), consulte Niveles RBAC.
Solución de problemas y preguntas frecuentes
Q. Una instancia aparece como «inalcanzable» en [Ejecutar análisis].
No coincide con ningún nodo del recolector run_report, así que no hay cableado de ejecución remota. Ejecute la sonda directamente en el host y envíela mediante [Pegar resultado].
Q. Error «No checks found».
El JSON pegado no tiene un array checks (ni items), o sus entradas carecen de key. Pegue literalmente la salida de infr_seccheck_probe.cjs --min.
Q. La puntuación es baja aunque el número de fallos es pequeño.
Es el multiplicador de severidad. Un fallo crit pesa 1,6 veces uno warn. Use las puntuaciones por categoría y el modal de informe para ver qué hallazgo está tirando de la puntuación hacia abajo.
Q. Todas las comprobaciones privilegiadas son skip.
La sonda se ejecutó con una cuenta que no es root. Comprobaciones como /etc/shadow, sudoers o sshd -T necesitan root. skip queda excluido del denominador (sin penalización), pero ejecútela como root (o a través de la pasarela) para obtener un diagnóstico completo.
Q. Una ejecución tarda un rato: ¿puedo cerrar el navegador?
Sí. El worker se lanza y se olvida, y continúa bajo ignore_user_abort aunque se aborte la petición. Vuelva a abrir y sondee run-status, o simplemente lea las calificaciones cuando aparezcan en el panel. El registro de progreso se conserva 30 minutos.
Q. Un caso desapareció por sí solo. Si un análisis posterior deja de notificar ese hallazgo como fail+crit (corregido o no detectado), el motor resuelve el caso automáticamente. Es el comportamiento esperado.
Relacionado
- Seguridad ▸ Resolución de problemas — flujo de trabajo de los casos críticos promovidos
- Emulación remota — despliegue y cableado de ejecución de la sonda en varios servidores
- Claves API — claves de la vía del reportero (ámbito
scan.write) - Activos ▸ Instancias — el registro de instancias de sistema operativo de destino
- Seguridad ▸ Resumen — la narrativa completa del embudo de defensa
- Registro de auditoría — historial de ejecución / ingesta / eliminación
