Lafine Systems Design · Documentación técnica

Informe técnico de arquitectura y seguridad de RoamSwitch Server Edition

Suite de seguridad de red autónoma & defensa de la integridad, sin cabeza, para infraestructuras Linux en la nube y locales. No se usan expresiones publicitarias, y lo que se describe puede comprobarse en el comportamiento real del paquete de distribución oficial y en las Autoverificaciones del §6.

Versión v1.11.1 Alcance RoamSwitch Server Edition 1.11.1 (detección de tunelización DNS, honeytokens, captura de evidencias forenses, correspondencia con NIST CSF / CIS Controls, actualización del mapa CVE del kernel firmado) Requisitos systemd + nftables Publicado 2026-10-08

§1Descripción general y principios básicos

RoamSwitch Server Edition es una suite de protección de integridad y seguridad de red autónoma residente y sin cabeza diseñada específicamente para infraestructuras de servidores Linux en proveedores de nube (AWS, GCP, Azure, VPS bare-metal) y centros de datos locales.

A diferencia de las ediciones de estaciones de trabajo cliente centradas en la movilidad y la adaptación del perímetro Wi-Fi, la edición Server se basa en cinco principios inflexibles: Caída predeterminada de entrada, Monitoreo de integridad de archivos de ruta crítica (FIM), LPE de kernel preventivo y refuerzo de escalada de privilegios, Telemetría de amenazas en tiempo de ejecución eBPF, y Aislamiento autónomo del host por espacio de aire con alertas de emergencia previas al despido.

El principio Zero Telemetry —ninguna biblioteca de comunicación externa en el propio demonio— se aplica a nivel de código (por defecto lo único que sale del host es la recepción de datos firmados (los datos de CVE del kernel, las reglas web y, en servidores Ubuntu, los datos de CVE de paquetes de Ubuntu que usa la comprobación de CVE de los paquetes del sistema; manifiesto comprobado cada día, los datos de CVE en sí aproximadamente cada 30 días), que además se puede desactivar, y toda otra excepción es algo que el operador activa de forma explícita; véanse los principios de diseño del §1), y los secretos (tokens de bot y configuración) se protegen con permisos 0600.

1.1 Comparación: Edición Cliente versus Edición Servidor

Dimensión Edición de cliente (escritorio/móvil) Edición de servidor (sin cabeza/infraestructura)
Implementación Computadoras portátiles, estaciones de trabajo móviles para desarrolladores VM en la nube, nodos bare-metal, hosts de contenedores
Comportamiento de la red Cambio de perfil dinámico basado en Wi-Fi SSID/Gateway MAC Descarga predeterminada de entrada estática. Sólo se exponen los puertos permitidos explícitamente
Interfaz de usuario GUI de escritorio GTK3 + indicador de bandeja CUI puro sin cabeza. Comandos CLI + asistente de configuración interactivo + interfaz de terminal (roamswitch-server-tui)
Notificaciones de incidentes Notificaciones de escritorio (libnotify) Envío directo a Telegram Bot, API de mensajería LINE y Webhooks (Slack/Discord)
Defensas Primarias Suplantación de ARP de Wi-Fi pública, BadUSB, cifrado de documentos ransomware Caída de reconocimiento externo, detección de manipulación FIM, fugas de contenedores, kernel LPE (CVE-2026-53362)

1.2 Principios arquitectónicos básicos

  • Zero Telemetry: por defecto no se envía al exterior nada sobre el usuario ni el entorno. El propio demonio no incluye ninguna biblioteca de comunicación HTTP/TLS; solo para las notificaciones y reenvíos que el operador ha configurado ejecuta, como subprocesos, el curl del sistema (Telegram / LINE / webhook; los secretos como tokens y URL se pasan en una configuración por la entrada estándar, nunca en la línea de comandos) y openssl (reenvío de syslog por TLS). La única comunicación externa activada por defecto, aparte de las actualizaciones del gestor de paquetes, es la siguiente. (1) Mientras cve_kernel_map_updates_enabled (true por defecto) esté activado, roamswitch-updater recibe de forma anónima desde lafine.net datos firmados. Son los datos de CVE del núcleo para la auditoría de aislamiento de contenedores, las reglas de detección de la capa de aplicaciones web (reglas web) y, en servidores Ubuntu, los datos de CVE de paquetes de Ubuntu que usa la comprobación de CVE de los paquetes del sistema. El manifiesto firmado y su firma se comprueban cada día (dos GET); los datos de CVE del núcleo y los de paquetes de Ubuntu solo se obtienen cuando han pasado unos 30 días desde la última actualización (un servidor que aún no tiene los datos de Ubuntu los obtiene en la siguiente comprobación diaria), y las reglas web solo cuando el manifiesto tiene una versión más reciente. Es un simple HTTPS GET sin cadena de consulta ni cookies, cuya única cabecera que nombra el software es un User-Agent: RoamSwitch-Updater fijo; solo se aceptan datos cuya firma Ed25519 se verifica, y no se envía nada sobre este host. El operador puede desactivarla con roamswitch server config set cve_kernel_map_updates_enabled false, el asistente de configuración o la TUI del servidor; un false explícito en un server.conf existente se respeta tal cual, y la auditoría sigue funcionando con la línea base incorporada cuando está desactivada; en cambio, la comprobación de CVE de los paquetes del sistema en un servidor Ubuntu indica entonces «sin datos de CVE locales» y no devuelve ningún resultado. Toda otra excepción la activa el operador de forma explícita. (2) El reenvío de eventos (forward_enabled, false por defecto) solo envía a los destinos syslog / archivo JSON Lines / webhook firmado que el operador configuró. (3) Solo si npm_audit_signatures_enabled (false por defecto) está activado y el operador ejecuta roamswitch audit-npm-signatures <DIR> --confirm, es npm quien contacta con registry.npmjs.org. (4) Solo si sensor_pairing_enabled (false por defecto) está activado se conecta, y únicamente a la dirección del Sensor que introduce el operador (el Sensor es un producto aparte, no cubierto por Zero Telemetry). (5) Fleet (nivel Business) es solo código del lado del dispositivo: un dispositivo no inscrito no envía nada ni resuelve ningún nombre; el servicio de gestión no está desplegado ni publicado y, por diseño, no existe ningún canal de comandos remotos. (6) La integración con LLM de la herramienta independiente incluida accessscope (diagnóstico de seguridad de registros de acceso) está desactivada por defecto. Solo si se activa (en la línea de comandos, cuando se indica --llm) pueden enviarse un resumen del diagnóstico, líneas de registro como evidencia e información del entorno (en la función de búsqueda de registros) a un servicio externo mediante el comando de LLM que haya elegido el operador. El demonio (roamswitch-server) no participa en esta comunicación, AccessScope puede ejecutar su análisis con LLM también como root (el LLM se ejecuta entonces con los derechos de root; se recomienda un usuario sin privilegios), y queda fuera de Zero Telemetry. La herramienta independiente incluida personalsoc (revisión de registros y defensas) funciona igual: solo cuando se indica --llm, un resumen del diagnóstico puede enviarse a un servicio externo a través del comando LLM elegido por el operador (fuera de Zero Telemetry). El comando pathscope no llama a ningún LLM. (7) Solo si se activa clamav_enabled (false por defecto), el demonio ejecuta freshclam para recibir las firmas de ClamAV (la primera vez, cuando aún no existe la base de datos de firmas, y después cada clamav_freshclam_interval_secs (7200 segundos por defecto) mientras se especifique clamav_scan_dirs).
  • Descarte entrante por defecto (Inbound Default Drop) descarta con nftables las conexiones entrantes nuevas (TCP) hacia cualquier puerto que no esté permitido, sin devolver siquiera un RST. Un escaneo de puertos solo ve los puertos permitidos (se admiten el loopback, las conexiones establecidas, ICMP e ICMPv6).
  • Cierre fallido (fail-closed) & autorreparación funciona así. Las reglas de denegación por defecto y de aislamiento permanecen en el kernel, por lo que se mantienen aunque el demonio se detenga o se reinicie (Restart=always). La protección de los puertos publicados de Docker se vuelve a comprobar cada 60 segundos, y la lista de bloqueo de egreso y el bloqueo de escaneo de puertos cada 300 segundos, reinstalando las reglas que hayan desaparecido. Las reglas de aislamiento en modo degradado (DEGRADED) se verifican en una evaluación cada 15 segundos y se reinstalan si faltan. Si alguien elimina desde fuera la propia regla de denegación por defecto, no se vuelve a aplicar automáticamente, así que compruébelo con el diagnóstico «Descarte entrante por defecto» (roamswitch server status).
  • Protección mediante permisos 0600: al archivo de configuración /etc/roamswitch/server.conf se le fuerzan automáticamente los permisos 0600 al crearlo y actualizarlo. Los secretos como tokens y URL de webhook también se pasan a curl en una configuración por la entrada estándar y nunca se colocan en la línea de comandos (que otros usuarios pueden ver mediante /proc/<pid>/cmdline).

§2Threat Landscape & Adversary Model

Modern internet-facing servers confront relentless automated scanning, container breakout vectors, and zero-day kernel privilege escalations.

Key Threat Vectors Addressed
  1. Reconocimiento automatizado y exposición de bases de datos: escáneres de Internet que localizan puertos de bases de datos internos expuestos involuntariamente (MySQL 3306, Redis 6379, Postgres 5432).
  2. Manipulación de ruta crítica e inserción de puerta trasera: Intrusos que establecen persistencia modificando los archivos binarios de autenticación centrales (/bin/login, /usr/bin/sudo, /etc/pam.d/*, /etc/shadow).
  3. Escalada de privilegios del kernel (CVE-2026-53362 Frag Gap): atacantes sin privilegios que utilizan espacios de nombres de usuario como armas (unshare -U -r) para desencadenar la corrupción de la memoria del kernel y escalar a la raíz.
  4. Container Socket Breakout: contenedores mal configurados con indicadores /var/run/docker.sock o --privileged montados que ejecutan acciones raíz en el sistema operativo host.
  5. Espionaje de memoria de procesos: los atacantes adjuntan ptrace o leen /proc/$pid/mem entre procesos para extraer claves privadas o credenciales.

§3System Architecture

RoamSwitch Server Edition operates as native Rust binaries designed for minimal memory and zero CPU overhead.

┌─────────────────────────────────────────────────────────────┐
│  Espacio de usuario & administrador                         │
│                                                             │
│   roamswitch (CLI)            roamswitch-mcp (AI Agent)     │
│   · server status (34 puntos) · herramientas de auditoría   │
│   · server config / setup     · JSON-RPC por stdio          │
│   · fim verify / update                                     │
│        │                               │                    │
└────────┼───────────────────────────────┼────────────────────┘
         │ (comandos CLI & config)       │ (stdio)
┌────────▼───────────────────────────────▼────────────────────┐
│  Base del sistema (systemd + privilegio root)               │
│                                                             │
│   roamswitch-server-daemon                                  │
│   ├── CriticalPathFim (barrido SHA-256 en segundo plano)    │
│   ├── módulo de integración eBPF / Falco                    │
│   ├── Notifier (alertas Telegram / LINE / Webhook)          │
│   └── Air-Gap Controller (reglas de emergencia nftables)    │
│                                                             │
│   Configuración: /etc/roamswitch/server.conf (permiso 0600) │
│   BD de integridad: /var/lib/roamswitch/fim.db              │
└─────────────────────────────────────────────────────────────┘

§4Core Defense Mechanisms

4.1 Boundary Security (Inbound Default Drop)

Aplica policy drop a la cadena input de la tabla inet roamswitch_server de nftables. Solo permite las conexiones TCP nuevas entrantes hacia los puertos indicados por el administrador (ssh_ports, allowed_ports); la comunicación hacia los puertos no permitidos expira por tiempo de espera (se descarta). Se permiten el loopback, las conexiones establecidas, ICMP e ICMPv6.

Aunque se active un Air-Gap (aislamiento de emergencia), las rutas de administración —los puertos SSH de mantenimiento (ssh_ports) y las IP de origen de mantenimiento registradas (whitelist_ips), tanto conexiones nuevas como establecidas— se mantienen en el conjunto de reglas, lo que evita quedarse sin posibilidad de recuperación remota (preserve_ssh_on_isolation, true por defecto). Cualquier otra conexión establecida (por ejemplo, un C2 o una reverse shell ya abiertos por el atacante) se corta con las cadenas default-drop. Si no se indica ninguna fuente de administración (whitelist_ips y las direcciones permitidas de guard.yaml están vacías), los puertos SSH de mantenimiento siguen aceptando conexiones nuevas desde cualquier origen durante el aislamiento, y el servidor emite una advertencia al arrancar. Con preserve_ssh_on_isolation=false, el aislamiento corta también el SSH. La edición de servidor hasta la 1.10.4 tenía un defecto. Las reglas de aislamiento descartaban las respuestas SSH, por lo que el SSH del operador no funcionaba durante el aislamiento. Se corrigió en la 1.10.5.

4.2 Critical Path File Integrity Monitoring (FIM)

Supervisa la integridad por hash SHA-256 de los archivos más críticos para el arranque, la autenticación, la red y la administración del sistema. El alcance incluye 23 rutas fijas (/etc/shadow, /etc/passwd, /etc/sudoers, /etc/ssh/sshd_config, /bin/login, /usr/bin/sudo, /usr/sbin/sshd, /usr/lib/systemd/systemd, etc.); los archivos situados directamente dentro de 12 directorios (/etc/pam.d, /etc/sudoers.d, /etc/ssh/sshd_config.d, /etc/cron.d y /etc/cron.{hourly,daily,weekly,monthly}, /etc/systemd/system, /etc/ld.so.conf.d, /etc/profile.d, /root/.ssh), donde también se detecta la aparición de archivos nuevos (en /root/.ssh se excluyen known_hosts y known_hosts.old, porque se reescriben cada vez que se establece una conexión SSH); los binarios propios de RoamSwitch; el authorized_keys de cada usuario que pueda iniciar sesión; y /etc/ld.so.preload, que no debe existir (se detecta su aparición). Tras una actualización legítima de paquetes, la línea base sana se actualiza automáticamente. En los sistemas basados en Debian, el hook /etc/apt/apt.conf.d/99roamswitch-fim (apt upgrade, etc.) y, en los basados en RHEL y en openSUSE, una unidad systemd.path (roamswitch-fim-update.path) que vigila /var/lib/rpm, ejecutan roamswitch fim update.

Determinación de la vía de intrusión (quién hizo el cambio y mediante qué inicio de sesión SSH)

Las notificaciones sobre un archivo modificado (FIM de rutas críticas y FIM de archivos de bloqueo) incluyen ahora el proceso que hizo el cambio (cuenta, ejecutable, comando) y el inicio de sesión SSH al que pertenece (IP y puerto de origen, hora de inicio de sesión, método de autenticación y huella de la clave pública). Está activado por defecto (fim_intrusion_context_enabled). En el momento en que se lee un evento, el demonio toma una instantánea de /proc/<pid> del proceso que escribe (ejecutable, usuario, loginuid, sessionid, cgroup, cadena de padres), porque un proceso como tee o sed desaparece un milisegundo después. Un proceso que ya había terminado se completa con el registrador de ejecuciones (si está activado). La asociación con un inicio de sesión usa primero la tabla de sesiones de la guardia SSH y, si no, las líneas Accepted de logind y journald. Funciona aunque la guardia SSH esté desactivada. No se envía nada al exterior.

Solo se confía en los valores que informa el kernel: el ejecutable sale de /proc/<pid>/exe (no de argv[0]), el inicio de sesión de loginuid (se fija una sola vez al iniciar sesión y se hereda a través de sudo y su) y del session-N.scope del cgroup, y el resto de la cadena de padres. Los valores que un proceso puede cambiar por sí mismo, como comm, la línea de comandos y el entorno, se muestran pero nunca se usan para decidir; un proceso que solo se llama sshd no se toma por un inicio de sesión SSH. Una notificación distingue tres respuestas: encontrado (origen, hora, método, clave); «No se encontró ningún inicio de sesión SSH» (la comprobación mostró un servicio de systemd, un contenedor, una sesión de inicio local o un proceso sin sesión de inicio); y «No se pudo determinar la vía de acceso» (el proceso ya no existía y no está en el registrador de ejecuciones, logind no respondió, el cambio lo halló el escaneo periódico y el proceso se desconoce, etc.). Lo que no se pudo comprobar nunca se presenta como «no es un inicio de sesión». La notificación sale aunque falle la consulta.

/etc/ssh/sshd_config: modified
  Proceso: root (inició sesión como admin) / /usr/bin/tee / comando: tee /etc/ssh/sshd_config (pid 4121)
  Inicio de sesión SSH: la sesión que admin abrió desde 203.0.113.25:51544 a las 2026-10-17 03:12:08 (autenticación: publickey, clave SHA256:AbCdEf0123456789)

/etc/sudoers: modified
  No se pudo determinar la vía de acceso (el cambio lo encontró el análisis periódico, así que no se sabe qué proceso lo hizo)

Ahora también se detectan las ediciones que colocan un archivo nuevo bajo el mismo nombre: install, sed -i y cp seguido de mv en el mismo directorio. La marca por archivo se asocia al inodo, por lo que antes este tipo de edición no generaba ningún evento. Ahora el demonio marca también los directorios que contienen archivos vigilados y, cuando se cierra un archivo con otro nombre en el mismo directorio, vuelve a consultar el nombre unos milisegundos después. Límite: un archivo escrito en otro directorio y movido con mv solo lo encuentra el escaneo periódico (fim_check_interval_secs). Entonces no se conoce el proceso que escribió, y la notificación lo indica.

Las mediciones se hicieron en una VM con Ubuntu 24.04 con el origen del operador en un espacio de nombres de red (netns) aparte. Con sudo tee, con un proceso desligado mediante nohup (su IP de origen se siguió identificando tras cerrar el operador la sesión, con la nota «Esa sesión ya ha terminado»), con una cadena sudo -i y con un cat de vida muy corta, la notificación mostró la IP, el usuario y el método de autenticación. Un inicio de sesión local dio «No se encontró ningún inicio de sesión SSH». Un proceso que disfrazó su nombre con exec -a "sshd: …" no se tomó por SSH.

Además de la notificación, el hallazgo se conserva en la cronología de incidentes (tipo fim) y en /var/lib/roamswitch/fim_intrusions.json (solo root, los últimos 100). La herramienta MCP verify_fim lo devuelve en un campo añadido, recentIntrusionContext (solo si se ejecuta como root). El veredicto sobre si un archivo fue manipulado no cambia. Límites: con el registrador de ejecuciones desactivado y un proceso escritor que vive solo un instante, se desconoce el ejecutable y se muestran el ejecutable y la línea de comandos del proceso padre. Las notificaciones de la vigilancia de persistencia (persistence_watch) no llevan información de proceso, por lo que no reciben vía de intrusión. La vía muestra el proceso que hizo el cambio y su inicio de sesión, y nada más: la puerta de entrada en sí (la vulnerabilidad explotada o cómo se robó una clave) no se muestra. Para desactivarlo, pon fim_intrusion_context_enabled=false.

4.3 Kernel & Container Hardening

Frag Gap (CVE-2026-53362) Mitigation: Enforces user.max_user_namespaces = 0 via sysctl, neutralizing LPE vectors. Yama LSM Protection enforces kernel.yama.ptrace_scope = 2, preventing unauthorized memory inspection. Container workloads are audited for docker.sock exposure. En la edición de servidor, esta prohibición de los espacios de nombres sin privilegios está activada por defecto (harden_userns_enabled). Impide Podman rootless y similares; póngala en false si depende de ellos.

Los puertos publicados de Docker (-p) eluden, como es sabido, el cortafuegos estándar del host. Por ello, con protect_docker_ports=true (por defecto) se descarta el tráfico procedente de la interfaz externa cuyo puerto de destino previo al DNAT no esté entre los puertos permitidos (ssh_ports, allowed_ports) (el tráfico de retorno, el loopback y el tráfico entre contenedores no se ven afectados). Con el backend iptables / iptables-nft, las reglas van en la cadena DOCKER-USER (IPv4, y también mediante ip6tables si el IPv6 de Docker está activado); con el backend nftables nativo de Docker (Docker 29 y posteriores, configuración sin DOCKER-USER), en una tabla propia inet roamswitch_docker_guard (hook forward, prioridad -10), y ese único conjunto de reglas cubre IPv4 e IPv6. El demonio vuelve a comprobar periódicamente y reinstala las reglas que desaparecen. Además, un guardián siempre activo detecta en tiempo real, mediante docker events, el inicio de un contenedor --privileged o el montaje de /var/run/docker.sock y lo notifica de inmediato como riesgo de escape de contenedor (sin configuración; solo notificación, sin bloqueo automático). En hosts que manejan archivos destinados a terceros, como relés de correo o carpetas compartidas, el File Scan Guard, activable opcionalmente (YARA integrado + ClamAV opcional), ofrece análisis periódico y cuarentena automática.

Container Exec Guard (container_exec_guard_enabled, true por defecto) — coloca fanotify FAN_OPEN_EXEC (clase de notificación) en la raíz overlay2 de cada contenedor Docker en ejecución e inspecciona el contenido de cada archivo ejecutado con las reglas YARA integradas, notificando si hay coincidencia. Como se suscribe a docker events y después enumera los contenedores en ejecución con docker ps, los contenedores que ya estaban en ejecución cuando arrancó el demonio también quedan cubiertos. El contenido del archivo ejecutado se lee del descriptor de archivo que entrega fanotify, por lo que puede inspeccionarse aunque el archivo se borre a sí mismo justo después de ejecutarse. Solo notifica y no detiene ni deniega la ejecución (la edición Server no tiene ningún fanotify de clase de permisos). Docker 29 eliminó el campo obsoleto Status de los mensajes de docker events, por lo que hasta la 1.11.0 la suscripción fallaba de inmediato y Container Exec Guard se reconectaba cada 5 segundos sin llegar a obtener una suscripción. Desde la 1.11.1 usa Action, que tiene el mismo valor también en versiones anteriores de Docker. Cuando la suscripción sigue fallando, el intervalo de reconexión se duplica de 5 segundos hasta un máximo de 5 minutos, y la advertencia por la misma causa se registra solo la primera vez.

4.4 eBPF Runtime Threat Telemetry & Anti-Bloat Tuning

Utiliza sondas eBPF modernas (CO-RE/BTF) para interceptar intentos de escalada de privilegios y ruptura de contenedores en milisegundos. Para eliminar los puntos débiles estándar de Falco ( hinchazón de registros, agotamiento del disco y sobrecarga de CPU), RoamSwitch estandariza un perfil optimizado (/etc/falco/config.d/99-roamswitch-optimized.yaml):

  • Filtrado de gravedad (priority: warning): suprime la telemetría operativa ruidosa (información/aviso) en un 95 %, lo que revela incidentes de seguridad estrictamente procesables.
  • Precaída a nivel de kernel (drop_failed_exit: true): Descarta las salidas de llamadas al sistema fallidas directamente en la sonda del kernel, lo que reduce los cambios de contexto de la CPU en más del 50 %.
  • Optimización del espacio de memoria (cpus_for_each_buffer: 2): comprime los buffers circulares de 8 MB a 2 MB, limitando estrictamente el uso de memoria a 30–50 MB.
  • IPC directo por socket de dominio UNIX: coordinación inmediata mediante un socket en memoria (/run/roamswitch/events.sock), sin pasar por E/S de disco.
  • Límite automático de registros (/etc/logrotate.d/roamswitch-falco): con maxsize 50M y una rotación comprimida de 7 generaciones (ejecutada a diario), evita que los registros de Falco crezcan sin control.

Precisión y rapidez del aislamiento eBPF (sin dejarlo solo en manos de las reglas de Falco)

La precisión y la rapidez del aislamiento eBPF dependen de las reglas del sensor (Falco o Tetragon). De hecho, las reglas estándar de Falco 0.45.0 (29 en total) no incluyen ninguna que detecte un reverse shell en el host: las reglas de reverse shell son solo para contenedores, y algunas se descartan por la condición priority: warning de RoamSwitch. RoamSwitch no da por buenas las reglas; lo compensa de tres maneras: mide cuánto tarda el aislamiento, comprueba con regularidad que la vía de alertas está viva y detecta por sí mismo los reverse shells.

Medición de la latencia del aislamiento: por cada incidente eBPF el demonio registra el tiempo desde la detección de Falco hasta que lee la alerta (detect_to_receive_ms), desde que la lee hasta que termina la respuesta (receive_to_action_ms, medido cuando vuelve la llamada de aislamiento; en el Air-Gap del host, hasta que se carga el conjunto de reglas) y la suma de ambos (detect_to_action_ms). Los valores aparecen como campos añadidos en ebpf_incidents.json y en la herramienta MCP get_ebpf_incidents. La notificación de un proceso aislado incluye una línea «de la detección a la respuesta: N ms». Medido en una VM con Ubuntu 24.04 (Falco 0.45.0, modern eBPF): de 1 a 2 ms de la detección a la recepción, de 41 a 62 ms de la recepción al aislamiento del proceso más SIGKILL (42 a 64 ms en total) y 109 ms en total para el Air-Gap del host (dentro del objetivo de 150 ms). Las cifras son de esa VM. Tetragon pone la marca de tiempo al recibir, por lo que no tiene detect_to_receive_ms.

Heartbeat (ebpf_heartbeat_interval_secs; 600 segundos por defecto, 0 lo desactiva, en otro caso de 60 a 86400): el paquete incluye una regla de Falco propia de RoamSwitch, 96-roamswitch-heartbeat.yaml. En cada intervalo el demonio abre una ruta desechable que solo root puede usar (un nombre aleatorio bajo /run/roamswitch/heartbeat/) y mide cuánto tarda la alerta de esa regla en volver por la salida de Falco. Un solo viaje de ida y vuelta muestra a la vez que la regla está cargada, que Falco funciona y que la vía de salida está viva. Solo cuando dos comprobaciones seguidas no vuelven (la segunda un minuto después de la primera) avisa: «las reglas no están cargadas o la salida no llega», como máximo una vez cada 24 horas. Una línea de heartbeat se descarta sin llegar al aislamiento, la notificación, la lista de permitidos ni el registro de incidentes. No se envía nada al exterior. Los viajes de ida y vuelta medidos fueron de 2 a 6 ms; al quitar la regla el aviso llegó tras unos 164 segundos, y al restaurarla el estado volvió a la normalidad en unos 75 segundos.

El estado se guarda en /var/lib/roamswitch/ebpf_heartbeat.json y lo leen el campo heartbeat de la herramienta MCP get_ebpf_incidents y el diagnóstico K-04 (agente de ejecución eBPF; el número de puntos de diagnóstico sigue siendo 34). Aunque Falco esté funcionando, K-04 falla si el heartbeat no vuelve. Límites: se comprueba un viaje de ida y vuelta de una sola regla (las llamadas al sistema de la familia open); no se puede saber si otra regla está rota por su cuenta. Lo que se rompe entre dos comprobaciones (10 minutos por defecto) no se ve hasta la siguiente. No se hace para Tetragon. En un host donde Falco no está en marcha o falta el archivo de la regla, no se comprueba nada ni se avisa de nada (de eso se encarga la notificación existente de «guardia detenida»). Falco escribe una línea de aviso en syslog por cada comprobación (144 líneas al día con el valor por defecto).

Detección de reverse shell (propia de RoamSwitch)

Sin apoyarse en las reglas de Falco, RoamSwitch lee por sí mismo /proc cada 2 segundos y busca una shell o un intérprete (sh, bash, python, perl, ruby, php, node y similares) cuya entrada y salida estándar sean una única conexión TCP que este host abrió hacia una dirección externa. Se cubren bash -i >& /dev/tcp/…, python, perl, nc -e y socat exec:. Solo se informa de lo que se ve dos veces seguidas, una vez por proceso. Por defecto solo cuentan las direcciones externas públicas; las privadas (RFC 1918, CGNAT, enlace local) cuentan solo con reverse_shell_watch_include_private=true, porque el ProxyCommand de ssh, los contenedores y el desarrollo local las hacen frecuentes y legítimas. El loopback nunca se vigila. Las conexiones que el host aceptó (por inetd o socat TCP-LISTEN) tampoco se cubren. Medido en una VM con Ubuntu 24.04, bash /dev/tcp, python, perl, nc -e y socat exec: se detectaron cada uno unos 2,4 segundos después de la conexión. No se detectan ssh, ssh con ProxyCommand, git sobre ssh ni un cliente nc.

Una detección entra por la misma vía que una alerta de Falco (lista de permitidos, enfriamiento de notificaciones, acciones por gravedad de guard.yaml, registro de incidentes) como un evento eBPF Critical llamado RoamSwitch Reverse Shell. Por defecto aísla el proceso y lo mata (kill). Un reverse shell propiedad de root no se puede aislar por proceso, así que, como cualquier evento Critical, pasa al Air-Gap de todo el host. Hay dos salidas si preocupa que se marque por error un programa legítimo: reverse_shell_watch_notify_only=true (un Warning con solo notificación; no se aísla ni se mata nada; false por defecto) y una entrada en la lista de permitidos, sudo roamswitch server allowlist add "RoamSwitch Reverse Shell" <nombre del proceso>. Las tres claves de ajuste son reverse_shell_watch_enabled (true por defecto), reverse_shell_watch_include_private (false) y reverse_shell_watch_notify_only (false).

Límites: se filtra por nombre (nombre del proceso y del ejecutable), así que un binario casero con un nombre distinto del de una shell o un intérprete que ate su entrada y salida estándar a un socket no se encuentra. Las conexiones por loopback (como el retorno de ssh -R) y las de menos de 2 segundos no se ven, ni tampoco los procesos que no son visibles desde el espacio de procesos del host. Esta detección existe solo en la Server Edition; el Mac y el cliente Linux no la tienen.

4.5 Aislamiento de emergencia por espacio de aire con alertas previas al despido

Inmediatamente antes de cortar las interfaces de red ante una manipulación crítica, el demonio envía sincrónicamente alertas de emergencia a Telegram, LINE y Webhooks. Las alertas agregan automáticamente el servidor Hostname y Primary IP Address.

Formato de alerta de emergencia (JA/EN)
🚨 [RoamSwitch IMMINENT AIR-GAP] CRITICAL: IMMINENT AIR-GAP HOST ISOLATION (cve_2026_53362_lpe)
🚨 CRITICAL SECURITY INCIDENT DETECTED: cve_2026_53362_lpe
Host: production-node-01 (IP: 192.168.1.10)
Process: exploit_payload (PID: 4921)
ACTION: Initiating host Air-Gap network isolation (SSH preservation active).
WARNING: Outbound internet connectivity will be cut immediately after this message.
Timestamp: 2026-09-06 18:00:00
⚠️ Network connectivity will be severed immediately.

Las condiciones de levantamiento dependen de la confianza del activador. Los activadores de baja confianza (por ejemplo, un evento eBPF de nivel Error o inferior) se restauran por completo con un temporizador corto (server_safeguards.safety_timer_secs en guard.yaml, 300 segundos por defecto). Los activadores de alta confianza (eBPF Critical/Emergency/Alert, escalada a aislamiento del host por una regla que se repite, manipulación de un canario, etc.) nunca restauran en silencio. Tras el temporizador corto se vuelve a comprobar la causa (si el PID congelado sigue vivo, si la regla se disparó de nuevo, etc.) y el aislamiento se levanta, con notificación, solo cuando se verifica que la causa ha desaparecido. Si no se puede verificar y no hay ACK al alcanzar el límite (isolation_hard_cap_secs, 3600 segundos por defecto; 0 = sin límite), pasa a DEGRADED. DEGRADED conserva la ruta de administración (puertos SSH de mantenimiento e IP de origen de mantenimiento, conexiones nuevas y establecidas), permite tráfico saliente nuevo solo hacia las IP de origen de mantenimiento y nunca se reabre por temporizador. Solo se levanta mediante una reverificación que confirme que la causa desapareció, con roamswitch server ack (un ACK durante DEGRADED significa levantamiento) o con roamswitch emergency-restore. Un aislamiento con ACK en estado FULL se considera asumido por el operador: ni se restaura por temporizador, ni se levanta automáticamente cuando se confirma que la causa desapareció, ni pasa a DEGRADED, y se mantiene hasta que lo levanta emergency-restore. Cada transición se anota en la cronología de incidentes y se notifica.

Registrador de ejecuciones de procesos & reenvío de eventos

Un registrador estrictamente local que anota exec mediante el conector de procesos de netlink funciona activado por defecto (compartido con la edición Client). Guarda la ruta del ejecutable, los argumentos de la línea de comandos (con longitud limitada y caracteres de control escapados), la ascendencia de procesos padre, el uid, el ID del contenedor, el SHA-256 del ejecutable, etc. en /var/lib/roamswitch/exec_log como JSON Lines con cadena de hashes (0600; 200 MB / 14 días por defecto). La cadena de hashes hace que la manipulación sea detectable pero no evitable (un atacante que obtenga root puede reescribir toda la cadena). Las reglas de correlación solo de notificación EXR-001 a EXR-010 (shell lanzada por un servicio web, ejecución desde /tmp o memfd, cadenas de herramientas de ataque, etc.) nunca aíslan por sí solas. Solo si un operador activa explícitamente exec_rules_isolation_optin (false por defecto), una regla de gravedad alta se trata como activador de alta confianza del aislamiento del host.

El reenvío de eventos (forward_enabled, false por defecto) solo envía a los destinos que el operador configuró: syslog (RFC 5424 o CEF, por UDP / TCP / TLS), un archivo JSON Lines o un webhook firmado con HMAC-SHA256. El syslog TLS pasa por el openssl s_client del sistema y el webhook por el curl del sistema (los secretos se pasan en una configuración por la entrada estándar, nunca en la línea de comandos); el demonio no incluye ningún crate TLS/HTTP. Lo que se envía se limita a los campos nombre de host, ID de evento, marca de tiempo, origen, gravedad, regla, acción, PID, nombre del proceso y resumen —para los eventos de detección que aparecen en la cronología de incidentes y el historial de notificaciones (eBPF, transiciones del estado de aislamiento, FIM, canarios, etc.) y para las notificaciones de reglas de ejecución— más los hashes de sellado de segmentos del registro de ejecuciones. El registro bruto de ejecuciones no se incluye.

Detección de tunelización DNS / exfiltración

Al activar dns_tunnel_detect_enabled (valor predeterminado: true), se inspeccionan las consultas DNS salientes propias de este host (solo puerto 53 UDP/TCP) mediante un consumidor NFQUEUE dedicado. Una consulta se cuenta como de «forma sospechosa» —el patrón característico de herramientas de tunelización DNS como iodine o dnscat2— cuando su etiqueta supera los 40 bytes Y su entropía de Shannon supera 3,8, o cuando su tipo de registro es TXT/NULL (solo las consultas TXT hacia clamav.net, que usa la actualización de firmas de ClamAV, no se cuentan); solo se notifica cuando se producen 10 o más consultas de ese tipo en una ventana deslizante de 60 segundos (las notificaciones repetidas para el mismo dominio se espacian 300 segundos y, mientras la ráfaga continúa, se alargan a 600 y 1200 segundos, hasta un máximo de 1800 segundos). Solo se inspecciona el tráfico DNS (puerto 53 UDP/TCP); no se examina ningún otro tráfico. Solo notifica y nunca bloquea automáticamente. La regla nft subyacente lleva el indicador bypass, de modo que las consultas DNS siguen pasando sin verse afectadas aunque el hilo de detección esté detenido o no responda: la resolución de nombres nunca se bloquea por un fallo de esta función. Un cambio de este ajuste solo surte efecto tras ejecutar roamswitch server restart (sin recarga en caliente).

Limitación conocida — los nombres de host largos separados por guiones (por ejemplo, prod-us-east-1-web-server-node-0042) y una actividad legítima especialmente realista en un servidor —como un servidor de correo que verifica registros TXT de SPF/DKIM de muchos dominios remitentes distintos en poco tiempo— provocaban aquí falsos positivos, según se comprobó de forma empírica, porque las ráfagas TXT/NULL se contaban con un único contador global que no distinguía el dominio de destino. Ambos se corrigieron el 23/09/2026. La corrección consiste en una lista blanca de entropía configurable y en contar las ráfagas por dominio apex (una aproximación sencilla basada en las dos últimas etiquetas del QNAME, no una implementación estricta de la Public Suffix List). La lista blanca incluye de forma predeterminada 16 sufijos de CDN/nube y PaaS habituales (cloudfront.net, akamaiedge.net, akamaized.net, edgekey.net, edgesuite.net, fastly.net, azureedge.net, googleusercontent.com, amazonaws.com, r2.dev, workers.dev, vercel.app, netlify.app, supabase.co, firebaseapp.com, web.app), y los administradores pueden añadir sus propios dominios internos mediante ServerConfig.dns_tunnel_entropy_allowlist (una clave separada por comas en server.conf, también editable desde el TUI). La lista blanca solo exime la comprobación de longitud de etiqueta más entropía; la detección por tipo de registro TXT/NULL sigue aplicándose siempre, independientemente de la lista blanca, lo que impide que un atacante disfrace un túnel bajo un dominio permitido (la única excepción son las consultas TXT hacia clamav.net que usa la actualización de firmas de ClamAV, que no se cuentan porque un tercero no puede crear subdominios bajo ese dominio). El contador de ráfagas ahora se organiza por dominio apex, lo que elimina los falsos positivos causados por un único contador global que ignoraba el dominio de destino (la lógica de decisión, con su ventana deslizante de 60 segundos y su umbral de 10 consultas, no se modificó con esta corrección; el intervalo entre notificaciones repetidas se cambió después para alargarse de 300 a un máximo de 1800 segundos mientras la ráfaga continúa). El tráfico malicioso real de herramientas como iodine o dnscat2 siempre rota etiquetas y tipos de registro bajo un único dominio controlado por el atacante, por lo que limitar el recuento por dominio apex no reduce la eficacia de la detección.

Detección y bloqueo de fuerza bruta SSH (ssh_guard)

Una defensa contra fuerza bruta propia de RoamSwitch, activa con ssh_guard_enabled (true por defecto). Sigue el registro de sshd desde journald con journalctl -f (solo sin journald sigue auth.log o secure), cuenta los fallos por dirección de origen y, para un origen que alcanza el umbral (por defecto 6 en 600 segundos), descarta solo las conexiones nuevas (ct state new) hacia los puertos SSH en la tabla inet roamswitch_sshguard. En el momento del bloqueo también se cortan las conexiones que ese origen ya tenía abiertas hacia SSH (se eliminan sus entradas de conntrack y una regla descarta los paquetes del sentido de respuesta). Solo se bloquea un origen sin sesión autenticada, sin inicio de sesión con clave pública registrado y sin exención, y esto se comprueba otra vez justo antes de cortar, así que una sesión autenticada y un origen exento nunca se cortan. Un bloqueo empieza en 1 hora, se cuadruplica en cada reincidencia y tiene un tope de 7 días; el vencimiento lo gestiona el timeout de nftables. No inicia el Air-Gap y no envía nada al exterior (solo ejecuta localmente comandos como journalctl, nft y loginctl). Cuando fail2ban (con una jail sshd activada) o CrowdSec (con un firewall bouncer en marcha) ya protege sshd, le cede el bloqueo y solo detecta y notifica.

Las líneas del registro contienen una cadena que elige el atacante (el nombre de usuario). Por eso la dirección de origen se lee solo del final de la línea, en from <IP> port <N>, y una dirección de origen falsa puesta en el nombre de usuario no hace que se bloquee a un tercero. De journald solo se leen las líneas cuyas credenciales del remitente (_UID, _COMM) son las de un sshd de root, y se ignoran las escritas con logger -t sshd. Seguir un archivo (auth.log) no es lo predeterminado, porque el archivo no tiene ningún campo que avale al remitente y cualquier usuario local que pueda usar syslog puede falsificar líneas. Para no dejar fuera a los operadores, nunca se bloquean la puerta de enlace, el DNS, el propio host, el bucle local, whitelist_ips, ssh_guard_allow, el origen de una sesión SSH autenticada, cualquier dirección que haya iniciado sesión con clave pública en los últimos 30 días y un Sensor emparejado. Las direcciones privadas no están exentas. Un fallo de clave pública (Failed publickey) no se cuenta.

Medido (una VM Ubuntu 24.04 con journald y systemd reales): del 6.º fallo al bloqueo pasaron unos 0,2 segundos (de 0,17 a 0,24 segundos). La dirección de origen del operador no se bloqueó, y un intento con una dirección de origen falsa en el nombre de usuario no hizo que se bloqueara a un tercero. Un hydra real (4 en paralelo) se detuvo tras 6 fallos. También se comprobaron la restauración de un bloqueo con su tiempo restante tras reiniciar el servidor, el levantamiento de un bloqueo y el paso a solo detectar y notificar mientras fail2ban protege sshd. Estas cifras proceden de este entorno de VM. Medido con el corte de conexiones añadido: los fallos que aún pasaban tras un bloqueo eran de 16 a 24 antes y de 0 a 4 después. Eliminar solo las entradas de conntrack no cortaba las conexiones; solo funcionó al añadir una regla que descarta el sentido de respuesta.

Reglas de la capa de aplicación web (web shells y código inyectado)

La edición Servidor no tenía ningún análisis activado por escritura, solo el análisis periódico de clamav_scan_dirs. Ahora vigila las raíces de documentos que encuentra en la configuración del servidor web (Apache, nginx, Caddy) con fanotify solo de notificación (FAN_CLASS_NOTIF, que nunca puede detener a otro proceso) y comprueba los archivos PHP, JavaScript, JSP y ASP en cuanto se escriben y cierran, con las reglas de la capa de aplicación web (web shells, código ofuscado inyectado, PHP oculto en imágenes, abuso de .htaccess). Un análisis periódico cada 6 horas cubre los eventos perdidos y los archivos anteriores al inicio. Las reglas tienen tres capas (integradas, instaladas por el actualizador diario tras verificar la firma y sus archivos en /etc/roamswitch/rules.d/*.json); las 18 reglas no contienen ninguna firma extraída de una muestra real y describen solo técnicas documentadas públicamente.

En la edición Servidor, la cuarentena automática se limita a critical por defecto. Un resultado high solo notifica y dispara el hook posterior a la detección; el archivo de un sitio en producción no se toca. Las notificaciones (Telegram, LINE, webhook) están en 10 idiomas e indican la regla y si el archivo se puso en cuarentena o se dejó. La obtención de reglas va incluida en la misma obtención única del actualizador diario y no añade ningún destino. Con cve_kernel_map_updates_enabled desactivado no hay ningún contacto de red y se usan las reglas integradas. Con un fanotify real, una web shell escrita se puso en cuarentena en menos de 2 segundos y un archivo inofensivo se dejó intacto.

Tras una detección, un ejecutable colocado en /etc/roamswitch/hooks/post-detection recibe los resultados como JSON por la entrada estándar (solo se ejecuta un archivo propiedad de root, no escribible y en un directorio seguro; sin shell, entorno mínimo y parada a los 30 segundos). La respuesta opcional sobre cuentas (desactivada por defecto) revoca el shell de inicio de sesión del propietario de un archivo puesto en cuarentena. No hace nada con root, cuentas por debajo de account_min_uid (1000 por defecto), usuarios con sesión iniciada ni el usuario del demonio; el shell anterior se registra y roamswitch web-rules unlock-account lo restablece. Falsos positivos medidos: 0 en el núcleo de WordPress (2.409 archivos), phpMyAdmin (3.356 archivos) y dependencias npm (14.743 archivos). Límites: solo se leen los primeros 256 KB (al escribir/ejecutar) y 4 MB (análisis), no se siguen nombres fragmentados como 'ev'.'al' y, por falta de muestras reales, no se incluyen firmas de malware concreto.

Honeytokens (señuelos de credenciales)

Al iniciarse el daemon, RoamSwitch despliega de forma idempotente archivos de credenciales señuelo en rutas reales conocidas que los atacantes y las herramientas automatizadas de robo de credenciales realmente rastrean: /root/.aws/credentials, /root/.ssh/id_rsa, /root/.docker/config.json, /root/.pypirc y /root/.env.backup (bajo /root, ya que el daemon se ejecuta como root), cada uno con un marcador ROAMSWITCH_HONEYTOKEN (activado por defecto; honeytokens_enabled lo desactiva y honeytokens_home cambia dónde se colocan los señuelos). Un archivo real ya existente en cualquiera de esas rutas nunca se sobrescribe (el despliegue siempre comprueba primero su existencia). El acceso real a un honeytoken también puede desencadenar, con un límite máximo, el despliegue en cascada de señuelos adicionales relacionados (un perfil de AWS adicional, un config de SSH, un kubeconfig, etc.). Los paquetes hasta la 1.10.4 no incluían roamswitch-honeytokens, necesario para esta función, por lo que no se ejecutaba en las instalaciones desde los paquetes. Se incluye desde la 1.10.5, y el problema de que el sandbox de systemd bloqueara las escrituras en /root en la edición de servidor también se corrigió en la 1.10.5. Desde la 1.11.1, cada comando docker que ejecuta el propio RoamSwitch se inicia con un directorio privado vacío como DOCKER_CONFIG, por lo que no activa el señuelo /root/.docker/config.json. La lectura por cualquier otro programa se sigue detectando como antes.

Un proceso vigilante en segundo plano (roamswitch-honeytokens) supervisa la lectura y la apertura de cada señuelo mediante fanotify de clase de notificación (no detiene nada). Como el evento incluye el PID de quien accede, se puede identificar en el acto el ejecutable que accede (en entornos donde fanotify no está disponible, se cambia a inotify y fuser). Como ningún proceso legítimo tiene motivo alguno para leer estos archivos, cualquier acceso, salvo el de los binarios propios de RoamSwitch y el de los clientes OpenSSH sobre los señuelos SSH (ssh, scp, sftp, ssh-add, ssh-agent, etc.; ssh-keygen no está incluido), se trata como una señal fuerte de reconocimiento posterior a un compromiso por parte de un intruso (MITRE ATT&CK T1552, Unsecured Credentials) y se notifica. Un acceso cuyo ejecutable no se puede identificar tampoco se permite, sino que se trata como detección. Es solo de notificación: no se toman medidas adicionales, como congelar o bloquear el proceso que accede.

Limitación conocida — el contenido del señuelo /root/.ssh/id_rsa incluye texto legible y su cuerpo en base64 sigue sin ser un formato válido para una clave privada OpenSSH real. Sin embargo, la mayoría de los escáneres de secretos marcan los archivos únicamente mediante una coincidencia de patrón con la cabecera -----BEGIN ... PRIVATE KEY-----, por lo que consideramos limitado el impacto real en la tasa de detección. Por otro lado, la identificación del proceso que accede (mediante una llamada a fuser) puede no obtener el PID ni el nombre del proceso en el caso de procesos extremadamente efímeros que finalizan justo después de leer el archivo; en ese caso simplemente se registra como desconocido, lo cual no afecta a la detección del acceso en sí (el evento se dispara igualmente y se notifica).

Paquetes de evidencias (captura forense automática)

La captura de evidencias se lanza en estos casos. Un evento del sensor de amenazas en tiempo de ejecución basado en eBPF culmina en un aislamiento de red específico de un solo proceso con éxito; ese aislamiento falla y se escala a un Air-Gap de todo el host; o se ejecuta un aislamiento Air-Gap de todo el host. También se lanza en la vía del canario de ransomware, cuando se detecta una alteración, se detiene el proceso y se aísla el host. Cuando el corte afecta a todo el host, se lanza antes del corte y nunca lo retrasa. Hasta la 1.10.4 solo se ejecutaba tras un aislamiento específico de proceso con éxito; la 1.10.5 amplió esos casos. Si no se conoce ningún PID objetivo, se omite la instantánea de /proc. Se lanza roamswitch-incident-capture y guarda un paquete de evidencias en <directorio de estado>/incident-evidence/<marca de tiempo>-<id corto>/. Lo que se captura: una captura de paquetes acotada a 30 segundos mediante tshark en la interfaz de ruta predeterminada (registrada como omitida si tshark no está instalado), y una instantánea /proc/<pid> del proceso objetivo (status, cmdline, maps, un environ con los valores que parecen secretos sustituidos por [REDACTED], una lista de los destinos de los descriptores de archivo abiertos —no se captura su contenido— y la cadena de procesos ancestros). El fallo al capturar un artefacto concreto no se trata como error fatal; simplemente se registra y continúa el resto de la captura. Los paquetes hasta la 1.10.4 no incluían roamswitch-incident-capture, necesario para esta función, por lo que no se ejecutaba en las instalaciones desde los paquetes. Se incluye desde la 1.10.5.

Los resultados se recogen en manifest.json, que además incorpora el hash SHA-256 de cada uno de los demás archivos del paquete (para poder detectar manipulaciones posteriores). Los paquetes nunca se eliminan automáticamente: los paquetes antiguos solo se eliminan cuando un operador ejecuta explícitamente sudo roamswitch-incident-capture prune [--keep-days N] (retención predeterminada: 30 días). También está disponible en cualquier momento una captura manual mediante sudo roamswitch-incident-capture snapshot [--pid PID] [--reason TEXT] [--json].

Las pruebas capturadas no se verifican en el momento de la captura. Para comprobar posteriormente si han sido alteradas, ejecute sudo roamswitch-incident-capture verify <directorio del paquete> [--json]. El comando recalcula el hash de cada archivo presente actualmente en el paquete y lo compara con los valores registrados en manifest.json, informando cada discrepancia por separado como modificado, ausente o no registrado (un archivo presente que el manifiesto nunca registró).

Limitación conocida — esta verificación compara el SHA-256 de manifest.json con registros guardados fuera del paquete, en dos lugares: una cadena de hashes fuera del paquete (evidence_anchor.log, cada línea se encadena con la anterior, y se le asigna el atributo de solo anexado) y el registro del sistema (etiqueta roamswitch-evidence). Reescribir manifest.json tras la captura hace fallar verify; editar o eliminar una línea de la cadena también, porque la cadena se rompe. No detiene a un atacante con root que pueda reescribir tanto la cadena como el destino al que se reenvía el registro del sistema; se recomienda reenviar el registro del sistema a otra máquina. La firma no está implementada. Un paquete sin registro (capturado por una versión anterior) se indica como «sin registro», lo que significa que no tiene confirmación externa.

§5Paquete de diagnóstico de seguridad de 34 comprobaciones

Server Edition ejecuta 34 auditorías de estado de seguridad especializadas:

# Categoría Comprobar título Estándar y lógica
1Autenticación y accesoAutenticación de inicio de sesión y contraseña de raíz SSHVerificación de PermitRootLogin no. Si la autenticación por contraseña también está deshabilitada, se informa como solo autenticación por clave pública; aunque esté habilitada, se considera superada si el inicio de sesión de root está prohibido
2Autenticación y accesoCuentas de contraseña vacíasGarantiza que ningún usuario tenga un hash de contraseña en blanco en /etc/shadow
3Autenticación y accesoHigiene privilegiada de SudoersAuditoría de que no exista NOPASSWD: ALL, que concede todos los privilegios sin contraseña, en /etc/sudoers ni en /etc/sudoers.d
4RedDenegación predeterminada entranteAuditorías nftables para la política de entrega entrante predeterminada
5Núcleo e integridadEspacios de nombres de usuarios sin privilegios (Frag Gap)Bloqueo de la vía de ataque LPE mediante user.max_user_namespaces=0 (o kernel.unprivileged_userns_clone=0)
6Núcleo e integridadSoporte eBPF BTFValida la información del tipo BTF del kernel a través de /sys/kernel/btf/vmlinux
7Núcleo e integridadeBPF LSM activoComprueba bpf en el parámetro de arranque lsm= del kernel
8RedCompuerta XDP de arranqueCubre con una compuerta XDP de denegación por defecto la ventana de arranque anterior a la instalación de la tabla nftables de Link Guard (experimental, solo por activación voluntaria, no se carga por defecto). Comprueba la presencia del enlace fijado; que no esté cargada no se considera un fallo
9Núcleo e integridadAgente de seguridad en tiempo de ejecución eBPFComprueba que un agente como Falco o Tetragon está en marcha. Mientras Falco funciona, comprueba también que vuelve la alerta de la regla propia de RoamSwitch (el heartbeat) y falla si no vuelve (indicando los archivos de Falco de RoamSwitch que falten)
10Núcleo e integridadCronología unificada de incidentesComprueba que la cronología que unifica los incidentes de Link Guard, Canary, eBPF Guard y Resource Guard puede registrar (eventos existentes o almacén con permiso de escritura) y muestra el último tiempo de contención
11ContenedorAuditoría de montaje de zócalo DockerDetecta montajes de contenedores de /var/run/docker.sock
12ContenedorContenedores privilegiados de DockerAuditorías para cargas de trabajo de contenedores --privileged de alto riesgo
13ContenedorAislamiento del runtime de contenedores (runc/gVisor/Kata)Comprueba el runtime de contenedores predeterminado. Un runtime basado en runc (comparte el kernel del host) no supera la comprobación; gVisor (runsc) o Kata Containers (aislamiento con una VM por contenedor) sí. No aplicable si no se detecta Docker/containerd
14ContenedorProtección contra la evasión del cortafuegos por Docker (DOCKER-USER)Verifica que las reglas de RoamSwitch estén realmente presentes en la cadena DOCKER-USER (siempre evaluada primero) o en su tabla nftables dedicada de protección de Docker, para que los puertos publicados de los contenedores no eludan la denegación por defecto del cortafuegos mediante las reglas propias de Docker. Solo se evalúa si protect_docker_ports está habilitado en server.conf
15Núcleo e integridadPrueba real de detección de ClamAV (EICAR)Dos minutos después del inicio y luego cada 6 horas, el demonio analiza un archivo de prueba EICAR inofensivo desde su propio entorno (por ejemplo, dentro de un sandbox de systemd) para confirmar que ClamAV lo detecta realmente. Solo se evalúa si clamav_enabled está activado en server.conf
16Núcleo e integridadfanotify: eventos perdidos (desbordamiento de cola)Comprueba si el kernel ha descartado eventos de supervisión de FIM y de ejecución de contenedores por desbordamiento de la cola de eventos de fanotify. Las escrituras y ejecuciones de archivos durante un desbordamiento escapan a la supervisión en tiempo real; si ocurre con frecuencia, aumente fs.fanotify.max_queued_events
17Núcleo e integridadExposición a CVE conocidas del kernelCompara la versión del kernel en ejecución con el mapa local de CVE conocidas (línea base integrada o actualización con firma verificada). La comprobación en sí no realiza ninguna comunicación externa; las actualizaciones del mapa se descargan según cve_kernel_map_updates_enabled (true por defecto, se puede desactivar). Un resultado que no puede determinarse se notifica como no concluyente, no como un fallo
18integridadIntegridad de archivos de ruta crítica (FIM)Verificación de integridad SHA-256 de 23 archivos críticos fijos, de todos los archivos directamente dentro de 12 directorios, y más
19Núcleo e integridadProtección del proceso YamaPrevención del espionaje de memoria mediante kernel.yama.ptrace_scope con valor 2 o 3
20RedReenvío de IP deshabilitadoVerifica net.ipv4.ip_forward = 0
21RedProtección de cookies TCP SYNVerifica net.ipv4.tcp_syncookies = 1
22RedFiltrado de ruta inversa (rp_filter)Descarte de paquetes falsificados mediante net.ipv4.conf.all.rp_filter con valor 1 (estricto) o 2 (flexible; para configuraciones con varias interfaces, multihomed)
23Núcleo e integridadRestricción de volcado de núcleoPreviene pérdidas de memoria de proceso a través de fs.suid_dumpable = 0
24OperacionesActualizaciones de seguridad automáticasComprobación de la configuración de aplicación automática de parches mediante unattended-upgrades (Debian / Ubuntu) o dnf-automatic (RHEL / Fedora), entre otros
25OperacionesSincronización de hora NTPComprueba la sincronización a través de chrony o systemd-timesyncd
26OperacionesProtección de fuerza bruta SSHSe supera mientras la guardia SSH propia de RoamSwitch (ssh_guard) esté en marcha y bloqueando, o verifica el monitoreo activo a través de fail2ban o crowdsec
27Núcleo e integridadeBPF sin privilegios deshabilitadoPrevención de Spectre/LPE mediante kernel.unprivileged_bpf_disabled con valor 1 o 2
28RedRedirecciones ICMP ignoradasPreviene el envenenamiento de ruta vía accept_redirects = 0
29RedTransmisiones de eco ICMP ignoradasMitiga los ataques DoS del amplificador Smurf
30Núcleo e integridadEndurecimiento de la memoria compartidaComprobación de que /dev/shm tiene alguna de las opciones de montaje nodev, nosuid o noexec
31Autenticación y accesoProtocolos inseguros heredados ausentesVerifica que no haya servicios en texto claro a la escucha (telnetd, rshd, tftpd, …) ni clientes en texto claro (telnet, rsh, tftp, …) instalados (los servicios y los clientes se informan por separado).
32Núcleo e integridadVolcados de núcleo del proceso SUID restringidosPreviene fugas de credenciales en fallas binarias SUID
33Núcleo e integridadProgramas auxiliares (señuelos y captura de evidencias) instaladosEl demonio inicia por nombre el programa de señuelos de credenciales (honeytokens) y el de paquetes de evidencias, y no hace nada, en silencio, si falta alguno. Esta comprobación verifica que ambos estén instalados.
34RedArchivos sensibles expuestos en el servidor webLocaliza las raíces de documentos en la configuración de Apache / nginx / Caddy y comprueba, con peticiones al propio servidor, si los .git, .env, .DS_Store, phpinfo, volcados SQL, copias de seguridad y claves privadas que hay en ellas se pueden leer realmente a través del servidor web. Un archivo solo se considera expuesto si la respuesta coincide con el comienzo del archivo en disco, de modo que un sitio que responde 200 a cualquier ruta, o un archivo ya denegado con 403, no genera una falsa alarma. Nunca se informa del contenido de los archivos, solo de las rutas. No aplica sin servidor web; si no se puede comprobar, el resultado es «no concluyente» y la puntuación no se reduce.

Cada uno de los 34 elementos se etiqueta, cuando corresponde, con un identificador de función/categoría de NIST CSF 2.0 (por ejemplo, PR.DS-01, DE.CM-09) y, solo cuando existe una correspondencia fiable, también con un número de control de CIS Controls v8 (por ejemplo, 10.1, 4.1). Los elementos ambiguos no se fuerzan a una correspondencia, sino que quedan sin asignar. Esta correspondencia se incluye tanto en la salida de la CLI (roamswitch server status) como en la de MCP (get_security_report), permitiendo a los operadores y auditores que necesitan cobertura de marcos de cumplimiento asignar mecánicamente las comprobaciones de RoamSwitch a los marcos existentes.

§6Autoverificación (verificación reproducible en un entorno Docker)

Sobre el paquete roamswitch-server instalado desde el repositorio APT oficial, se realiza una Autoverificación que simula ataques internos y externos, con otro contenedor en Docker como atacante externo. La medición real del 2026-09-07 con la v1.3.2 fue de 18 puntos PASS en total. El 2026-09-25 se repitió con un paquete compilado a partir del código fuente de la serie 1.10 (arm64, Docker Desktop) y 19 comprobaciones fueron PASS. La nueva ejecución encontró 6 defectos del producto, todos corregidos (incluidos en la 1.10.5). Son los siguientes: el paquete no incluía los ejecutables auxiliares, el FIM informaba de los honeytokens como archivos nuevos, se ignoraba la confirmación (ack), durante el aislamiento se descartaban las respuestas del SSH de administración y el administrador quedaba bloqueado, el puerto 22 seguía abierto incluso con preserve_ssh_on_isolation=false, y el sandbox de la unidad systemd bloqueaba la escritura de los señuelos. Puede reproducir esta comprobación en su propia máquina. Consulte el procedimiento y los scripts en «Cómo reproducirlo usted mismo» más abajo.

# Escenario Vector de ataque Resultado de la verificación
PENT-S1 Nmap SYN Escaneo sigiloso Sonda a través de los puertos 22, 80, 443, 3306, 8080 Con Default Drop de nftables, los puertos no permitidos como 3306 (MySQL) y 8080 no responden en absoluto y se descartan (Filtered). Bloqueo del 100%
PENT-S2 Sondeo de puerto no permitido conexiones curl directas al puerto 3306 y 8080 Con el descarte entrante por defecto, solo el puerto permitido 80 responde con normalidad; el resto se bloquea por tiempo de espera agotado
PENT-S3 Lista de bloqueo de Egress / C2 Intento de conexión saliente a una dirección registrada en el feed de IP maliciosas egress_ip_blocklist_enabled (true por defecto, nftables) bloquea solo las direcciones registradas. El tráfico hacia hosts no registrados no se ve afectado (no es un bloqueo general)
PENT-S4 Ataque de manipulación FIM Inyectar carga útil de puerta trasera en /bin/login El FIM de rutas críticas SHA-256 detecta la manipulación de inmediato. Tras una restauración legítima, la línea base se resincroniza correctamente
PENT-S5 Contención basada en gravedad mediante guard.yaml Inyección de un evento Falco Critical que hace referencia a un proceso real Con on_critical: isolate_process + kill_process, el proceso correspondiente se aísla de la red y después se termina con SIGKILL
PENT-S6 Mecanismo de seguridad para procesos protegidos Inyección de un evento del mismo tipo que hace referencia al PID real de sshd Gracias a la lista de procesos protegidos (sshd / systemd / runtimes de contenedores, etc.), sshd sigue funcionando. Solo se registra un rechazo como falso positivo
PENT-S7 Temporizador de seguridad Se dejó un incidente de Air-Gap sin confirmar (sin ACK del operador) Con safety_timer_secs, se restaura automáticamente si no hay respuesta. Si ya se confirmó (ACK), el aislamiento continúa hasta emergency-restore
PENT-S8 Brecha de fragmentación LPE del kernel Ejecución de unshare -U -r con privilegios de usuario normal (comprobado en una VM real) unshare -U -r, que se permite sin el producto, se rechaza tras el arranque. user.max_user_namespaces y kernel.unprivileged_userns_clone valen 0, el ptrace_scope de Yama vale 2 y el eBPF sin privilegios está deshabilitado (2026-09-25, VM con Ubuntu 24.04; comprobado tanto con la versión publicada v1.10.4 como con la versión corregida)

Nota: la medición de PENT-S7 es de la v1.3.2. En la versión actual, un aislamiento causado por un disparador de alta confianza se libera cuando se verifica que su causa ha desaparecido, y un aislamiento reconocido (ack) por el operador se mantiene hasta que el operador lo libera (un ack ignorado se encontró y corrigió en la nueva ejecución del 2026-09-25). Los disparadores de baja confianza se recuperan con el mismo temporizador. Véase §4.

Tipo de comprobaciones — PENT-S8 confirma que una función del kernel (prohibir espacios de nombres de usuario) está en vigor. PENT-S1 y S2 confirman las reglas nftables que instala RoamSwitch; quien descarta los paquetes es el kernel. Los puntos que ejercitan la lógica propia de RoamSwitch son PENT-S3 (aplicación del feed), S4 (FIM), S5 y S6 (contención y procesos protegidos) y S7 (temporizador de seguridad).

Prevención de bloqueo de SSH

preserve_ssh_on_isolation=true (predeterminado) está diseñado para mantener el canal SSH de administración durante un Air-Gap (on_emergency). Sin embargo, la nueva ejecución del 2026-09-25 mostró que, hasta la 1.10.4, las reglas de aislamiento descartaban las respuestas SSH, por lo que el operador quedaba sin acceso durante el aislamiento; la comprobación anterior solo había tenido éxito después de que se levantara el aislamiento. La corrección está en la 1.10.5, y se ha verificado, con la causa del aislamiento aún presente, que el SSH funciona durante el aislamiento.

Límites del alcance de la verificación (una divulgación honesta)

Lo que esto no puede verificar

El endurecimiento del kernel (deshabilitar los espacios de nombres de usuario sin privilegios, aumentar el ptrace_scope de Yama) no se puede verificar de extremo a extremo desde dentro de un contenedor. Dado que estos sysctl no tienen espacio de nombres por contenedor, las escrituras desde un contenedor privilegiado (--privileged) no afectan a un entorno aislado de prueba, sino a la propia máquina real que ejecuta la verificación. Por lo tanto, esta verificación solo confirma que la escritura del sysctl se degrada correctamente (sin fallos) frente a un sistema de archivos de solo lectura; confirmar que el valor realmente se aplica requiere una máquina real o una VM, que es el destino para el que está pensado el producto. Esta comprobación se realizó una sola vez, el 2026-09-25, en una VM con Ubuntu 24.04 (véase la fila PENT-S8).

Reprodúzcalo usted mismo

Todos los resultados anteriores se pueden reproducir al 100 % en su propia máquina, utilizando únicamente el paquete de distribución oficial, mediante el conjunto de pruebas Docker publicado en el repositorio de soporte oficial (roamswitch-support); no se requiere acceso al código fuente ni confiar en nosotros.

# Obtener y reproducir la suite de verificación (instala automáticamente roamswitch-server desde el repositorio APT oficial)
git clone https://github.com/lafine1211/roamswitch-support.git
cd roamswitch-support/test/docker/server
./run.sh

Crea tres contenedores desechables (Target, Attacker y un señuelo C2) en la red bridge predeterminada de Docker, verifica automáticamente los 18 puntos y genera un informe. El resultado completo de la ejecución con la v1.3.2 está publicado en RESULTS-PENTEST-SERVER-2026-09-07.ja.md (incluyendo, por transparencia, los problemas del propio arnés de pruebas detectados y corregidos durante su construcción). El registro de la nueva ejecución del 2026-09-25 está en RESULTS-PENTEST-2026-09-25.ja.md.

§7Operaciones, implementación y ciclo de vida

7.1 Asistente de configuración y herramientas de CUI sin cabeza

Gestión remota completa a través de CLI:

  • roamswitch server status: genera el informe de diagnóstico de 34 puntos (admite 10 idiomas y sigue la configuración del usuario que lo ejecuta o la configuración regional del sistema operativo).
  • roamswitch server setup: Asistente de terminal interactivo para puertos y notificaciones.
  • roamswitch server config show / set <key> <val>: Automatización programable para Ansible/Terraform.
  • roamswitch server test-notify [all|telegram|line|webhook]: sondeo de notificación de un extremo a otro.
  • roamswitch server config set language <ja|en|zh-Hans|zh-Hant|ko|de|fr|es|it|pt|system>: el idioma del informe de diagnóstico periódico del demonio (cada hora) y de las notificaciones push (Telegram / LINE / webhook, incluido el mensaje de prueba), un idioma por host. system sigue la configuración regional del sistema operativo, y una configuración regional no identificable pasa a ja. El idioma de visualización de roamswitch server status no depende de este ajuste, sino de la configuración del usuario que lo ejecuta o de la configuración regional del sistema operativo.
  • roamswitch server bans [--json] / sudo roamswitch server unban <IP|/64 IPv6|--all>: estado de la guardia contra fuerza bruta SSH, y lista y levantamiento de sus bloqueos. La herramienta MCP get_ssh_guard_incidents es de solo lectura y no puede levantar un bloqueo.

TUI de Server (roamswitch server tui)

Una interfaz de terminal que permite manejar toda la edición Server por SSH, en 80x24 y solo con el teclado (roamswitch server tui o roamswitch-server-tui). Tiene 11 pantallas: panel, protección, incidentes, notificaciones y reenvío, Sensor, configuración, servicio, herramientas, auditoría de registros, información y ayuda, y Fleet. El editor de configuración se genera a partir de las definiciones de claves (el esquema) de server.conf y guard.yaml: valida tipo, rango y valores enumerados, indica si hace falta reiniciar, muestra un diff antes de guardar y guarda con copia de seguridad con marca de tiempo y escritura atómica (los valores secretos, como los tokens, se enmascaran y se introducen sin eco). Las operaciones de gran impacto, como restaurar un aislamiento, hacer ACK, actualizar la línea base de FIM o detener el servicio, muestran una pantalla de confirmación que indica el efecto, y las de mayor impacto exigen escribir yes (confirmación escrita). Sin root es de solo lectura e indica el comando sudo roamswitch ... necesario. No hay recarga de configuración; hace falta reiniciar el servicio para que los cambios surtan efecto. La interfaz admite 10 idiomas.

7.2 Embalaje

Se distribuye mediante paquetes .deb para Debian / Ubuntu y paquetes .rpm para RHEL / Fedora y para openSUSE.

§8Conclusión

RoamSwitch Server Edition ofrece a las infraestructuras Linux de nube y empresa, en un único paquete integrado, «Zero Telemetry (la única comunicación externa por defecto es una recepción de datos firmados, sin envío, que se puede desactivar)», «una sólida protección perimetral mediante denegación entrante por defecto», «la detección inmediata de manipulaciones en rutas críticas» y «el bloqueo preventivo de la escalada de privilegios del núcleo».

Una suite de seguridad para servidores Linux que combina detección por capas con contención autónoma (Air-Gap) en las principales fases que persiguen los atacantes: reconocimiento inicial, intrusión, escalada de privilegios y persistencia.

Este documento refleja la implementación de RoamSwitch Server Edition v1.11.1 (denegación entrante por defecto, FIM de rutas críticas, motor de políticas guard.yaml, endurecimiento LPE del núcleo, monitorización en tiempo de ejecución con eBPF, levantamiento escalonado del Air-Gap, registrador de ejecuciones y reenvío de eventos, la TUI de Server, detección de tunelización DNS, honeytokens, captura de evidencias forenses, correspondencia con NIST CSF / CIS Controls y actualización del mapa CVE del kernel firmado). Las mediciones reales de las Autoverificaciones del §6, sin embargo, son de la v1.3.2 (2026-09-07) y de un paquete compilado a partir del código fuente de la serie 1.10 (2026-09-25). Consulte el CHANGELOG de roamswitch-support para conocer el historial de cambios detallado. Para la información más reciente, consulte roamswitch.com/linux.