Lafine Systems Design · Documentazione tecnica

Whitepaper su architettura e sicurezza di RoamSwitch Server Edition

Suite headless di sicurezza di rete autonoma e di difesa dell'integrità per infrastrutture Linux cloud e on-premises. Evita espressioni promozionali: quanto descritto può essere verificato rispetto al comportamento effettivo del pacchetto ufficiale distribuito e all'autoverifica del §6.

Versione v1.11.1 Ambito RoamSwitch Server Edition 1.11.1 (rilevamento di tunneling DNS, honeytoken, acquisizione forense delle prove, mappatura NIST CSF / CIS Controls, aggiornamenti della mappa CVE del kernel firmata) Requisiti systemd + nftables Pubblicato 2026-10-08

§1Panoramica e principi fondamentali

RoamSwitch Server Edition è una suite headless e autonoma per la sicurezza di rete e la protezione dell'integrità, creata appositamente per le infrastrutture server Linux tra provider cloud (AWS, GCP, Azure, VPS bare metal) e data center locali.

A differenza delle edizioni workstation client incentrate sulla mobilità e sull'adattamento del perimetro Wi-Fi, la Server Edition si fonda su cinque principi senza compromessi: Inbound Default Drop, Critical Path File Integrity Monitoring (FIM), Preemptive Kernel LPE e Privilege Escalation Hardening, eBPF Runtime Threat Telemetria e Isolamento autonomo dell'host con air-gap con avvisi di emergenza pre-dismissione.

Il principio Zero Telemetry – nessuna libreria di comunicazione esterna nel demone stesso – è applicato a livello di codice (per impostazione predefinita l'unica cosa che esce dall'host è la ricezione di dati firmati (i dati CVE del kernel, le regole web e, sui server Ubuntu, i dati CVE dei pacchetti Ubuntu usati dal controllo CVE dei pacchetti di sistema; manifest controllato ogni giorno, i dati CVE veri e propri circa ogni 30 giorni), che si può anche disattivare, e ogni altra eccezione è qualcosa che l'operatore attiva esplicitamente; vedi i principi di progettazione del §1), e i segreti (token dei bot e configurazione) sono protetti con permessi 0600.

1.1 Confronto: edizione client e edizione server

Dimensione Edizione client (desktop/mobile) Edizione Server (headless/infrastruttura)
Distribuzione Laptop, workstation mobili per sviluppatori VM cloud, nodi bare metal, host container
Comportamento di rete Commutazione dinamica del profilo basata su SSID Wi-Fi / Gateway MAC Rilascio predefinito in entrata statico. Vengono esposte solo le porte esplicitamente consentite
Interfaccia utente GUI desktop GTK3 + indicatore vassoio CUI puro senza testa. Comandi CLI + procedura guidata interattiva + interfaccia a terminale (roamswitch-server-tui)
Notifiche di incidenti Notifiche sul desktop (libnotify) Push diretto a Bot di Telegram, API di messaggistica LINE e Webhook (Slack/Discord)
Difese primarie Spoofing ARP Wi-Fi pubblico, BadUSB, crittografia dei documenti ransomware Rilascio ricognizione esterna, rilevamento manomissione FIM, fughe di container, kernel LPE (CVE-2026-53362)

1.2 Principi architettonici fondamentali

  • Zero Telemetry: per impostazione predefinita non viene inviato all'esterno nulla sull'utente o sull'ambiente. Il demone stesso non contiene alcuna libreria di comunicazione HTTP/TLS; solo per le notifiche e gli inoltri configurati dall'operatore esegue, come sottoprocessi, il curl di sistema (Telegram / LINE / webhook; i segreti come token e URL sono passati in una configurazione sullo standard input, mai sulla riga di comando) e openssl (inoltro syslog TLS). L'unica comunicazione esterna attiva per impostazione predefinita, a parte gli aggiornamenti del gestore di pacchetti, è la seguente. (1) Finché cve_kernel_map_updates_enabled (true per impostazione predefinita) è attivo, roamswitch-updater riceve in forma anonima da lafine.net dati firmati. Sono i dati CVE del kernel per l'audit dell'isolamento dei container, le regole di rilevamento del livello applicativo web (regole web) e, sui server Ubuntu, i dati CVE dei pacchetti Ubuntu usati dal controllo CVE dei pacchetti di sistema. Il manifest firmato e la sua firma vengono controllati ogni giorno (due GET); i dati CVE del kernel e i dati CVE dei pacchetti Ubuntu vengono scaricati solo quando sono passati circa 30 giorni dall'ultimo aggiornamento (un server che non ha ancora i dati Ubuntu li scarica al successivo controllo giornaliero), le regole web solo quando il manifest ha una versione più recente. È un semplice HTTPS GET senza query string né cookie, il cui unico header che nomina il software è uno User-Agent: RoamSwitch-Updater fisso; vengono accettati solo i dati con firma Ed25519 valida, e non viene inviato nulla su questo host. L'operatore può disattivarla con roamswitch server config set cve_kernel_map_updates_enabled false, la procedura guidata di configurazione o la TUI del server; un false esplicito in un server.conf esistente viene mantenuto così com'è, e l'audit continua a funzionare con la baseline integrata quando è disattivata; il controllo CVE dei pacchetti di sistema su un server Ubuntu segnala invece «nessun dato CVE locale» e non restituisce alcun risultato. Ogni altra eccezione è attivata esplicitamente dall'operatore. (2) L'inoltro degli eventi (forward_enabled, false per impostazione predefinita) invia solo alle destinazioni syslog / file JSON Lines / webhook firmato configurate dall'operatore. (3) Solo se npm_audit_signatures_enabled (false per impostazione predefinita) è attivo e l'operatore esegue roamswitch audit-npm-signatures <DIR> --confirm, è npm stesso a contattare registry.npmjs.org. (4) Solo se sensor_pairing_enabled (false per impostazione predefinita) è attivo si connette, e solo all'indirizzo del Sensor inserito dall'operatore (il Sensor è un prodotto distinto, non coperto da Zero Telemetry). (5) Fleet (livello Business) è solo codice lato dispositivo: un dispositivo non registrato non invia nulla e non risolve alcun nome; il servizio di gestione non è distribuito né rilasciato e, per progetto, non esiste alcun canale di comando remoto. (6) L'integrazione LLM dello strumento separato incluso accessscope (diagnosi di sicurezza dei log di accesso) è disattivata per impostazione predefinita. Solo se è attivata (da riga di comando, quando si specifica --llm) un riepilogo della diagnosi, le righe di log come evidenza e informazioni sull'ambiente (per la funzione di ricerca dei log) possono essere inviati a un servizio esterno tramite il comando LLM scelto dall'operatore. Il demone (roamswitch-server) non partecipa a questa comunicazione, AccessScope può eseguire la propria analisi con LLM anche come root (l'LLM gira allora con i privilegi di root; si consiglia un utente senza privilegi), ed è al di fuori di Zero Telemetry. Anche lo strumento separato incluso personalsoc (controllo di log e difese) funziona così: solo quando si specifica --llm, un riepilogo della diagnosi può essere inviato a un servizio esterno tramite il comando LLM scelto dall'operatore (al di fuori di Zero Telemetry). Il comando pathscope non richiama alcun LLM. (7) Solo se clamav_enabled (false per impostazione predefinita) è attivo, il demone esegue freshclam per ricevere le firme di ClamAV (la prima volta, quando non esiste ancora un database delle firme, e, finché è impostato clamav_scan_dirs, ogni clamav_freshclam_interval_secs, 7200 secondi per impostazione predefinita).
  • Rifiuto predefinito in ingresso (Inbound Default Drop): le nuove connessioni in ingresso verso porte (TCP) diverse da quelle consentite vengono scartate da nftables senza rispondere nemmeno con un RST. Una scansione delle porte vede solo le porte consentite (loopback, connessioni già stabilite, ICMP e ICMPv6 sono ammessi).
  • Fail-closed e autoriparazione: le regole di rifiuto predefinito e di isolamento restano nel kernel, quindi sopravvivono all'arresto e al riavvio del demone (Restart=always). La protezione delle porte pubblicate di Docker viene ricontrollata ogni 60 secondi, mentre la blocklist in uscita (egress) e il blocco delle scansioni di porte ogni 300 secondi, e le regole scomparse vengono reinstallate. Le regole di isolamento DEGRADED vengono verificate a ogni valutazione ogni 15 secondi e reinstallate se mancano. Se la regola di rifiuto predefinito stessa viene cancellata dall'esterno, non viene riapplicata automaticamente: verificala con la diagnosi «Rifiuto predefinito in ingresso» (roamswitch server status).
  • Protezione tramite permessi 0600: al file di configurazione /etc/roamswitch/server.conf vengono imposti automaticamente i permessi 0600 alla creazione e all'aggiornamento. I segreti come token e URL dei webhook vengono passati a curl in una configurazione sullo standard input e non sono mai messi sulla riga di comando (visibile agli altri utenti tramite /proc/<pid>/cmdline).

§2Panorama delle minacce e modello avversario

I moderni server connessi a Internet devono far fronte a scansioni automatizzate incessanti, vettori di breakout di contenitori ed escalation dei privilegi del kernel zero-day.

Affrontati i principali vettori di minaccia
  1. Riconoscimento automatizzato ed esposizione DB: scanner a livello di Internet che individuano porte di database interne esposte involontariamente (MySQL 3306, Redis 6379, Postgres 5432).
  2. Manomissione del percorso critico e inserimento di backdoor: intrusi che stabiliscono la persistenza modificando i file binari di autenticazione principali (/bin/login, /usr/bin/sudo, /etc/pam.d/*, /etc/shadow).
  3. Kernel Privilege Escalation (CVE-2026-53362 Frag Gap): aggressori senza privilegi utilizzano come arma gli spazi dei nomi degli utenti (unshare -U -r) per attivare il danneggiamento della memoria del kernel ed eseguire l'escalation a root.
  4. Container Socket Breakout: contenitori configurati in modo errato con flag /var/run/docker.sock o --privileged montati che eseguono azioni root sul sistema operativo host.
  5. Snooping della memoria dei processi: gli aggressori allegano ptrace o leggono /proc/$pid/mem attraverso i processi per estrarre chiavi o credenziali private.

§3Architettura del sistema

RoamSwitch Server Edition funziona come file binari Rust nativi progettati per memoria minima e zero sovraccarico della CPU.

┌─────────────────────────────────────────────────────────────┐
│  Spazio utente e amministratore                             │
│                                                             │
│   roamswitch (CLI)             roamswitch-mcp (AI Agent)    │
│   · server status (34 voci)    · strumenti di audit         │
│   · server config / setup      · stdio JSON-RPC             │
│   · fim verify / update                                     │
│        │                               │                    │
└────────┼───────────────────────────────┼────────────────────┘
         │ (comandi CLI e config)        │ (stdio)
┌────────▼───────────────────────────────▼────────────────────┐
│  Base di sistema (systemd + privilegi root)                 │
│                                                             │
│   roamswitch-server-daemon                                  │
│   ├── CriticalPathFim (scansione SHA-256 in background)     │
│   ├── modulo di integrazione eBPF / Falco                   │
│   ├── Notifier (avvisi immediati Telegram / LINE / Webhook) │
│   └── Air-Gap Controller (regole di emergenza nftables)     │
│                                                             │
│   Config: /etc/roamswitch/server.conf (permessi 0600)       │
│   DB di integrità: /var/lib/roamswitch/fim.db               │
└─────────────────────────────────────────────────────────────┘

§4Meccanismi di difesa fondamentali

4.1 Sicurezza dei confini (eliminazione predefinita in entrata)

Con nftables, nella tabella inet roamswitch_server, applica alla chain input la policy drop. Consente solo le nuove connessioni TCP in ingresso verso le porte indicate dall'amministratore (ssh_ports, allowed_ports) e fa andare in timeout (drop) il traffico verso le porte non consentite. Loopback, connessioni già stabilite, ICMP e ICMPv6 sono consentiti.

Anche se scatta un Air-Gap (isolamento di emergenza), i percorsi di amministrazione — le porte SSH di manutenzione (ssh_ports) e gli IP di origine di manutenzione registrati (whitelist_ips), sia connessioni nuove sia stabilite — restano nel set di regole, evitando che il ripristino da remoto diventi impossibile (preserve_ssh_on_isolation, true per impostazione predefinita). Ogni altra connessione stabilita (per esempio un C2 o una reverse shell già aperti dall'attaccante) viene tagliata dalle catene default-drop. Se non è indicata alcuna sorgente di amministrazione (whitelist_ips e gli indirizzi consentiti di guard.yaml sono vuoti), le porte SSH di manutenzione continuano ad accettare nuove connessioni da qualsiasi origine durante l'isolamento e il server emette un avviso all'avvio. Con preserve_ssh_on_isolation=false, l'isolamento interrompe anche l'SSH. L'edizione server fino alla 1.10.4 presentava un difetto. Le regole di isolamento scartavano le risposte SSH, quindi l'SSH dell'operatore non funzionava durante l'isolamento. È stato corretto nella 1.10.5.

4.2 Monitoraggio dell'integrità dei file del percorso critico (FIM)

Monitora l'integrità tramite hash SHA-256 dei file più critici per l'avvio del sistema operativo, l'autenticazione, la rete e l'amministrazione del sistema. L'ambito comprende 23 percorsi fissi (/etc/shadow, /etc/passwd, /etc/sudoers, /etc/ssh/sshd_config, /bin/login, /usr/bin/sudo, /usr/sbin/sshd, /usr/lib/systemd/systemd, ecc.); tutti i file direttamente contenuti in 12 directory (/etc/pam.d, /etc/sudoers.d, /etc/ssh/sshd_config.d, /etc/cron.d e /etc/cron.{hourly,daily,weekly,monthly}, /etc/systemd/system, /etc/ld.so.conf.d, /etc/profile.d, /root/.ssh), dove viene rilevata anche la comparsa di nuovi file (in /root/.ssh sono esclusi known_hosts e known_hosts.old, perché vengono riscritti a ogni connessione SSH); i binari di RoamSwitch stesso; l'authorized_keys di ogni utente che può accedere; e /etc/ld.so.preload, che non deve esistere (ne viene rilevata la comparsa). Dopo un aggiornamento legittimo dei pacchetti la baseline sana viene aggiornata automaticamente: nei sistemi Debian dall'hook /etc/apt/apt.conf.d/99roamswitch-fim (apt upgrade ecc.), in quelli RHEL e in openSUSE da un'unità systemd.path che sorveglia /var/lib/rpm (roamswitch-fim-update.path); entrambi eseguono roamswitch fim update.

Individuazione del percorso di intrusione (chi ha fatto la modifica e tramite quale accesso SSH)

Le notifiche su un file modificato (FIM dei percorsi critici e FIM dei lockfile) riportano ora il processo che ha fatto la modifica (account, eseguibile, comando) e l'accesso SSH a cui appartiene (IP e porta di origine, ora di accesso, metodo di autenticazione e impronta della chiave pubblica). È attivo per impostazione predefinita (fim_intrusion_context_enabled). Nel momento in cui un evento viene letto, il daemon scatta un'istantanea di /proc/<pid> del processo che scrive (eseguibile, utente, loginuid, sessionid, cgroup, catena dei genitori), perché un processo come tee o sed scompare un millisecondo dopo. Un processo già terminato viene integrato dal registratore di esecuzioni (se attivo). Il collegamento a un accesso usa prima la tabella delle sessioni della guardia SSH e, in mancanza, le righe Accepted di logind e journald. Funziona anche con la guardia SSH disattivata. Non viene inviato nulla all'esterno.

Ci si fida solo dei valori riportati dal kernel: l'eseguibile viene da /proc/<pid>/exe (non da argv[0]), l'accesso da loginuid (impostato una sola volta al login e ereditato attraverso sudo e su) e dal session-N.scope del cgroup, il resto dalla catena dei genitori. I valori che un processo può cambiare da sé, come comm, la riga di comando e l'ambiente, vengono mostrati ma non usati mai per decidere; un processo che si limita a chiamarsi sshd non viene scambiato per un accesso SSH. Una notifica tiene distinte tre risposte: trovato (origine, ora, metodo, chiave); «Non è stato trovato alcun accesso SSH» (il controllo ha mostrato un servizio systemd, un container, una sessione di login locale o un processo senza sessione di login); e «Non è stato possibile determinare il percorso di accesso» (il processo non c'era già più e non è nel registratore di esecuzioni, logind non ha risposto, la modifica è stata trovata dalla scansione periodica e il processo è sconosciuto, e così via). Ciò che non si è potuto verificare non viene mai riportato come «non è un accesso». La notifica parte anche se la ricerca fallisce.

/etc/ssh/sshd_config: modified
  Processo: root (ha effettuato l'accesso come admin) / /usr/bin/tee / comando: tee /etc/ssh/sshd_config (pid 4121)
  Accesso SSH: la sessione aperta da admin da 203.0.113.25:51544 alle 2026-10-17 03:12:08 (autenticazione: publickey, chiave SHA256:AbCdEf0123456789)

/etc/sudoers: modified
  Non è stato possibile determinare il percorso di accesso (la modifica è stata trovata dalla scansione periodica, quindi il processo responsabile non è noto)

Vengono ora rilevate anche le modifiche che collocano un nuovo file con lo stesso nome: install, sed -i e cp seguito da mv nella stessa directory. Il contrassegno per file è legato all'inode, perciò questo tipo di modifica prima non generava alcun evento. Ora il daemon contrassegna anche le directory che contengono file sorvegliati e, quando in quella stessa directory viene chiuso un file con un altro nome, ricerca di nuovo il nome pochi millisecondi dopo. Limite: un file scritto in un'altra directory e spostato con mv viene trovato solo dalla scansione periodica (fim_check_interval_secs). In quel caso il processo che ha scritto non è noto, e la notifica lo dice.

Le misurazioni sono state fatte su una VM Ubuntu 24.04 con l'origine dell'operatore in un network namespace (netns) separato. Con sudo tee, con un processo staccato con nohup (il suo IP di origine è stato identificato anche dopo il logout dell'operatore, con la nota «Quella sessione è già terminata»), con una catena sudo -i e con un cat di vita brevissima, la notifica ha mostrato IP, utente e metodo di autenticazione. Un accesso locale ha dato «Non è stato trovato alcun accesso SSH». Un processo che ha mascherato il proprio nome con exec -a "sshd: …" non è stato scambiato per SSH.

Oltre alla notifica, il rilevamento viene conservato nella cronologia degli incidenti (tipo fim) e in /var/lib/roamswitch/fim_intrusions.json (solo root, gli ultimi 100). Lo strumento MCP verify_fim lo restituisce in un campo aggiunto, recentIntrusionContext (solo se eseguito come root). Il verdetto su una manomissione del file non cambia. Limiti: con il registratore di esecuzioni disattivato e un processo scrivente che vive solo un istante, l'eseguibile non è noto e vengono mostrati l'eseguibile e la riga di comando del processo padre. Le notifiche della sorveglianza della persistenza (persistence_watch) non contengono informazioni sul processo e non ricevono quindi il percorso di intrusione. Il percorso mostra il processo che ha fatto la modifica e il suo accesso, non oltre: la porta d'ingresso in sé (la vulnerabilità sfruttata o il modo in cui una chiave è stata rubata) non viene mostrata. Per disattivarlo, imposta fim_intrusion_context_enabled=false.

4.3 Rafforzamento del kernel e del contenitore

Frag Gap (CVE-2026-53362) Mitigazione: applica user.max_user_namespaces = 0 tramite sysctl, neutralizzando i vettori LPE. Yama LSM Protection applica kernel.yama.ptrace_scope = 2, impedendo l'ispezione non autorizzata della memoria. I carichi di lavoro dei contenitori vengono controllati per l'esposizione docker.sock. Nell'edizione server questo divieto dei namespace non privilegiati è attivo per impostazione predefinita (harden_userns_enabled). Impedisce Podman rootless e simili; impostalo su false se ne hai bisogno.

Le porte pubblicate di Docker (-p) aggirano, com'è noto, il firewall standard dell'host. Per questo, con protect_docker_ports=true (impostazione predefinita) viene scartato il traffico proveniente dall'interfaccia esterna la cui porta di destinazione prima del DNAT non è tra le porte consentite (ssh_ports, allowed_ports) (il traffico di ritorno, il loopback e il traffico tra container non sono interessati). Con il backend iptables / iptables-nft le regole vanno nella catena DOCKER-USER (IPv4, e anche tramite ip6tables se l'IPv6 di Docker è attivo); con il backend nftables nativo di Docker (da Docker 29, configurazione senza DOCKER-USER) in una tabella dedicata inet roamswitch_docker_guard (hook forward, priorità -10), e quest'unico set di regole copre sia IPv4 sia IPv6. Il demone ricontrolla periodicamente e reinstalla le regole scomparse. Inoltre, una guardia sempre attiva rileva in tempo reale tramite docker events l'avvio di un container --privileged o il montaggio di /var/run/docker.sock e lo segnala subito come rischio di fuga dal container (nessuna configurazione; solo notifica, senza blocco automatico). Per gli host che gestiscono file destinati ad altri, come relay di posta o cartelle condivise, il File Scan Guard, attivabile a scelta (YARA integrato + ClamAV opzionale), offre scansione periodica e quarantena automatica.

Container Exec Guard (container_exec_guard_enabled, true per impostazione predefinita) — imposta fanotify FAN_OPEN_EXEC (classe notifica) sulla radice overlay2 di ogni container Docker in esecuzione e ispeziona il contenuto di ogni file eseguito con le regole YARA integrate, notificando in caso di corrispondenza. Poiché si sottoscrive a docker events e poi elenca i container in esecuzione con docker ps, sono coperti anche i container già in esecuzione all'avvio del demone. Il contenuto del file eseguito viene letto dal file descriptor fornito da fanotify, quindi può essere ispezionato anche se il file si cancella da solo subito dopo l'esecuzione. Si limita a notificare e non ferma né nega l'esecuzione (l'edizione Server non ha alcun fanotify di classe permessi). Docker 29 ha eliminato dai messaggi di docker events il campo Status, ormai deprecato, quindi fino alla 1.11.0 la sottoscrizione falliva subito e la protezione continuava a riconnettersi ogni 5 secondi senza mai ottenere una sottoscrizione. Dalla 1.11.1 usa Action, che ha lo stesso valore anche sui Docker meno recenti. Quando la sottoscrizione continua a fallire, l'intervallo di riconnessione raddoppia da 5 secondi fino a un massimo di 5 minuti, e l'avviso per la stessa causa viene registrato solo la prima volta.

4.4 Telemetria delle minacce runtime eBPF e ottimizzazione anti-gonfiore

Utilizza moderne sonde eBPF (CO-RE / BTF) per intercettare l'escalation dei privilegi e i tentativi di breakout del contenitore in millisecondi. Per eliminare i punti critici standard di Falco—log bloat, esaurimento del disco e sovraccarico della CPU—RoamSwitch standardizza un profilo ottimizzato (/etc/falco/config.d/99-roamswitch-optimized.yaml):

  • Severity Filtering (priority: warning): sopprime la telemetria operativa rumorosa (informazioni/avvisi) del 95%, facendo emergere incidenti di sicurezza strettamente perseguibili.
  • Kernel-Level Pre-Drop (drop_failed_exit: true): scarta le uscite syscall non riuscite direttamente nel sonda kernel, riducendo i cambi di contesto della CPU di oltre il 50%.
  • Ottimizzazione dell'impronta della memoria (cpus_for_each_buffer: 2): comprime i buffer circolari da 8 MB a 2 MB, limitando rigorosamente l'utilizzo della memoria a 30–50 MB.
  • Direct UNIX Domain Socket IPC: coordinamento immediato tramite un socket in memoria (/run/roamswitch/events.sock), senza passare dall'I/O su disco.
  • Limite automatico dei log (/etc/logrotate.d/roamswitch-falco): con maxsize 50M e una rotazione compressa su 7 generazioni (eseguita ogni giorno) si contiene la crescita dei log di Falco.

Precisione e rapidità dell'isolamento eBPF (senza affidarsi solo alle regole di Falco)

La precisione e la rapidità dell'isolamento eBPF dipendono dalle regole del sensore (Falco o Tetragon). In effetti, le regole standard di Falco 0.45.0 (29 in tutto) non ne includono nessuna che rilevi una reverse shell sull'host: le regole per le reverse shell valgono solo per i container e alcune vengono scartate dalla condizione priority: warning di RoamSwitch. RoamSwitch non dà per scontata la bontà delle regole; compensa in tre modi: misura quanto dura l'isolamento, verifica regolarmente che il percorso degli avvisi sia vivo e rileva da sé le reverse shell.

Misurazione della latenza dell'isolamento: per ogni incidente eBPF il daemon registra il tempo che passa dal rilevamento di Falco alla lettura dell'avviso (detect_to_receive_ms), dalla lettura al termine della risposta (receive_to_action_ms, misurato dopo il ritorno della chiamata di isolamento; per l'Air-Gap dell'host, fino al caricamento del ruleset) e la loro somma (detect_to_action_ms). I valori compaiono come campi aggiunti in ebpf_incidents.json e nello strumento MCP get_ebpf_incidents. La notifica di un processo isolato riporta una riga «dal rilevamento alla risposta: N ms». Misurato su una VM Ubuntu 24.04 (Falco 0.45.0, modern eBPF): da 1 a 2 ms dal rilevamento alla ricezione, da 41 a 62 ms dalla ricezione all'isolamento del processo più SIGKILL (da 42 a 64 ms in totale) e 109 ms in totale per l'Air-Gap dell'host (entro l'obiettivo di 150 ms). I valori sono di quella VM. Tetragon applica l'orario alla ricezione, quindi non ha detect_to_receive_ms.

Heartbeat (ebpf_heartbeat_interval_secs; 600 secondi per impostazione predefinita, 0 lo disattiva, altrimenti da 60 a 86400): il pacchetto include una regola Falco propria di RoamSwitch, 96-roamswitch-heartbeat.yaml. A ogni intervallo il daemon apre un percorso usa e getta che solo root può usare (un nome casuale sotto /run/roamswitch/heartbeat/) e misura quanto tempo impiega l'avviso di quella regola a tornare dall'output di Falco. Un solo andata e ritorno mostra insieme che la regola è caricata, che Falco funziona e che il percorso di output è vivo. Solo quando due controlli di fila non tornano (il secondo un minuto dopo il primo) avvisa: «le regole non sono caricate o l'output non arriva», al massimo una volta ogni 24 ore. Una riga di heartbeat viene scartata senza raggiungere isolamento, notifica, allowlist o registro degli incidenti. Non viene inviato nulla all'esterno. Gli andata e ritorno misurati sono stati di 2-6 ms; togliendo la regola l'avviso è arrivato dopo circa 164 secondi e, ripristinandola, lo stato è tornato normale in circa 75 secondi.

Lo stato è conservato in /var/lib/roamswitch/ebpf_heartbeat.json e viene letto dal campo heartbeat dello strumento MCP get_ebpf_incidents e dalla diagnostica K-04 (agente runtime eBPF; il numero di voci diagnostiche resta 34). Anche se Falco è in esecuzione, K-04 non passa se l'heartbeat non torna. Limiti: si verifica un solo andata e ritorno di una sola regola (le chiamate di sistema della famiglia open); non si può sapere se un'altra regola sia rotta per conto suo. Ciò che si rompe tra due controlli (10 minuti per impostazione predefinita) non si vede fino al successivo. Non viene eseguito per Tetragon. Su un host in cui Falco non è in esecuzione o manca il file della regola, non si controlla nulla e non si avvisa di nulla (se ne occupa la notifica esistente «guardia arrestata»). Falco scrive una riga di avviso nel syslog a ogni controllo (144 righe al giorno con l'impostazione predefinita).

Rilevamento delle reverse shell (proprio di RoamSwitch)

Senza affidarsi alle regole di Falco, RoamSwitch legge da sé /proc ogni 2 secondi e cerca una shell o un interprete (sh, bash, python, perl, ruby, php, node e simili) il cui input e output standard siano un'unica connessione TCP che questo host ha aperto verso un indirizzo esterno. Sono coperti bash -i >& /dev/tcp/…, python, perl, nc -e e socat exec:. Viene segnalato solo ciò che si vede due volte di fila, una volta per processo. Per impostazione predefinita contano solo gli indirizzi esterni pubblici; quelli privati (RFC 1918, CGNAT, link-local) contano solo con reverse_shell_watch_include_private=true, perché il ProxyCommand di ssh, i container e lo sviluppo locale li rendono comuni e legittimi. Il loopback non viene mai sorvegliato. Anche le connessioni accettate dall'host (da inetd o socat TCP-LISTEN) non sono coperte. Misurato su una VM Ubuntu 24.04, bash /dev/tcp, python, perl, nc -e e socat exec: sono stati rilevati ciascuno circa 2,4 secondi dopo la connessione. Non vengono rilevati ssh, ssh con ProxyCommand, git su ssh e un client nc.

Un rilevamento segue lo stesso percorso di un avviso di Falco (allowlist, raffreddamento delle notifiche, azioni per gravità di guard.yaml, registro degli incidenti) come evento eBPF Critical chiamato RoamSwitch Reverse Shell. Per impostazione predefinita isola il processo e lo termina (kill). Una reverse shell di proprietà di root non può essere isolata per processo; come per ogni evento Critical, si passa quindi all'Air-Gap dell'intero host. Ci sono due vie d'uscita se si teme che un programma legittimo venga segnalato per errore: reverse_shell_watch_notify_only=true (un Warning con sola notifica; non si isola né si termina nulla; false per impostazione predefinita) e una voce nella allowlist, sudo roamswitch server allowlist add "RoamSwitch Reverse Shell" <nome del processo>. Le tre chiavi di impostazione sono reverse_shell_watch_enabled (true per impostazione predefinita), reverse_shell_watch_include_private (false) e reverse_shell_watch_notify_only (false).

Limiti: il filtro avviene per nome (nome del processo e dell'eseguibile), perciò un binario fatto in casa con un nome diverso da quello di una shell o di un interprete che leghi input e output standard a un socket non viene trovato. Le connessioni via loopback (come il ritorno di ssh -R) e quelle più brevi di 2 secondi non si vedono, né si vedono i processi non visibili dallo spazio dei processi dell'host. Questo rilevamento esiste solo nella Server Edition; il Mac e il client Linux non lo hanno.

4.5 Isolamento di emergenza del traferro con avvisi pre-interruzione

Immediatamente prima di tagliare le interfacce di rete in caso di manomissione critica, il demone invia in modo sincrono avvisi di emergenza a Telegram, LINE e Webhook. Gli avvisi aggiungono automaticamente il server Nome host e Indirizzo IP primario.

Formato avviso di emergenza (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-app-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.

Le condizioni di rilascio dipendono dalla confidenza del trigger. I trigger a bassa confidenza (ad es. un evento eBPF di livello Error o inferiore) vengono ripristinati completamente con un timer breve (guard.yaml, server_safeguards.safety_timer_secs, 300 secondi per impostazione predefinita). I trigger ad alta confidenza (eBPF Critical/Emergency/Alert, escalation all'isolamento dell'host per una regola che si ripete, manomissione di un canary, ecc.) non ripristinano mai in silenzio. Dopo il timer breve la causa viene ricontrollata (il PID congelato è ancora attivo, la regola è scattata di nuovo, ecc.) e l'isolamento viene rimosso, con notifica, solo quando la scomparsa della causa è verificata. Se non è verificabile e non c'è ACK al raggiungimento del limite (isolation_hard_cap_secs, 3600 secondi per impostazione predefinita; 0 = nessun limite), passa a DEGRADED. DEGRADED mantiene il percorso di amministrazione (porte SSH di manutenzione e IP di origine di manutenzione, connessioni nuove e stabilite), consente nuovo traffico in uscita solo verso gli IP di origine di manutenzione e non si riapre mai per timer. Si rimuove solo con una riverifica che accerti la scomparsa della causa, con roamswitch server ack (un ACK durante DEGRADED significa rilascio) o con roamswitch emergency-restore. Un isolamento confermato con ACK in stato FULL è considerato assunto dall'operatore: non viene ripristinato dal timer, non viene rimosso automaticamente nemmeno quando la scomparsa della causa è verificata e non passa a DEGRADED; resta finché non lo rimuove emergency-restore. Ogni transizione è registrata nella cronologia degli incidenti e notificata.

Registratore delle esecuzioni dei processi & inoltro degli eventi

Un registratore strettamente locale che annota exec tramite il connettore di processo netlink funziona attivo per impostazione predefinita (in comune con l'edizione Client). Salva percorso dell'eseguibile, argomenti della riga di comando (lunghezza limitata, caratteri di controllo sottoposti a escape), discendenza dei processi padre, uid, ID del container, SHA-256 dell'eseguibile e altro in /var/lib/roamswitch/exec_log come JSON Lines con catena di hash (0600; 200 MB / 14 giorni per impostazione predefinita). La catena di hash rende la manomissione rilevabile ma non prevenibile (un attaccante che ottiene root può riscrivere l'intera catena). Le regole di correlazione a sola notifica da EXR-001 a EXR-010 (shell avviata da un servizio web, esecuzione da /tmp o memfd, catene di strumenti di attacco, ecc.) non isolano mai da sole. Solo se un operatore attiva esplicitamente exec_rules_isolation_optin (false per impostazione predefinita), una regola ad alta gravità è trattata come trigger ad alta confidenza per l'isolamento dell'host.

L'inoltro degli eventi (forward_enabled, false per impostazione predefinita) invia solo alle destinazioni configurate dall'operatore: syslog (RFC 5424 o CEF, su UDP / TCP / TLS), un file JSON Lines o un webhook firmato con HMAC-SHA256. Il syslog TLS passa per l'openssl s_client di sistema e il webhook per il curl di sistema (i segreti sono passati in una configurazione sullo standard input, mai sulla riga di comando); il demone non include alcun crate TLS/HTTP. Ciò che viene inviato è limitato ai campi nome host, ID evento, timestamp, origine, gravità, regola, azione, PID, nome del processo e riepilogo — per gli eventi di rilevamento che compaiono nella cronologia degli incidenti e nella cronologia delle notifiche (eBPF, transizioni dello stato di isolamento, FIM, canary, ecc.) e per le notifiche delle regole di esecuzione — più gli hash di sigillo dei segmenti del registro delle esecuzioni. Il log grezzo delle esecuzioni non è incluso.

Rilevamento di tunneling DNS / exfiltrazione

Abilitando dns_tunnel_detect_enabled (valore predefinito: true) si ispezionano le query DNS inviate da questo host stesso (solo porta 53 UDP/TCP) tramite un consumer NFQUEUE dedicato. Una query viene conteggiata come «di forma sospetta», il pattern caratteristico degli strumenti di tunneling DNS come iodine o dnscat2, quando la sua etichetta supera i 40 byte E la sua entropia di Shannon supera 3,8, oppure quando il suo tipo di record (qtype) è TXT/NULL (le sole query TXT verso clamav.net, usate dall'aggiornamento delle firme di ClamAV, non vengono conteggiate). Viene inviata una notifica solo quando si verificano 10 o più query di questo tipo negli ultimi 60 secondi (le notifiche ripetute per lo stesso dominio attendono 300 secondi, poi 600 e 1200 secondi finché il burst continua, fino a un intervallo massimo di 1800 secondi). Viene ispezionato solo il traffico DNS (porta 53 UDP/TCP); nessun altro traffico viene esaminato. La funzione si limita a notificare e non blocca mai automaticamente. La regola nft sottostante porta il flag bypass, quindi le query DNS continuano a passare senza essere influenzate anche se il thread di rilevamento è fermo o non risponde: la risoluzione dei nomi non viene mai bloccata da un malfunzionamento di questa funzione. Una modifica di questa impostazione ha effetto solo dopo roamswitch server restart (nessun ricaricamento a caldo).

Limite noto: i nomi host lunghi separati da trattini (ad esempio prod-us-east-1-web-server-node-0042) e un'attività legittima particolarmente realistica in ambito server, come un server di posta che verifica record TXT SPF/DKIM per molti domini mittenti diversi in poco tempo, generavano falsi positivi, come verificato con misurazioni, perché i burst TXT/NULL venivano conteggiati con un unico contatore globale che non distingueva il dominio di destinazione. Entrambi sono stati risolti il 23/09/2026. La soluzione è una allowlist di entropia configurabile e il conteggio dei burst per dominio apex (un'approssimazione semplice basata sulle ultime due etichette del QNAME, non un'implementazione rigorosa della Public Suffix List). L'allowlist include per impostazione predefinita 16 suffissi comuni di CDN, cloud e PaaS (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), e gli amministratori possono aggiungere i propri domini interni tramite ServerConfig.dns_tunnel_entropy_allowlist (una chiave separata da virgole in server.conf, modificabile anche dalla TUI). L'allowlist esonera solo il controllo combinato lunghezza dell'etichetta più entropia; il rilevamento in base al tipo di record TXT/NULL si applica sempre, a prescindere dall'allowlist, il che impedisce a un attaccante di mascherare un tunnel sotto un dominio consentito (l'unica eccezione sono le query TXT verso clamav.net, usate dall'aggiornamento delle firme di ClamAV, che non vengono conteggiate perché una terza parte non può creare sottodomini sotto quel dominio). Il contatore dei burst è ora suddiviso per dominio apex, il che elimina i falsi positivi causati da un unico contatore globale che ignorava il dominio di destinazione (la logica di finestra scorrevole di 60 secondi e la soglia di 10 query non sono cambiate con questa correzione; l'intervallo di notifica ripetuta è stato poi modificato in modo da crescere da 300 secondi fino a un massimo di 1800 secondi finché il burst continua). Il traffico malevolo reale di strumenti come iodine o dnscat2 ruota sempre etichette e tipi di record sotto un unico dominio controllato dall'attaccante, quindi limitare il conteggio per dominio apex non riduce l'efficacia del rilevamento.

Rilevamento e blocco della forza bruta SSH (ssh_guard)

Una difesa dalla forza bruta propria di RoamSwitch, attiva con ssh_guard_enabled (true per impostazione predefinita). Segue il log di sshd da journald con journalctl -f (solo in assenza di journald segue auth.log o secure), conta i fallimenti per indirizzo di origine e, per un'origine che raggiunge la soglia (di default 6 in 600 secondi), scarta solo le nuove connessioni (ct state new) verso le porte SSH nella tabella inet roamswitch_sshguard. Al momento del ban vengono interrotte anche le connessioni che quell'origine aveva già aperto verso SSH (si eliminano le sue voci conntrack e una regola scarta i pacchetti della direzione di risposta). Viene bannita solo un'origine senza sessione autenticata, senza login a chiave pubblica registrato e senza esenzione, e lo si ricontrolla subito prima di interrompere: una sessione autenticata e un'origine esente non vengono quindi mai interrotte. Un ban parte da 1 ora, si quadruplica a ogni recidiva e ha un tetto di 7 giorni; la scadenza è gestita dal timeout di nftables. Non avvia l'Air-Gap e non invia nulla all'esterno (in locale esegue solo comandi come journalctl, nft e loginctl). Quando fail2ban (con una jail sshd attiva) o CrowdSec (con un firewall bouncer in esecuzione) protegge già sshd, gli lascia il ban e si limita a rilevare e notificare.

Le righe del log contengono una stringa scelta dall'attaccante (il nome utente). Perciò l'indirizzo di origine viene letto solo dalla fine della riga, in from <IP> port <N>, e un indirizzo di origine falso inserito nel nome utente non fa bannare un terzo. Da journald vengono lette solo le righe le cui credenziali del mittente (_UID, _COMM) sono quelle di un sshd di root; le righe scritte con logger -t sshd vengono ignorate. Seguire un file (auth.log) non è l'impostazione predefinita, perché il file non ha alcun campo che garantisca il mittente e qualsiasi utente locale autorizzato a usare syslog può falsificare righe. Per non chiudere fuori gli operatori, non vengono mai bannati il gateway, il DNS, l'host stesso, il loopback, whitelist_ips, ssh_guard_allow, l'origine di una sessione SSH autenticata, qualsiasi indirizzo che abbia effettuato l'accesso con chiave pubblica negli ultimi 30 giorni e un Sensor associato. Gli indirizzi privati non sono esentati. Un fallimento di chiave pubblica (Failed publickey) non viene contato.

Misurato (una VM Ubuntu 24.04 con journald e systemd reali): dal 6° fallimento al ban sono trascorsi circa 0,2 secondi (da 0,17 a 0,24 secondi). L'indirizzo di origine dell'operatore non è stato bannato, e un tentativo con un indirizzo di origine falso nel nome utente non ha fatto bannare un terzo. Un vero hydra (4 in parallelo) è stato fermato dopo 6 fallimenti. Sono stati verificati anche il ripristino di un ban con il tempo residuo dopo un riavvio del server, la rimozione di un ban e il passaggio alla sola rilevazione con notifica mentre fail2ban protegge sshd. Questi valori provengono da questo ambiente VM. Misurato con l'interruzione delle connessioni aggiunta: i fallimenti che passavano ancora dopo un ban erano da 16 a 24 prima e da 0 a 4 dopo. Eliminare solo le voci conntrack non interrompeva le connessioni; ha funzionato solo dopo aver aggiunto una regola che scarta la direzione di risposta.

Regole del livello applicativo web (web shell e codice iniettato)

L'edizione Server non aveva alcuna scansione attivata dalla scrittura, solo quella periodica di clamav_scan_dirs. Ora controlla le document root trovate nella configurazione del web server (Apache, nginx, Caddy) con fanotify di sola notifica (FAN_CLASS_NOTIF, che non può mai bloccare un altro processo) ed esamina i file PHP, JavaScript, JSP e ASP subito dopo che sono stati scritti e chiusi, con le regole del livello applicativo web (web shell, codice offuscato iniettato, PHP nascosto nelle immagini, abusi di .htaccess). Una scansione periodica ogni 6 ore copre gli eventi persi e i file preesistenti. Le regole hanno tre livelli (integrate, installate dall'aggiornatore giornaliero dopo la verifica della firma e i tuoi file in /etc/roamswitch/rules.d/*.json); le 18 regole non contengono alcuna firma ricavata da un campione reale e descrivono solo tecniche documentate pubblicamente.

Nell'edizione Server la quarantena automatica è limitata a critical per impostazione predefinita. Un risultato high si limita a notificare e a far partire l'hook post-rilevamento; il file di un sito in produzione non viene toccato. Le notifiche (Telegram, LINE, webhook) sono in 10 lingue e indicano la regola e se il file è stato messo in quarantena o lasciato. Il recupero delle regole è incluso nello stesso recupero unico dell'aggiornatore giornaliero e non aggiunge alcuna destinazione. Con cve_kernel_map_updates_enabled disattivato non c'è alcun contatto di rete e si usano le regole integrate. Con un vero fanotify, una web shell scritta è stata messa in quarantena entro 2 secondi e un file innocuo è stato lasciato.

Dopo un rilevamento, un eseguibile posto in /etc/roamswitch/hooks/post-detection riceve i risultati come JSON sullo standard input (viene eseguito solo un file di proprietà di root, non scrivibile e in una directory sicura; nessuna shell, ambiente minimo, interruzione dopo 30 secondi). La risposta facoltativa sugli account (disattivata per impostazione predefinita) revoca la shell di login del proprietario di un file messo in quarantena. Non fa nulla per root, account con UID inferiore a account_min_uid (1000 per impostazione predefinita), utenti connessi e utente del demone; la shell precedente viene registrata e roamswitch web-rules unlock-account la ripristina. Falsi positivi misurati: 0 nel core di WordPress (2.409 file), in phpMyAdmin (3.356 file) e nelle dipendenze npm (14.743 file). Limiti: si leggono solo i primi 256 KB (in scrittura/esecuzione) e 4 MB (scansione), i nomi frammentati come 'ev'.'al' non vengono seguiti e, mancando campioni reali, non si forniscono firme di malware specifici.

Honeytoken (esche di credenziali)

All'avvio del demone, RoamSwitch distribuisce in modo idempotente file di credenziali esca in percorsi reali noti effettivamente scansionati da attaccanti e strumenti automatizzati di furto di credenziali (/root/.aws/credentials, /root/.ssh/id_rsa, /root/.docker/config.json, /root/.pypirc e /root/.env.backup, sotto /root poiché il demone viene eseguito come root), ciascuno contenente un marcatore ROAMSWITCH_HONEYTOKEN. Attivo per impostazione predefinita: honeytokens_enabled lo disattiva e honeytokens_home cambia dove vengono collocate le esche. Un file reale già esistente in uno di questi percorsi non viene mai sovrascritto (la distribuzione verifica sempre prima l'esistenza del file). Un accesso reale a un honeytoken può inoltre attivare, entro un limite massimo, la distribuzione a cascata di ulteriori esche correlate (un profilo AWS aggiuntivo, un config SSH, un kubeconfig e simili). I pacchetti fino alla 1.10.4 non includevano roamswitch-honeytokens, necessario per questa funzione, che quindi non veniva eseguita nelle installazioni dai pacchetti. È incluso dalla 1.10.5, e nella 1.10.5 è stato corretto anche il problema per cui la sandbox di systemd bloccava le scritture in /root nell'edizione server. Dalla 1.11.1, i comandi docker eseguiti da RoamSwitch stesso vengono avviati con una directory dedicata vuota come DOCKER_CONFIG, quindi non fanno scattare l'esca /root/.docker/config.json. La lettura da parte di qualsiasi altro programma viene rilevata come prima.

Un watcher in background (roamswitch-honeytokens) usa fanotify di classe notifica (non blocca nulla) per monitorare la lettura e l'apertura di ciascuna esca. Poiché l'evento porta con sé il PID di chi accede, l'eseguibile che accede viene identificato sul momento (negli ambienti in cui fanotify non è disponibile si passa a inotify e fuser). Poiché nessun processo legittimo ha motivo di leggere questi file, ogni accesso diverso da quello dei binari di RoamSwitch stesso e dei client OpenSSH (ssh, scp, sftp, ssh-add, ssh-agent e simili, ma non ssh-keygen) sulle esche SSH viene trattato come un forte segnale di ricognizione post-compromissione da parte di un intruso (MITRE ATT&CK T1552, Unsecured Credentials) e viene inviata una notifica. Anche un accesso il cui eseguibile non è identificabile non viene lasciato passare, ma trattato come rilevamento. La funzione si limita a notificare e non congela né blocca di per sé il processo che accede.

Limite noto: il contenuto dell'esca /root/.ssh/id_rsa include testo leggibile e il suo corpo base64 resta un formato non valido per una vera chiave privata OpenSSH. Tuttavia, la maggior parte degli scanner di segreti segnala i file basandosi solo sulla corrispondenza dell'intestazione -----BEGIN ... PRIVATE KEY-----, quindi riteniamo limitato l'impatto reale sul tasso di rilevamento. Inoltre, negli ambienti in cui fanotify non è disponibile e si passa a inotify, l'identificazione del processo che accede al file (tramite una chiamata a fuser) può non riuscire a ottenere PID o nome del processo per processi estremamente effimeri che terminano subito dopo la lettura; in tal caso il processo viene semplicemente trattato come sconosciuto, il che non influisce sul rilevamento dell'accesso stesso (l'evento scatta comunque e viene comunque notificato).

Pacchetti di prove (acquisizione forense automatica)

L'acquisizione delle prove viene avviata in questi casi. Un evento del sensore di minacce runtime eBPF porta a un isolamento di rete mirato a un singolo processo riuscito; tale isolamento fallisce e si passa a un Air-Gap a livello di host; oppure viene eseguito un isolamento Air-Gap a livello di host. Viene avviata anche sul percorso del canarino anti-ransomware, quando rileva una manomissione, ferma il processo e isola l'host. Per un taglio a livello di host, parte prima del taglio e non lo ritarda mai. Fino alla 1.10.4 veniva eseguita solo dopo un isolamento mirato al processo riuscito; la 1.10.5 ha ampliato i casi. Se non è noto alcun PID target, l'istantanea di /proc viene omessa. roamswitch-incident-capture viene avviato e salva un pacchetto di prove in <directory di stato>/incident-evidence/<timestamp>-<id breve>/. Ciò che viene acquisito: una cattura di pacchetti limitata a 30 secondi tramite tshark sull'interfaccia di instradamento predefinita (registrata come saltata se tshark non è installato), e uno snapshot /proc/<pid> del processo target (status, cmdline, maps, un environ con i valori che sembrano segreti sostituiti da [REDACTED], un elenco delle destinazioni dei descrittori di file aperti — il contenuto non viene acquisito — e la catena dei processi antenati). Il fallimento nell'acquisizione di un singolo artefatto non viene trattato come errore fatale; viene semplicemente registrato e il resto dell'acquisizione prosegue. I pacchetti fino alla 1.10.4 non includevano roamswitch-incident-capture, necessario per questa funzione, che quindi non veniva eseguita nelle installazioni dai pacchetti. È incluso dalla 1.10.5.

I risultati vengono raccolti in manifest.json, che incorpora anche l'hash SHA-256 di ogni altro file del pacchetto (per poter rilevare eventuali manomissioni successive). I pacchetti non vengono mai eliminati automaticamente — i pacchetti vecchi vengono rimossi solo quando un operatore esegue esplicitamente sudo roamswitch-incident-capture prune [--keep-days N] (conservazione predefinita: 30 giorni). È inoltre disponibile in qualsiasi momento un'acquisizione manuale tramite sudo roamswitch-incident-capture snapshot [--pid PID] [--reason TEXT] [--json].

Le prove raccolte non vengono verificate al momento dell'acquisizione. Per controllare in seguito eventuali manomissioni, eseguire sudo roamswitch-incident-capture verify <directory del bundle> [--json]. Il comando ricalcola l'hash di ogni file attualmente presente nel bundle e lo confronta con i valori registrati in manifest.json, segnalando ogni discrepanza singolarmente come modificato, mancante o non tracciato (un file presente che il manifest non aveva mai registrato).

Limite noto: questa verifica confronta lo SHA-256 di manifest.json con registrazioni conservate fuori dal pacchetto, in due posti: una catena di hash fuori dal pacchetto (evidence_anchor.log, in cui ogni riga si appoggia alla precedente, impostata in sola aggiunta dove il file system lo consente) e il log di sistema (tag roamswitch-evidence). Riscrivere manifest.json dopo l'acquisizione fa fallire verify, e lo stesso accade se si modifica o si rimuove una riga della catena, perché la catena si spezza. Non ferma però un attaccante con privilegi root che possa riscrivere sia la catena sia la destinazione a cui viene inoltrato il log di sistema; si consiglia di inoltrare il log di sistema a un'altra macchina. La firma non è implementata. Un pacchetto senza registrazione (acquisito da una versione precedente) viene segnalato come «nessuna registrazione», cioè privo di conferma esterna.

§5Suite di diagnostica di sicurezza da 34 controlli

La Server Edition esegue 34 controlli specializzati sullo stato della sicurezza:

# Categoria Controlla il titolo Standard e logica
1Autenticazione e accessoAccesso root SSH e autenticazione passwordVerifica di PermitRootLogin no. Se anche l'autenticazione con password è disabilitata, viene riportato l'accesso con sola chiave pubblica; se è abilitata, la verifica è comunque superata purché il login diretto come root sia vietato
2Autenticazione e accessoAccount con password vuotiGarantisce che nessun utente abbia un hash della password vuoto in /etc/shadow
3Autenticazione e accessoSudoers Privilegio IgieneVerifica che in /etc/sudoers e /etc/sudoers.d non ci sia alcuna regola NOPASSWD: ALL, che concede tutti i privilegi senza password
4ReteRifiuto predefinito in entrataControlla nftables la policy di consegna in entrata predefinita
5Kernel e integritàSpazi dei nomi degli utenti non privilegiati (Frag Gap)Blocco della via di attacco LPE tramite user.max_user_namespaces=0 (oppure kernel.unprivileged_userns_clone=0)
6Kernel e integritàSupporto BTF eBPFConvalida le informazioni sul tipo BTF del kernel tramite /sys/kernel/btf/vmlinux
7Kernel e integritàeBPF LSM attivoVerifica la presenza di bpf nel parametro di avvio lsm= del kernel
8ReteGate XDP all'avvioCopre con un gate XDP in negazione predefinita la finestra di avvio precedente all'installazione della tabella nftables di Link Guard (sperimentale, solo su attivazione esplicita, non caricato per impostazione predefinita). Verifica la presenza del link pinnato; l'assenza di caricamento non è un errore
9Kernel e integritàeBPF Agente di sicurezza runtimeVerifica che un agente come Falco o Tetragon sia in esecuzione. Finché Falco è in esecuzione, verifica anche che torni l'avviso della regola propria di RoamSwitch (l'heartbeat) e non passa se non torna (indicando gli eventuali file Falco di RoamSwitch mancanti)
10Kernel e integritàCronologia unificata degli incidentiVerifica che la cronologia che unifica gli incidenti di Link Guard, Canary, eBPF Guard e Resource Guard sia in grado di registrare (eventi esistenti o archivio scrivibile) e mostra l'ultimo tempo di contenimento
11ContenitoreControllo del montaggio del socket DockerRileva i montaggi del contenitore di /var/run/docker.sock
12ContenitoreContenitori privilegiati DockerControlli per carichi di lavoro di contenitori --privileged ad alto rischio
13ContenitoreIsolamento del runtime dei container (runc/gVisor/Kata)Verifica il runtime di container predefinito. Un runtime basato su runc (condivide il kernel dell'host) non supera il controllo; gVisor (runsc) o Kata Containers (isolamento con una VM per container) lo supera. Non applicabile se Docker/containerd non viene rilevato
14ContenitoreProtezione dall'aggiramento del firewall da parte di Docker (DOCKER-USER)Verifica che le regole di RoamSwitch siano effettivamente presenti nella catena DOCKER-USER (sempre valutata per prima) o nella sua tabella nftables dedicata alla protezione di Docker, così che le porte pubblicate dei container non aggirino la negazione predefinita del firewall tramite le regole proprie di Docker. Valutato solo se protect_docker_ports è abilitato in server.conf
15Kernel e integritàTest reale di rilevamento ClamAV (EICAR)Due minuti dopo l'avvio e poi ogni 6 ore, il demone analizza un file di test EICAR innocuo dal proprio ambiente (ad esempio all'interno di una sandbox systemd) per confermare che ClamAV lo rilevi davvero. Valutato solo se clamav_enabled è attivo in server.conf
16Kernel e integritàfanotify: eventi persi (overflow della coda)Verifica se il kernel ha scartato eventi di monitoraggio FIM e di esecuzione dei container a causa di un overflow della coda di eventi di fanotify. Le scritture e le esecuzioni di file durante un overflow sfuggono al monitoraggio in tempo reale; se accade spesso, aumenta fs.fanotify.max_queued_events
17Kernel e integritàEsposizione a CVE note del kernelConfronta la versione del kernel in esecuzione con la mappa locale delle CVE note (baseline integrata o aggiornamento con firma verificata). Il controllo in sé non effettua comunicazioni esterne; gli aggiornamenti della mappa vengono scaricati secondo cve_kernel_map_updates_enabled (true per impostazione predefinita, disattivabile). Un risultato non determinabile viene segnalato come non conclusivo, non come un errore
18IntegritàIntegrità del file del percorso critico (FIM)Verifica di integrità SHA-256 di 23 file critici fissi, di tutti i file direttamente in 12 directory e altro
19Kernel e integritàProtezione del processo YamaPrevenzione dello sniffing della memoria: kernel.yama.ptrace_scope deve valere 2 o 3
20ReteInoltro IP disabilitatoVerifica net.ipv4.ip_forward = 0
21ReteProtezione dei cookie TCP SYNVerifica net.ipv4.tcp_syncookies = 1
22ReteFiltraggio del percorso inverso (rp_filter)Scarto dei pacchetti falsificati: net.ipv4.conf.all.rp_filter deve valere 1 (rigoroso) o 2 (permissivo, per configurazioni multihomed)
23Kernel e integritàRestrizione al core dumpPreviene le perdite di memoria del processo tramite fs.suid_dumpable = 0
24OperazioniAggiornamenti automatici di sicurezzaVerifica della configurazione dell'applicazione automatica delle patch tramite unattended-upgrades (Debian / Ubuntu), dnf-automatic (RHEL / Fedora) e simili
25OperazioniSincronizzazione dell'ora NTPControlla la sincronizzazione tramite chrony o systemd-timesyncd
26OperazioniProtezione dalla forza bruta SSHSuperato finché la guardia SSH propria di RoamSwitch (ssh_guard) è attiva e banna, oppure verifica il monitoraggio attivo tramite fail2ban o crowdsec
27Kernel e integritàeBPF non privilegiato disabilitatoPrevenzione di Spectre/LPE: kernel.unprivileged_bpf_disabled deve valere 1 o 2
28ReteReindirizzamenti ICMP ignoratiPreviene l'avvelenamento del percorso tramite accept_redirects = 0
29ReteTrasmissioni eco ICMP ignorateMitiga gli attacchi DoS dell'amplificatore Smurf
30Kernel e integritàRafforzamento della memoria condivisaVerifica che /dev/shm sia montato con almeno una delle opzioni nodev, nosuid o noexec
31Autenticazione e accessoProtocolli legacy non sicuri assentiVerifica che non siano installati servizi in chiaro in ascolto (telnetd, rshd, tftpd, …) né client in chiaro (telnet, rsh, tftp, …) (servizi e client sono segnalati separatamente).
32Kernel e integritàDump core del processo SUID limitatiPreviene la perdita di credenziali in caso di arresti anomali del binario SUID
33Kernel e integritàProgrammi ausiliari (esche e raccolta di prove) installatiIl demone avvia per nome il programma delle esche di credenziali (honeytoken) e quello dei pacchetti di prove, e non fa nulla, in silenzio, se ne manca uno. Questo controllo verifica che entrambi siano installati.
34ReteFile sensibili esposti sul server webIndividua le radici dei documenti dalla configurazione di Apache / nginx / Caddy e verifica, con richieste al server stesso, se i file .git, .env, .DS_Store, phpinfo, dump SQL, backup e chiavi private presenti sono realmente leggibili tramite il server web. Un file è considerato esposto solo se la risposta corrisponde all'inizio del file su disco: un sito che risponde 200 a ogni percorso, o un file già negato con 403, non genera quindi falsi allarmi. Il contenuto dei file non viene mai riportato, solo i percorsi. Non applicabile senza server web; se non è possibile verificare, il risultato è «non determinabile» e il punteggio non viene ridotto.

Ciascuno dei 34 elementi viene etichettato, ove applicabile, con un identificatore di funzione/categoria NIST CSF 2.0 (ad es. PR.DS-01, DE.CM-09) e, solo quando esiste una corrispondenza affidabile, anche con un numero di controllo CIS Controls v8 (ad es. 10.1, 4.1). Gli elementi ambigui non vengono forzati in una corrispondenza, ma restano non mappati. Questa mappatura è inclusa sia nell'output della CLI (roamswitch server status) sia in quello di MCP (get_security_report), consentendo agli operatori e ai revisori che necessitano di copertura dei framework di conformità di associare meccanicamente i controlli di RoamSwitch ai framework esistenti.

§6Autoverifica (verifica riproducibile in un ambiente Docker)

Sul pacchetto roamswitch-server installato dal repository APT ufficiale viene eseguita un'autoverifica che simula attacchi interni ed esterni, con container Docker separati nel ruolo di attaccante esterno. La misurazione del 2026-09-07 sulla v1.3.2 ha dato 18 voci su 18 PASS. Il 2026-09-25 la suite è stata rieseguita su un pacchetto compilato dai sorgenti della serie 1.10 (arm64, Docker Desktop) e 19 controlli sono stati superati. La nuova esecuzione ha rivelato sei difetti del prodotto, tutti corretti (inclusi nella 1.10.5): il pacchetto non includeva alcuni eseguibili ausiliari, il FIM segnalava gli honeytoken come file nuovi, una conferma (ack) dell'operatore veniva ignorata, durante l'isolamento le risposte del canale SSH di amministrazione venivano scartate lasciando l'amministratore senza accesso, la porta 22 restava aperta anche con preserve_ssh_on_isolation=false e la sandbox dell'unità systemd bloccava la scrittura delle esche. Puoi riprodurre questa verifica anche sulla tua macchina. Per la procedura e gli script vedi «Come riprodurla in autonomia» più sotto.

# Scenario Vettore di attacco Esito
PENT-S1 Nmap SYN Stealth Scan Sonda attraverso le porte 22, 80, 443, 3306, 8080 Il Default Drop di nftables scarta senza alcuna risposta (Filtered) il traffico verso le porte non consentite come 3306 (MySQL) e 8080. Bloccato al 100%
PENT-S2 Sondaggio porta non consentito curl connessioni dirette alle porte 3306 e 8080 Con il rifiuto predefinito in ingresso risponde normalmente solo la porta consentita 80; le altre vanno in timeout e vengono bloccate
PENT-S3 Elenco di blocco Egress / C2 Tentativo di connessione in uscita verso un indirizzo registrato nel feed di IP dannosi egress_ip_blocklist_enabled (true per impostazione predefinita, nftables) blocca solo gli indirizzi registrati. Il traffico verso host non registrati non è interessato (non è un blocco generalizzato)
PENT-S4 Attacco di manomissione FIM Inietta il payload backdoor in /bin/login Il FIM Critical Path SHA-256 rileva immediatamente la manomissione. Dopo il ripristino legittimo la baseline si risincronizza correttamente
PENT-S5 Contenimento basato sulla gravità tramite guard.yaml Iniezione di un evento Falco Critical che fa riferimento a un processo reale Con on_critical: isolate_process + kill_process, il processo interessato viene isolato dalla rete e poi terminato con SIGKILL
PENT-S6 Meccanismo di sicurezza per i processi protetti Iniezione di un evento dello stesso tipo che fa riferimento al PID reale di sshd Grazie all'elenco dei processi protetti (sshd / systemd / runtime dei container, ecc.), sshd continua a funzionare. Viene registrato solo un rifiuto, come falso positivo
PENT-S7 Timer di sicurezza Incidente Air-Gap lasciato senza conferma (nessun ACK dell'operatore) Con safety_timer_secs, in assenza di risposta l'host si ripristina automaticamente. Una volta ricevuto l'ACK, l'isolamento prosegue fino a emergency-restore
PENT-S8 Gap frammento LPE kernel Esecuzione di unshare -U -r come utente senza privilegi (verificata su una VM reale) unshare -U -r, consentito senza il prodotto, viene rifiutato dopo l'avvio. user.max_user_namespaces e kernel.unprivileged_userns_clone valgono 0, il ptrace_scope di Yama vale 2 e l'eBPF non privilegiato è disabilitato (2026-09-25, VM Ubuntu 24.04, verificato sia con la versione pubblica v1.10.4 sia con quella corretta)

Nota: la misura di PENT-S7 risale alla v1.3.2. Nella versione attuale un isolamento causato da un trigger ad alta confidenza viene rilasciato quando la causa risulta verificata come scomparsa, e un isolamento confermato (ack) dall'operatore resta attivo finché l'operatore non lo rilascia (un ack ignorato è stato trovato e corretto nella nuova esecuzione del 2026-09-25). I trigger a bassa confidenza si ripristinano con lo stesso timer. Vedi §4.

Tipo di verifiche — PENT-S8 conferma che una funzione del kernel (divieto degli spazi dei nomi utente) è attiva. PENT-S1 e S2 confermano le regole nftables installate da RoamSwitch; è il kernel a scartare davvero i pacchetti. I punti che esercitano la logica propria di RoamSwitch sono PENT-S3 (applicazione del feed), S4 (FIM), S5 e S6 (contenimento e processi protetti) e S7 (timer di sicurezza).

Prevenzione del blocco SSH

preserve_ssh_on_isolation=true (predefinito) è progettato per mantenere il canale SSH di amministrazione durante un Air-Gap (on_emergency). La nuova esecuzione del 2026-09-25 ha però mostrato che, fino alla 1.10.4, le regole di isolamento scartavano le risposte SSH, lasciando l'operatore senza accesso durante l'isolamento; il controllo precedente era riuscito solo dopo il rilascio dell'isolamento. La correzione è nella 1.10.5 ed è stato verificato, con la causa dell'isolamento ancora presente, che l'SSH funziona durante l'isolamento.

Limiti dell'ambito di verifica (una divulgazione onesta)

Cosa non è possibile verificare con questo test

L'irrobustimento del kernel (disabilitazione degli User Namespace non privilegiati, innalzamento del ptrace_scope di Yama) non può essere verificato end-to-end dall'interno di un container. Poiché questi sysctl non sono isolati per namespace a livello di container, le scritture da un container privilegiato (--privileged) non influiscono su una sandbox di test, ma sulla macchina reale che esegue la verifica stessa. Questa verifica conferma quindi solo che la scrittura sysctl degrada in modo controllato (senza crash) contro un file system in sola lettura; confermare che il valore venga effettivamente applicato richiede una macchina reale o una VM, ovvero l'ambiente per cui il prodotto è pensato. Questa conferma è stata eseguita una sola volta, il 2026-09-25, su una VM Ubuntu 24.04 (vedi la riga PENT-S8).

Riproducilo tu stesso

Tutti i risultati sopra riportati possono essere riprodotti al 100% sulla propria macchina, utilizzando esclusivamente il pacchetto di distribuzione ufficiale, tramite la suite di test Docker pubblicata nel repository di supporto ufficiale (roamswitch-support): non è richiesto alcun accesso al codice sorgente né alcuna fiducia nei nostri confronti.

# Recupera e riproduci la suite di verifica (installa automaticamente roamswitch-server dal repository APT ufficiale)
git clone https://github.com/lafine1211/roamswitch-support.git
cd roamswitch-support/test/docker/server
./run.sh

Crea tre container usa e getta (Target, Attacker e un'esca C2) sulla rete bridge predefinita di Docker, verifica automaticamente tutte e 18 le voci e stampa un report. L'output completo dell'esecuzione sulla v1.3.2 è pubblicato su RESULTS-PENTEST-SERVER-2026-09-07.ja.md (inclusi, per trasparenza, i problemi del banco di test stesso individuati e corretti durante la sua costruzione). La registrazione della nuova esecuzione del 2026-09-25 si trova in RESULTS-PENTEST-2026-09-25.ja.md.

§7Operazioni, distribuzione e ciclo di vita

7.1 Strumenti CUI headless e configurazione guidata

Gestione remota completa tramite CLI:

  • roamswitch server status: produce il report diagnostico di 34 controlli (supporta 10 lingue e segue l'impostazione dell'utente che lo esegue o la locale del sistema operativo).
  • roamswitch server setup: procedura guidata terminale interattiva per porte e notifiche.
  • roamswitch server config show / set <key> <val>: automazione tramite script per Ansible/Terraform.
  • roamswitch server test-notify [all|telegram|line|webhook]: sonda di notifica end-to-end.
  • roamswitch server config set language <ja|en|zh-Hans|zh-Hant|ko|de|fr|es|it|pt|system>: la lingua del report diagnostico periodico del demone (ogni ora) e delle notifiche push (Telegram / LINE / webhook, compreso il messaggio di prova), una lingua per host. system segue la locale del sistema operativo e una locale non riconoscibile diventa ja. La lingua di visualizzazione di roamswitch server status non segue questa impostazione, ma quella dell'utente che lo esegue o la locale del sistema operativo.
  • roamswitch server bans [--json] / sudo roamswitch server unban <IP|/64 IPv6|--all>: stato della guardia contro la forza bruta SSH, elenco e rimozione dei suoi ban. Lo strumento MCP get_ssh_guard_incidents è in sola lettura e non può rimuovere un ban.

TUI Server (roamswitch server tui)

Un'interfaccia da terminale che consente di gestire l'intera edizione Server via SSH, in 80x24 e con la sola tastiera (roamswitch server tui oppure roamswitch-server-tui). Ha 11 schermate: dashboard, protezione, incidenti, notifiche e inoltro, Sensor, configurazione, servizio, strumenti, audit dei log, informazioni e guida, e Fleet. L'editor di configurazione è generato dalle definizioni delle chiavi (lo schema) di server.conf e guard.yaml: convalida tipo, intervallo e valori enumerati, indica se serve un riavvio, mostra un diff prima del salvataggio e salva con backup con marca temporale e scrittura atomica (i valori segreti come i token sono mascherati e inseriti senza eco). Le operazioni ad alto impatto, come ripristinare un isolamento, fare ACK, aggiornare la baseline FIM o fermare il servizio, mostrano una schermata di conferma che indica l'effetto, e quelle di impatto maggiore richiedono di digitare yes (conferma digitata). Senza root è in sola lettura e indica il comando sudo roamswitch ... necessario. Non esiste il ricaricamento della configurazione; per applicare le modifiche serve riavviare il servizio. L'interfaccia supporta 10 lingue.

7.2 Imballaggio

Viene fornito tramite pacchetti .deb per Debian / Ubuntu e pacchetti .rpm per RHEL / Fedora e per openSUSE.

§8Conclusione

RoamSwitch Server Edition offre alle infrastrutture Linux cloud ed enterprise, in un unico pacchetto integrato, «Zero Telemetry (l'unica comunicazione esterna predefinita è una ricezione di dati firmati, senza invio, che si può disattivare)», «una solida protezione perimetrale tramite default-deny in ingresso», «il rilevamento immediato delle manomissioni dei percorsi critici» e «il blocco preventivo dell'escalation di privilegi del kernel».

Una suite di sicurezza per server Linux che combina rilevamento a più livelli e contenimento autonomo (Air-Gap) nelle principali fasi mirate dagli attaccanti: ricognizione iniziale, intrusione, escalation dei privilegi e persistenza.

Questo documento riflette l'implementazione di RoamSwitch Server Edition v1.11.1 (default-deny in ingresso, FIM dei percorsi critici, motore di policy guard.yaml, hardening LPE del kernel, monitoraggio runtime eBPF, rilascio graduale dell'Air-Gap, registratore delle esecuzioni e inoltro degli eventi, TUI Server, rilevamento di tunneling DNS, honeytoken, acquisizione di prove forensi, mappatura NIST CSF / CIS Controls e aggiornamenti della mappa CVE del kernel firmata). Le misurazioni delle autoverifiche del §6 provengono tuttavia dalla v1.3.2 (2026-09-07) e da un pacchetto compilato dai sorgenti della serie 1.10 (2026-09-25). Per la cronologia dettagliata delle modifiche consulta il CHANGELOG di roamswitch-support. Per le ultime informazioni vedi roamswitch.com/linux.