Lafine Systems Design · Documento tecnico

RoamSwitch per Linux
Libro bianco su architettura e sicurezza

Un documento tecnico che spiega cosa fa RoamSwitch per Linux al limite dei privilegi. Evita il linguaggio del marketing; le affermazioni possono essere verificate rispetto al comportamento effettivo del pacchetto distribuito e all'autocontrollo nell'appendice.

Versione v1.11.1 Ambito RoamSwitch for Linux 1.11.1 (hardening del kernel contro gli LPE / protezione runtime eBPF / blocco SIGSTOP dei processi attaccanti / ottimizzazione a basso overhead di Falco / rilevamento di tunneling DNS / acquisizione forense delle prove / honeytoken / tracciamento del pass_age nella verifica delle vulnerabilità / mappatura NIST CSF/CIS Controls / rivalutazione della diagnosi / audit della configurazione MCP) Requisiti systemd + nftables Pubblicato 2026-10-08

§1

RoamSwitch per Linux è un'applicazione residente di sicurezza di rete e diagnostica di sistema che porta il modello di difesa della rete zero-trust di macOS RoamSwitch su Linux (ambienti systemd + nftables).

Questa edizione è destinata alle macchine client (workstation, laptop, computer a scheda singola) e presuppone un comportamento autonomo che segue la macchina mentre si sposta tra le reti. Un'edizione con server rinforzato/infrastruttura headless (RoamSwitch Server Edition: sempre attivo, con negazione predefinita, integrità FIM con percorso critico, webhook remoti e applicazione di policy statiche) viene fornita come build di server dedicato.

Identifica la rete Wi-Fi/cablata connessa tramite l'indirizzo MAC del gateway e cambia autonomamente il profilo firewall nftables attivo. Oltre a ciò, fornisce tre livelli di difesa man-in-the-middle (MITM) su reti non attendibili: un tunnel VPN + kill-switch (WireGuard o Tailscale, la difesa primaria), un blocco ARP/NDP preventivo e il rilevamento dello spoofing ARP. A questi si aggiungono il rilevamento comportamentale del ransomware (fanotify + entropia di Shannon + canaries), una guardia passiva dei collegamenti che segnala o blocca le connessioni di phishing (NFQUEUE + SNI), una guardia delle anomalie delle porte, la mitigazione BadUSB, contromisure contro l'escalation di privilegi locali del kernel (LPE) (come mitigazione di Frag Gap CVE-2026-53362 è possibile attivare, in modo facoltativo, il rifiuto degli user namespace non privilegiati su reti non attendibili; per impostazione predefinita è disattivato), l'integrazione del sensore di minacce runtime eBPF (Falco / Tetragon) con congelamento dei processi attaccanti in pochi millisecondi (SIGSTOP) e isolamento automatico Air-Gap, una valutazione dello stato della sicurezza composta da 27 elementi e un server MCP (Model Context Protocol) integrato.

Questo whitepaper descrive l'architettura, il modello di minaccia e la progettazione di ciascun meccanismo di difesa e, in particolare, come il principio «elaborazione completamente locale; per impostazione predefinita l'unica cosa che lascia la macchina è una richiesta di dati di rilevamento firmati, che non contiene nulla su di te» sia garantito a livello di codice. Le funzioni opzionali che un operatore deve attivare esplicitamente (come l'inoltro degli eventi) sono eccezioni a questo comportamento predefinito; sono tutte elencate nella tabella seguente e nel §9.4. Evita il linguaggio di marketing; le affermazioni possono essere verificate rispetto al comportamento effettivo del pacchetto distribuito (binario) e all'autocontrollo in appendice. RoamSwitch per Linux è freeware proprietario e il suo codice sorgente non è pubblicato (§11).

Principi di progettazione
PrincipioDettaglio
Telemetria zeroPer impostazione predefinita, il prodotto non invia nulla su di te o sulla tua macchina. Non esistono telemetria, analisi, report di crash, statistiche d'uso, configurazione remota né server di attivazione della licenza. Possono comunicare solo le funzioni opzionali che un operatore attiva esplicitamente, elencate nella riga successiva («Eccezioni al comportamento predefinito») e nel 9.4.
Eccezioni al comportamento predefinito (tutte opzionali)Le funzioni seguenti comunicano con l'esterno solo se un operatore le attiva esplicitamente. Le destinazioni sono soltanto quelle configurate dall'operatore; non esistono destinazioni integrate. (1) Inoltro degli eventi (forward_enabled, disattivato per impostazione predefinita): invia gli eventi di rilevamento a un syslog / file JSON Lines / webhook firmato indicato dall'operatore. (2) Fleet (livello Business, solo codice lato dispositivo): un dispositivo non registrato non invia nulla e non risolve nemmeno il nome host di Fleet; il servizio di gestione non è distribuito né rilasciato. Tutto il resto, comprese le verifiche di aggiornamento avviate dall'utente, è elencato nel 9.4.
Ricevi solo aggiornamentiL'unica comunicazione in uscita che il prodotto effettua per impostazione predefinita è un GET giornaliero di un manifesto firmato a URL fisso (lafine.net/updates/v1). Se ClamAV è installato, a ogni esecuzione viene avviato anche lo strumento di aggiornamento di ClamAV, freshclam, ma il suo traffico appartiene a ClamAV ed è elencato separatamente nel §9.4. Dal manifesto si ottengono l'elenco dei siti truffa per la guardia dei collegamenti, l'ultima versione dell'applicazione, le mappe CVE dei pacchetti del kernel e del sistema operativo (Debian / Ubuntu / Arch / famiglia RHEL / SUSE) e dei pacchetti dei linguaggi (npm / PyPI / crates.io / RubyGems / Packagist / Go / Maven), la mappa CVE della scansione dimostrativa delle vulnerabilità e l'elenco dei nomi di pacchetti npm popolari usato per il rilevamento del typosquatting. Ogni giorno vengono scaricati solo il manifesto e la sua firma (due GET). L'elenco della guardia dei collegamenti, la mappa CVE della scansione dimostrativa e l'elenco npm vengono scaricati solo quando il manifesto indica una versione più recente; le mappe CVE del kernel e dei pacchetti solo quando sono passati circa 30 giorni dall'ultimo aggiornamento. La mappa CVE di Ubuntu viene scaricata solo dai dispositivi con una versione di Ubuntu supportata; un dispositivo che non ce l'ha ancora la scarica alla successiva esecuzione giornaliera, senza attendere i 30 giorni. Non contiene stringa di query né cookie, l'unica intestazione che nomina il software è uno User-Agent: RoamSwitch-Updater fisso, e non include alcuna informazione su di te. updates.enabled = false la disattiva del tutto; il prodotto funziona allora con i soli dati inclusi e, finché non si attiva una funzione opzionale, non ha alcun contatto esterno.
Elaborazione completamente localeOgni valutazione, rilevamento e controllo viene completato sul computer locale. L'analisi della sicurezza degli URL e la scansione segreta valutano euristicamente il loro input senza trasmetterlo da nessuna parte.
Privilegio minimoLe operazioni privilegiate sono confinate a un demone root; UI, CLI e server MCP le richiedono tramite IPC su socket di dominio Unix. Per impostazione predefinita il demone non apre alcun socket TCP/UDP (fanno eccezione le funzioni opzionali attivate da un operatore, §9.4); la guardia dei link usa solo il canale di verdetto NFQUEUE su AF_NETLINK (kernel ↔ spazio utente, nessun pacchetto emesso). Le chiamate IPC che modificano lo stato sono accettate solo da root o da un UID con una sessione di accesso locale (dalla 1.10.4; le chiamate di sola lettura restano invariate).
Fail chiusoAir-Gap (isolamento di emergenza) sopravvive al riavvio del demone e viene ripristinato in pochi secondi se le sue regole vengono eliminate esternamente.
Licenza offlineLe licenze Pro/Business vengono verificate esclusivamente tramite il controllo della firma Ed25519. Non si verifica alcuna chiamata a un server di attivazione.

§2

RoamSwitch per Linux è destinato principalmente a laptop/workstation mobile che effettua il roaming tra reti non attendibili.

Aggressori nel raggio d'azione

  • Un utente malintenzionato sulla stessa LAN: scansione delle porte su Wi-Fi condiviso, spoofing ARP/NDP (inclusi annunci di router non autorizzati) per un man-in-the-middle, DHCP non autorizzato, sniffing passivo dopo inondazione del MAC, accesso non autorizzato a un server di sviluppo o API LLM locale accidentalmente associato a 0.0.0.0.
  • Dispositivi USB dannosi (BadUSB) — impersonano una tastiera e immettono sequenze di tasti prima che lo schermo si blocchi; dispositivi di archiviazione di massa USB non registrati per l'esfiltrazione di dati o per il trasporto di malware.
  • Ransomware / malware senza file: entra tramite un file scaricato e crittografa in blocco i documenti dell'utente.
  • C2 e phishing basati su DNS: malware che utilizza la risoluzione dei nomi per raggiungere un server C2 o un sito di phishing.

Fuori portata

  • Un utente malintenzionato che possiede già il root; rootkit a livello di kernel o firmware.
  • Attacchi che rimuovono fisicamente il disco (il dominio della crittografia dell'intero disco; RoamSwitch diagnostica se la crittografia è presente ma non crittografa).
  • Controllo della condivisione wireless di prossimità in stile AirDrop (nessun meccanismo nativo del sistema operativo su Linux; non supportato).

§3

RoamSwitch per Linux è implementato in Rust e il pacchetto contiene 8 file eseguibili (roamswitch-daemon, roamswitch-app, roamswitch (CLI), roamswitch-mcp, roamswitch-updater, roamswitch-honeytokens, roamswitch-incident-capture e roamswitch-xdp-gate, sperimentale e disattivato per impostazione predefinita). La logica comune si trova nella libreria roamswitch-core, mentre roamswitchkit è l'SDK client. Tutte le comunicazioni con il demone passano attraverso /run/roamswitch/roamswitch.sock (un socket di dominio Unix). Non esiste alcun socket TCP/UDP in ascolto nel demone, nell'app o nel server MCP. L'unica cosa che l'app binda è un singolo socket Unix per impedire una seconda istanza.

# Spazio utente (non privilegiato)
roamswitch-app      Tray GTK3 + AppIndicator
roamswitch (CLI)    Frontend shell leggero
roamswitch-mcp      stdio JSON-RPC (avviato dai client IA)
      │
      │  AF_UNIX  /run/roamswitch/roamswitch.sock  (solo IPC locale)
      ▼
roamswitch-daemon   root / systemd Type=notify + WatchdogSec=30
  ・Controllo dei profili nftables (tabella inet roamswitch)
  ・Ciclo sentinel (rilevamento di rete e applicazione dei profili)
  ・Tunnel VPN + kill-switch (WireGuard: wg-quick / roamswitch_vpn, Tailscale: roamswitch_ts)
  ・Blocco preventivo ARP/NDP (fissaggio della tabella neigh) / rilevamento
  ・Guardia anomalie porte (roamswitch_dev_guard)
  ・Guardia fanotify (ransomware / malware)
  ・Guardia link (NFQUEUE — inet roamswitch_linkguard)
  ・Motore canary / Vault di quarantena
  ・Contromisure LPE del kernel (rifiuto degli userns non privilegiati facoltativo, disattivato per impostazione predefinita)
  ・Integrazione runtime eBPF (/var/run/roamswitch/events.sock)
  ・Blocco SIGSTOP dei processi attaccanti in millisecondi e collegamento con Air-Gap
  ・Monitoraggio USB (evdev EVIOCGRAB — BadUSB) / coda di approvazione
  ・Verifica della licenza Ed25519

roamswitch-updater  Un helper systemd-timer autonomo. L'unico crate
                    con uno stack HTTP/TLS (GET giornaliero di un manifest firmato)
roamswitch-honeytokens          Processo ausiliario avviato dal demone (posa e monitoraggio delle credenziali esca)
roamswitch-incident-capture     Processo ausiliario avviato dal demone (raccolta del pacchetto di prove durante il contenimento)
roamswitch-core     Libreria di logica pura (condivisa da tutti i crate)
roamswitchkit       SDK client MCP
Separazione dei privilegi
ComponentePrivilegioRuolo
roamswitch-daemonradice (systemd, Type=notify + WatchdogSec=30)Tutte operazioni privilegiate. In qualità di scrittore singolo, controlla nftables, DNS e unità systemd.
roamswitch-apputente di accessoGUI GTK3 + vassoio AppIndicator. Solo modifica e visualizzazione della configurazione; ogni azione privilegiata passa attraverso IPC.
roamswitch (CLI)utente di accessoFront-end a guscio sottile che utilizza lo stesso socket IPC.
roamswitch-mcpprocesso generato dal client AIstdio JSON-RPC. Fornisce informazioni diagnostiche di sola lettura.

Tutte le comunicazioni con il demone passano attraverso /run/roamswitch/roamswitch.sock (la directory è 0755, il socket è 0666). Il controllo degli accessi non si basa sui permessi del file ma sulla verifica di chi si connette. Tramite SO_PEERCRED si ottiene l'UID; le connessioni degli account di sistema (UID da 1 a 999 e nobody) vengono rifiutate e le chiamate che modificano lo stato sono limitate a root o a un UID con una sessione di login locale (§1). La configurazione utente viene archiviata come testo semplice JSON in ~/.config/roamswitch/config.json. Il demone, con privilegi di root, scansiona e legge /home/*/.config/roamswitch/config.json (perché funziona con HOME=/root). Legge solo i file che appartengono all'account stesso, che non sono collegamenti simbolici e che altri utenti non possono modificare. La configurazione non lascia mai la macchina; non è prevista la registrazione dell'account né alcuna sincronizzazione cloud.

§4

Il ciclo sentinel (con periodo di 3 secondi) legge il MAC del gateway predefinito da /proc/net/arp e ip neigh e lo confronta con i MAC registrati in config.json. Viene utilizzato il MAC anziché l'SSID perché un SSID è banalmente falsificabile. La notifica sul desktop mostrata quando si cambia profilo indica il motivo del cambio («rete non registrata» / «impostazione della rete registrata "XX"» / «nessuna connessione di rete»).

Profili
ProfiloSituazionecomportamento di nftables
aperto (attendibile)Una rete domestica o aziendale registratapolicy accept. Traffico locale consentito.
bilanciatoNon registrato ma configurato come relativamente sicuroRifiuta per impostazione predefinita in entrata. Sono consentiti solo le connessioni stabilite, lo e ICMP / ICMPv6. Monitora l'esposizione delle porte pubbliche.
blocco (protezione assenza)Wi-Fi pubblica/rete non registrataTutti i pacchetti in entrata vengono scartati in modalità stealth. I servizi di condivisione vengono arrestati automaticamente e ripristinati al ritorno su una rete attendibile (attivo per impostazione predefinita, disattivabile in qualsiasi momento).

Tutte le regole sono isolate in una tabella nftables dedicata denominata inet roamswitch e non entrano mai in conflitto con altre configurazioni del firewall (ufw/firewalld). La scadenza dell'override manuale (1 ora/2 ore/4 ore/fino a mezzanotte/fino alla cancellazione/fino alla successiva disconnessione) viene applicata dal demone come singolo scrittore. La GUI non scrive mai una scadenza; rilegge config.json il demone ha riscritto al tick successivo e lo riflette nell'interfaccia utente (evitando un conflitto).

Air-Gap (isolamento della rete di emergenza)

Una modalità di isolamento totale che imposta policy drop (priorità -100) su entrambi gli hook di input e output della tabella inet roamswitch, consentendo solo lo.

  • Fail closed — al riavvio del demone, il rilevamento del file marker (/run/roamswitch/airgap.active) riafferma l'isolamento.
  • Self-healing: se un processo esterno elimina le regole con nft delete table, il ciclo sentinella si riapplica entro pochi secondi.
  • I trigger a bassa confidenza si sbloccano con un timer breve. I trigger a bassa confidenza, come un Air-Gap manuale attivato senza contesto di avviso (il pulsante della GUI o roamswitch airgap), vengono ripristinati completamente con un timer breve (airgap_low_confidence_secs, 600 secondi per impostazione predefinita), così un errore di manovra o un falso positivo non isola a lungo la macchina dalla rete. Anche un'anomalia ARP (fuori da lockdown), un'anomalia di porta e i livelli eBPF fino a Error sono classificati a bassa confidenza dalla macchina a stati comune, ma nel client non attivano mai automaticamente l'Air-Gap: si limitano a una notifica e a una registrazione, oppure al blocco della porta interessata.
  • I trigger ad alta confidenza non ripristinano mai in silenzio. La manomissione di un canary, un evento eBPF Emergency/Alert (un evento Critical solo quando non è stato possibile contenere il singolo processo), lo spoofing ARP che coinvolge il gateway durante il lockdown e un Air-Gap attivato in risposta a un avviso ARP (e, solo con opt-in, una regola ad alta gravità del registratore delle esecuzioni; §5) non vengono rimossi automaticamente, per quanto tempo passi. Dopo il timer breve, a ogni ciclo si ricontrolla se la causa persiste (PID congelato ancora attivo, canary riscritto, anomalia ARP che continua, ecc.) e si rimuove l'isolamento, con notifica, solo quando la scomparsa della causa è verificata. Una verifica non conclusiva, compresa quella che non consente di decidere, vale come «non risolto». Un'esplosione di entropia da ransomware attiva anche l'Air-Gap, oltre a congelare il processo interessato con SIGSTOP e a inviare una notifica, ed è trattata allo stesso modo come trigger ad alta confidenza.
  • Modalità DEGRADED — se un isolamento ad alta confidenza raggiunge il limite (airgap_hard_cap_secs, 3600 secondi per impostazione predefinita; 0 = nessun limite) senza rimozione da parte dell'utente e senza causa verificata come scomparsa, passa a DEGRADED. Nel client, DEGRADED è rigido quanto FULL: tutto tranne il loopback viene scartato, comprese le connessioni stabilite; la differenza è che non si riapre mai per timer. Si rimuove tramite l'utente locale (pulsante della GUI o roamswitch emergency-restore) o con una riverifica che accerti la scomparsa della causa. Air-Gap blocca per impostazione predefinita anche la radio Wi-Fi con rfkill (airgapAutoRfkill); DEGRADED la mantiene bloccata a meno che airgap_degraded_lift_rfkill sia true.
  • Transizioni registrate e notificate — il passaggio da FULL a DEGRADED, ogni tipo di rilascio automatico e un rilascio da parte dell'utente vengono scritti nella cronologia degli incidenti e generano una notifica desktop. Lo stato dell'isolamento è salvato in /run/roamswitch/airgap_isolation.json e sopravvive al riavvio del demone (un riavvio azzera il conteggio delle verifiche ARP consecutive, il che pende verso il mantenimento dell'isolamento: fail-closed).
table inet roamswitch {
    chain input {
        type filter hook input priority -100; policy <accept|drop>;
        iif "lo" accept
        ct state established,related accept
        # open ha policy accept e nessuna regola. balanced / lockdown hanno policy drop (ICMP e ICMPv6 sono consentiti solo in balanced)
    }
    chain output {
        type filter hook output priority -100; policy <accept|drop>;
        # Durante l'Air-Gap (isolamento) la stessa tabella viene ricreata. input / output hanno policy drop, è consentito solo lo (nemmeno le connessioni stabilite passano)
    }
}

Protezione passiva dei collegamenti (rilevamento delle connessioni phishing)

Il demone controlla dove stanno andando le connessioni in uscita e segnala (o, per il ristretto gruppo "chiaramente di phishing", blocca) le connessioni a host pericolosi. Sostituisce il vecchio controllo Incolla un URL, che richiedeva all'utente di agire.

  • Interception: una tabella nftables dedicata inet roamswitch_linkguard aggancia il percorso output e accoda le domande DNS e le connessioni web nuove/precoci a un lavoratore dello spazio utente tramite NFQUEUE. NFQUEUE è un canale di verdetto kernel↔userspace su AF_NETLINK; il demone non associa alcun socket e non genera alcun pacchetto. Le regole della coda trasportano bypass, quindi se il lavoratore non sta correndo, il traffico passa senza ostacoli (Air-Gap rimane l'hard stop separato).
  • Hostname source: il nome della domanda DNS, TLS ClientHello SNI (quindi un browser che utilizza DoH è ancora coperto dal nome host) e l'intestazione HTTP Host in testo normale. Encrypted ClientHello (ECH), una volta ampiamente distribuito, è un noto punto cieco.
  • Verdict (roamswitch-core, logica pura): un host viene controllato rispetto al feed locale di phishing/minacce e a una serie di euristiche offline: IDN/omografo confondibile di un marchio protetto, token del marchio in un dominio non ufficiale, host IP non elaborato, TLD ad alto rischio.
Modalità (linkGuard.mode)Colpo del feed o omografo del marchioAltri segnali sospetti
offnon ispezionatonon ispezionato
warnconnessione mantenuta fino a 8 s + una richiesta di approvazione consenti/bloccanessuna notifica, nessun blocco
block (predefinito)connessione interrotta + una finestra di dialogo di autorizzazione (una volta per host)nessuna notifica, nessun blocco

Solo un riscontro nel feed o un host omografo di un marchio vengono bloccati in modo definitivo (hard-block); tutto ciò che viene individuato solo dall'euristica resta un verdetto di semplice avvertimento e non genera né blocco né notifica. La modalità warn trattiene per un massimo di 8 secondi la connessione a un host pericoloso mentre lo chiede all'utente; se il blocco scade senza risposta è fail-closed (i pacchetti vengono scartati e il verdetto non viene memorizzato nella cache). Una mancata risposta alla richiesta di conferma non viene considerata un «consenso»: al tentativo successivo viene chiesto di nuovo. Anche la modalità block mostra una sola volta per host una notifica e una finestra di dialogo di autorizzazione (un browser che apre molte connessioni allo stesso host non inonda di notifiche). Nella finestra di dialogo di approvazione per un bersaglio pericoloso il pulsante predefinito è «Continua a bloccare», mentre «Consenti» ha una colorazione discreta. Se nella finestra di dialogo si sceglie «Consenti», un host bloccato viene aggiunto in modo permanente a linkGuard.allowlist e passa finché non viene rimosso dalle impostazioni.

Il DNS viene ispezionato, mai contraffatto: una domanda DNS presente nella blocklist produce solo una notifica; l'applicazione avviene alla successiva connessione TCP, quindi RoamSwitch non inserisce mai una risposta DNS sintetica. Il feed è un elenco compilato di domini di phishing/truffa, fornito nel pacchetto e aggiornato dal programma di aggiornamento quotidiano (§11). Senza feed, l'euristica offline continua a funzionare. La firma Ed25519 e lo SHA-256 del feed vengono verificati dal programma di aggiornamento quotidiano prima dell'installazione, e i dati che non corrispondono non vengono adottati. La guardia dei collegamenti carica il feed installato (in sua assenza quello fornito nel pacchetto); se non ce n'è nessuno dei due, funziona in modalità «solo euristica» (mai «consenti tutto»).

Tunnel VPN + kill switch (la difesa MITM primaria)

Protezione anti-MITM che non dipende dall'integrità del livello 2. Su una rete non attendibile, un tunnel crittografato sempre attivo verso un endpoint attendibile rende irrilevanti lo spoofing ARP/NDP locale, il DHCP non autorizzato e lo sniffing passivo: l'aggressore vede solo il testo cifrato. Il blocco ARP/NDP e il rilevamento di seguito sono secondari rispetto a questa funzionalità.

Due backend tra cui scegliere: "WireGuard (file di configurazione)" o "Tailscale (nodo di uscita)". RoamSwitch non implementa alcuna crittografia propria e non è un provider VPN. Entrambi i backend sono dipendenze opzionali; se assente la GUI mostra il comando di installazione.

(A) Backend WireGuard

  • Non viene fornito alcun server VPN: utilizza una configurazione WireGuard fornita dall'utente: un .conf da un provider come Mullvad, IVPN o Proton VPN o il server WireGuard dell'utente (un VPS o un server domestico). Non viene fornita alcuna configurazione predefinita.
  • Config: il demone convalida .conf e lo memorizza in /etc/wireguard/roamswitch.conf (0600, interfaccia roamswitch). Non può vivere altrove: il profilo AppArmor wg-quick su Ubuntu 24.04+ / Debian 13 nega a wg-quick di leggere un .conf all'esterno di /etc/wireguard/ — anche come root. Una configurazione a tunnel diviso (AllowedIPs non 0.0.0.0/0 / ::/0) è contrassegnata come "non tutto il traffico è protetto".
  • Kill-switch — una tabella nftables dedicata inet roamswitch_vpn (policy drop, entrambi gli hook) rilascia tutto tranne il loopback, l'interfaccia tunnel (roamswitch, abbinata a oifname), UDP all'IP dell'endpoint bloccato, DHCP e ICMP. Viene installato prima che il tunnel venga attivato (l'endpoint viene prima risolto in IP fissi tramite getent ahosts), quindi non vi è alcuna finestra di perdita. Se wg-quick up fallisce, il kill switch rimane alzato.

(B) Backend Tailscale

  • Per gli utenti che già utilizzano Tailscale. RoamSwitch not esegue tailscale up / login / install tailscaled — legge tailscale status --json ed esegue tailscale set --exit-node=<node> / --exit-node= (cancella).
  • È richiesto un nodo di uscita: la protezione anti-MITM vale solo con un nodo di uscita che instrada tutto il traffico. Senza alcuna configurazione, la protezione non è armata e l'interfaccia utente avverte "non protetto". Se il nodo scelto va offline, la protezione viene disattivata automaticamente e l'utente viene avvisato (instradare tutto attraverso un nodo morto manderebbe in blackhole la connessione).
  • Kill-switch (inet roamswitch_ts, policy drop) — Il trasporto di Tailscale non può essere bloccato su un singolo IP endpoint (relè DERP + peer roaming), quindi questo è più flessibile rispetto a quello WireGuard. Permette: loopback, l'interfaccia tunnel (tailscale0), gli intervalli CGNAT 100.64.0.0/10 e fd7a:115c:a1e0::/48, STUN (udp/3478), DERP (tcp/443), udp/41641, DHCP, ICMP, MagicDNS (100.100.100.100:53), connessioni stabilite. Tutto il resto (UDP arbitrario, DNS su un risolutore locale, SMB, mDNS, HTTP in testo normale, TCP arbitrario) viene eliminato.
  • Avvertenze sul kill-switch — (1) elenco di consentimenti più ampio rispetto al backend WireGuard (udp/3478, tcp/443, l'intero CGNAT /10). (2) Un utente malintenzionato sul percorso vede ancora i metadati (che Tailscale è in uso, la regione DERP, i tempi) e può rilasciare/ritardare, ma non può leggere il carico utile nel tunnel. (3) Quando l'UDP diretto viene bloccato, la riservatezza si basa sul DERP TLS con certificato bloccato di Tailscale. (4) Con MagicDNS disattivato + un risolutore locale, il DNS si interrompe mentre è armato (avviso UI). (5) È necessario effettuare l'accesso ai portali captive prima dell'abilitazione.

Comune: si attiva solo quando vpn_on_untrusted_enabled è attivo e il livello è lockdown (protezione fuori sede). In open e balanced non viene creato alcun tunnel e, con Tailscale, l'Exit Node viene cancellato e il kill switch rimosso (mai tailscale down). Disattivato per impostazione predefinita, con attivazione opt-in. La destinazione del traffico in uscita, sia con WireGuard sia con Tailscale, è l'endpoint VPN/tailnet dell'utente e non è correlata all'infrastruttura di lafine (§9).

Blocco preventivo ARP/NDP

Nel momento in cui si accede a una rete non attendibile, gli indirizzi MAC dell'infrastruttura on-link che un utente malintenzionato MITM dovrebbe impersonare vengono inseriti nella tabella dei vicini del kernel come PERMANENT voci (statiche). Una volta bloccato, il kernel ignora le risposte ARP contraffatte/gli annunci dei vicini.

  • Che cosa è pinned — (1) il gateway predefinito IPv4, (2) il router predefinito IPv6 (spesso un indirizzo fe80:: link-local), (3) qualsiasi risolutore DNS on-link (un risolutore pubblico viene raggiunto tramite il gateway e viene ignorato). I vicini IPv6 sono bloccati con ip -6 neigh replace ... nud permanent.
  • Le reti attendibili non vengono mai bloccate, quindi il riavvio del router domestico non può creare un buco nero all'utente. Ricollegato su ogni switch di rete (TOFU); alla successiva riconnessione viene rilevato un failover legittimo. Le voci appuntate vengono registrate in /var/lib/roamswitch/arp_lock.json.
  • Attivo per impostazione predefinita (criteri della Community Edition; stessa ripartizione della build di macOS Pro). Alterna: gateway_arp_lock_enabled.

Rilevamento spoofing ARP (notifica prima)

Monitora /proc/net/arp per rilevare uno stato in cui un solo MAC corrisponde a più IP (MAC duplicato) e una modifica improvvisa sospetta del MAC del gateway. In lockdown: per i rilevamenti che coinvolgono il gateway taglia tutto il traffico (Air-Gap) e avvisa. Su reti balanced/attendibili: notifica senza interrompere subito; l'utente può attivare l'isolamento di emergenza da RoamSwitch. Ciò (a) impedisce a un malintenzionato di usare come arma un singolo pacchetto ARP contraffatto per provocare un DoS in cui l'utente si mette offline da solo e (b) impedisce a un riavvio del router o al roaming del punto di accesso di attivarlo per errore. Interruttore: arp_spoof_guard_enabled.

Verifica della causa prima di rimuovere un Air-Gap (riverifica ARP)

Un Air-Gap causato da spoofing ARP (rilevato sotto lockdown o attivato in risposta a un avviso ARP) viene rimosso come «causa scomparsa» solo se valgono tutte le condizioni seguenti. Il demone legge la tabella dei vicini del kernel (ip -j neigh show, in alternativa /proc/net/arp) e la route predefinita; il MAC del gateway predefinito deve coincidere con il MAC attendibile registrato all'attivazione (stato REACHABLE, STALE, DELAY, PROBE o PERMANENT, senza altri host IPv4 sullo stesso dispositivo che rivendichino quel MAC), per arp_recheck_consecutive verifiche consecutive (3 per impostazione predefinita) che coprano almeno arp_recheck_min_secs secondi (60 per impostazione predefinita). Solo quando la voce di vicinato manca o è in stato di errore e il collegamento è attivo (carrier presente, operstate up, Wi-Fi non bloccato da rfkill), invia un solo ARP who-has al gateway (a distanza di almeno 20 secondi, tramite AF_PACKET) per verificare (arp_recheck_active_probe, true per impostazione predefinita). Se una qualsiasi risposta riporta un MAC diverso da quello attendibile, è una discrepanza. Discrepanza, voce mancante, MAC duplicato, errore di lettura o assenza di MAC attendibile registrato contano tutti come «non risolto» (fail-closed) e azzerano il conteggio. Limite noto: un attaccante che clona il MAC del vero gateway non è rilevabile da alcun confronto di MAC, né in cache né attivo.

Lo spoofing ARP rilevato fa scattare l'Air-Gap automatico solo quando il gateway è coinvolto. Rientrano in questo caso il cambio del MAC del gateway e l'IP del gateway che condivide un MAC con un altro IP. Se cambia il MAC di un altro host, o se una macchina risponde per più IP con lo stesso MAC (un PC che esegue macchine virtuali, un NAS con indirizzi alias), viene solo segnalato e la rete non viene interrotta. La stessa regola vale per il controllo di rimozione: solo un duplicato che coinvolge il gateway conta come «la causa persiste». Alla correzione della 1.10.7 (2026-09-26), in una LAN reale, un falso ARP del gateway inviato da un'altra macchina ha fatto scattare l'Air-Gap in circa 1 secondo, ed è stato rimosso automaticamente circa 11 minuti dopo la fine dell'attacco (10 minuti predefiniti più 60 secondi di conferma). La rimozione è riuscita anche mentre due IP non correlati continuavano a dichiararsi con lo stesso MAC (prima di questa regola scattava un'interruzione totale che non veniva mai rimossa).

Guardia delle anomalie del porto

Contiene automaticamente una porta di ascolto appena esposta alla LAN esterna (una backdoor, C2 o un server di sviluppo/API LLM locale accidentalmente collegata a 0.0.0.0). Il verdetto è baseline-relative, non "numero di porta pericoloso". All'abilitazione, gli eseguibili attualmente esposti vengono appresi come "conosciuti"; successivamente viene contrassegnato solo un eseguibile esposto appena apparso (identità = percorso dell'eseguibile, snap normalizzati su snap:<name>). I demoni di sistema non rientrano nell'ambito. Il contenimento avviene tramite la tabella inet roamswitch_dev_guard: rilascia TCP su quella porta da qualsiasi cosa tranne lo: la macchina stessa continua a utilizzarla. Dopo il contenimento automatico, viene presentata una notifica desktop immediata e una finestra di dialogo di conferma ("Consenti questa porta" / "Mantieni bloccato"), consentendo l'inserimento permanente nella whitelist con un clic. Dalla schermata di diagnostica della porta è inoltre possibile consentire qualsiasi porta individualmente e ricatturare la linea di base. Attivo per impostazione predefinita. Alterna: port_anomaly_guard_enabled. Portato da macOS.

Rilevamento di tunneling DNS / exfiltrazione

Opera in modo continuo come parte del percorso DNS già utilizzato dalla guardia passiva dei collegamenti (non esiste un interruttore dedicato). Una query DNS viene conteggiata come «di forma sospetta» quando corrisponde a una delle forme caratteristiche degli strumenti di tunneling DNS come iodine o dnscat2 (un'etichetta di oltre 40 byte E un'entropia di Shannon superiore a 3,8, oppure un qtype di record TXT/NULL), e viene inviata una notifica solo quando negli ultimi 60 secondi se ne raggiungono 10 (per una nuova notifica sullo stesso apex domain è previsto un periodo di attesa di 300 secondi; se le notifiche continuano, l'attesa si allunga a 600 secondi, 1200 secondi, fino a un massimo di 1800 secondi, e torna a 300 secondi se per almeno il doppio dell'attesa non ci sono notifiche). 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. Le sole query TXT verso clamav.net non vengono conteggiate, perché freshclam le usa per verificare l'aggiornamento. La regola NFQUEUE sottostante porta il flag bypass, quindi anche se il processo di rilevamento è fermo o non risponde, il traffico DNS continua a passare senza essere influenzato: la risoluzione dei nomi non viene mai bloccata da un malfunzionamento di questa funzione (lo stesso design fail-open della guardia passiva dei collegamenti).

Limite noto: i nomi host lunghi separati da trattini (ad esempio prod-us-east-1-web-server-node-0042) e un unico contatore globale che non distingueva i domini di destinazione per i burst TXT/NULL generavano qui falsi positivi, come verificato in misure reali. Entrambi sono stati risolti il 23/09/2026. La soluzione è una allowlist di entropia configurabile e la separazione della chiave del contatore 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 di CDN/cloud e PaaS comuni (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 LinkGuardCfg.dns_tunnel_entropy_allowlist (nelle impostazioni della guardia dei collegamenti di roamswitch-app c'è un campo dedicato in cui inserirli separati da virgole). L'allowlist esonera solo il controllo combinato lunghezza dell'etichetta più entropia; il rilevamento in base al qtype TXT/NULL si applica sempre, a prescindere dall'allowlist, il che impedisce a un attaccante di mascherare un tunnel sotto un dominio consentito (solo per i TXT, i nomi sotto clamav.net, usati da freshclam per verificare l'aggiornamento, non vengono conteggiati grazie a una piccola lista di esclusione separata). Il contatore dei burst è ora separato 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 non è cambiata; il periodo di attesa per le notifiche ripetute parte da 300 secondi e, se le notifiche continuano, si allunga fino a 1800 secondi). Il traffico malevolo reale di strumenti come iodine o dnscat2 ruota soltanto etichette e qtype sotto un unico dominio controllato dall'attaccante, quindi restringere il conteggio al dominio apex non riduce l'efficacia del rilevamento.

Rilevamento rischi Docker e protezione anti-bypass del firewall

Le porte pubblicate da Docker (-p) presentano un problema noto di bypass del firewall standard dell'host, pertanto vengono inserite permanentemente regole di ispezione nella catena DOCKER-USER, bloccando qualsiasi traffico esterno non esplicitamente consentito (nessuna configurazione richiesta, attivo per impostazione predefinita). Inoltre, docker events viene monitorato in tempo reale per rilevare istantaneamente l'avvio di un container --privileged o il mount di /var/run/docker.sock — un rischio di escape dal container — e avvisarti immediatamente. Poiché questo segnala una configurazione a rischio e non una compromissione confermata, si tratta solo di una notifica, senza blocco automatico.

Copertura — questa ispezione della catena DOCKER-USER copre il backend iptables (IPv4), IPv6 e il backend nftables nativo di Docker, e si ricontrolla e si ripara ogni 60 secondi (anche nell'edizione desktop, dalla 1.10.4; le versioni precedenti coprivano solo il backend iptables (IPv4)).

Scanner per la fuga di segreti e chiavi API

Rileva chiavi API (OpenAI, Anthropic, AWS, GitHub, Slack, Stripe e altre) e chiavi private SSH tramite entropia di Shannon e corrispondenza di pattern. roamswitch audit-secrets <text|file|directory> analizza testo e file, oltre a un'intera directory in modo ricorsivo (escludendo .git, node_modules e simili; i file superiori a 2MB o che sembrano binari vengono saltati). La stessa rilevazione è disponibile anche tramite lo strumento MCP audit_secrets (con text o path) e tramite il client Rust roamswitch-linux-kit dell'SDK pubblico (audit_secrets() per il testo, audit_secrets_path() per un file o una directory). Il contenuto non viene mai trasmesso altrove.

Rilevamento e blocco della forza bruta SSH (solo finché sshd è in esecuzione)

Il daemon della Client Edition esegue lo stesso motore della Server Edition (ssh_guard_enabled, true per impostazione predefinita). Le differenze sono tre. Primo, lavora solo finché sshd è in esecuzione: ogni minuto controlla se qualcosa è in ascolto sulle porte SSH e si ferma dopo tre assenze di fila. Su una macchina senza sshd non segue il log e non tocca alcuna tabella nftables. Secondo, le impostazioni sono le stesse 10 chiavi (ssh_guard_*) con gli stessi valori predefiniti, tenute in config.json e recepite in circa un minuto. Terzo, le notifiche sono notifiche del desktop e una voce nel registro degli incidenti del client; non avvia mai l'Air-Gap. Un'origine i cui fallimenti raggiungono la soglia (di default 6 in 600 secondi) perde le nuove connessioni, e le connessioni SSH che aveva già aperto vengono interrotte nello stesso momento (una sessione autenticata e un'origine esente non vengono mai interrotte). Quando fail2ban (con una jail sshd attiva) o CrowdSec protegge già sshd, si limita a rilevare e notificare. I ban si elencano e si rimuovono con sudo roamswitch bans e sudo roamswitch unban <IP>; non c'è una schermata GTK per questo, solo config.json e i comandi. Ai livelli di protezione predefiniti del client il traffico in ingresso è chiuso, quindi le connessioni potrebbero non raggiungere affatto SSH.

Procedura guidata di configurazione iniziale e interfaccia utente di conferma

Il pacchetto viene spedito /etc/xdg/autostart/roamswitch.desktop, quindi la procedura guidata di configurazione viene avviata al primo accesso dopo l'installazione (successivamente è solo residente nel vassoio). L'abilitazione/disabilitazione di una guardia di sicurezza mostra una finestra di dialogo di conferma per le stesse guardie della build macOS (anomalia porta, contenimento automatico ARP, archivio USB, tastiera USB difettosa, Bluetooth). Le decisioni relative all'ora dell'evento (una tastiera USB sconosciuta, un'unità USB non registrata, una porta sconosciuta bloccata automaticamente, un Air-Gap di emergenza) vengono presentate come finestre di dialogo modali.

§5

Rilevamento di burst di entropia di Shannon · Scansione YARA

Gli eventi di scrittura vengono tracciati per PID. Se un singolo processo crea 20 o più file distinti con entropia ≥ 7,92 entro 5 secondi, viene trattato come crittografia di massa da ransomware e il processo viene congelato con SIGSTOP. Vengono inoltre inviate una notifica sul desktop e una registrazione nella timeline dell'incidente, e viene raccolto un pacchetto di prove. Questo rilevamento attiva anche l'Air-Gap (l'isolamento dell'intera rete), come trigger ad alta confidenza al pari della manomissione di un canary e degli eventi eBPF ad alta gravità.

Misurato su una macchina reale (Ubuntu 24.04, kernel 7.0) con un programma di riproduzione che riscrive 40 file (misura del 2026-09-26, sul demone nella versione inclusa nella 1.10.7, con l'aggiunta del rilevamento della cifratura parziale). Una cifratura completa è stata rilevata dopo il 20° file e il processo è stato congelato al 31°. Viene rilevata anche quando il processo si rinomina python3 o git: la lista consentita ora verifica che anche l'eseguibile e il nome dello script concordino con il nome, non solo il nome (prima tutti i 40 file passavano inosservati). cp -r, rsync -a, tar -x e gli script Python vengono identificati nel momento in cui si vede la scrittura, quindi non generano più un falso rilevamento anche se terminano in fretta (prima erano già usciti al momento della valutazione ed erano segnalati come «processo sconosciuto»). La cifratura parziale di soli 16 byte ogni 32 (la scrittura tipica della famiglia LockBit) non veniva rilevata prima, perché l'entropia media complessiva non raggiunge 7,92, ma dalla 1.10.7 viene rilevata. Il rilevamento riconosce lo schema in cui, ogni 16 byte, si alternano blocchi simili a dati casuali e blocchi simili a testo: ha rilevato il 94% dei file simulati e, su 19.997 file normali, ha dato 2 falsi positivi (0,01%). Un programma di riproduzione che scrive a piena velocità, senza pause tra un file e l'altro, finisce di scrivere i 40 file prima della valutazione del 20° e, quando viene fermato, il processo è già terminato; con una pausa di 0,2 secondi tra i file si ferma al 20°. La cifratura solo dell'inizio, la cifratura con salti di intervalli e il caso in cui il contenuto cifrato viene scritto in Base64 non vengono conteggiati come rilevamento: vengono solo registrati nel log, senza congelamento (c'è il rischio di falsi positivi su file normali). Un ransomware scritto con un vero programma della lista consentita (come python3) non si distingue nemmeno dal nome.

  • Separazione della scansione YARA e del rilevamento dell'entropia: il rilevamento malware YARA viene eseguito sui file scritti, inclusi ~/.cache/, node_modules/ e gli archivi vari, senza esclusioni per estensione (sono esclusi scan_exclusions impostato dall'utente, il Vault di quarantena, /dev/shm e i file in memoria o già eliminati, posizioni come /proc, /sys e /dev, e ciò che scrivono processi di sistema come il demone stesso, ClamAV e la base del desktop; vengono letti i primi 256 KB). Dal rilevamento delle esplosioni di entropia (cifratura) vengono invece esclusi i formati già compressi in origine (.zip, .tgz, .tar.gz, .gz, .png, .jpg, .mp4, .wasm, ecc.), i file la cui testata è ancora un formato legittimo di compressione o di contenuti multimediali (dalla 1.10.25) e il lavoro di cache dei gestori di pacchetti legittimi (npm, npx, cargo, rustc, dpkg, apt, tar, ecc.), per evitare congelamenti SIGSTOP dovuti a falsi positivi durante l'estrazione con strumenti legittimi.
  • Sovrascritture ripetute dello stesso file (ad esempio i tre passaggi di shred) contano come un evento.
  • Protezione dei processi critici: i processi vengono controllati rispetto a un elenco hardcoded di processi protetti (PROTECTED_PROCESS_NAMES: systemd / dockerd / NetworkManager / sshd / gnome-shell, ecc.); quelli il cui nome corrisponde e il cui eseguibile si trova in un'area di sistema (/usr, /opt, ecc.) non vengono mai congelati (si evita così un blocco dell'intera macchina). I programmi in /tmp o /home che imitano solo il nome non sono protetti.

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

Regole dedicate rilevano web shell PHP, JavaScript, JSP e ASP, codice offuscato iniettato, PHP nascosto nelle immagini e il dirottamento dell'esecuzione tramite .htaccess. Sono distinte dal rilevamento integrato di ELF e shell e sono scritte in un piccolo linguaggio di letterali, sequenze di letterali a distanza limitata e due statistiche (una riga estremamente lunga, un enorme blocco Base64). Senza espressioni regolari la valutazione non può esplodere né bloccarsi; anche un file di regole ostile può causare al massimo falsi positivi. Nessuna firma deriva da un campione reale né è legata a una famiglia di malware con nome: le 18 regole descrivono solo tecniche documentate pubblicamente (esecuzione dell'output di un decodificatore, valori della richiesta passati direttamente all'esecuzione, PHP eseguibile nelle directory di upload, PHP dentro un'immagine, associazioni di handler in .htaccess, script iniettati costruiti con unescape, librerie di mining nel browser).

Le regole hanno tre livelli: integrate, installate dall'aggiornatore giornaliero (11.3) dopo la verifica della firma e i tuoi file in /etc/roamswitch/rules.d/*.json (si leggono solo file di proprietà di root che né il gruppo né altri possono scrivere). Le gravità sono critical, high, medium e low; critical e high passano da approvazione o quarantena, medium e inferiori sono solo segnalate. La quarantena automatica è limitata a critical per impostazione predefinita (quarantine in /etc/roamswitch/web_rules.conf); nell'edizione Client un rilevamento in scrittura o esecuzione resta deciso dall'utente nella finestra di approvazione. Tutto ciò che sta sotto tests/, fixtures/ e .git/ viene saltato per impostazione predefinita, perché il codice di test cita gli stessi costrutti.

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.

Canarini (file esca)

I file esca vengono inseriti nelle cartelle Documenti, Desktop, Download e Immagini dell'utente e in una directory dedicata e confrontati con una linea di base SHA-256 su un ciclo di 3 secondi. Qualsiasi ridenominazione, cancellazione, troncamento o modifica del contenuto attiva immediatamente Air-Gap. Poiché il demone viene eseguito come root, enumera /home/* e distribuisce canarini nelle directory di ogni utente reale.

Pacchetti di acquisizione forense delle prove

Solo quando scatta il rilevamento di burst di entropia da ransomware, oppure quando un evento di gravità Critical del sensore di minacce runtime eBPF porta a un isolamento mirato del singolo processo (SIGSTOP + blocco di rete), viene avviato roamswitch-incident-capture, che 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, quando viene passato un PID, uno snapshot /proc/<pid> del processo interessato (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 registrato e il resto dell'acquisizione prosegue. Per gli eventi eBPF viene passato il PID del processo interessato. Nel rilevamento dei burst di entropia il PID viene passato quando è stato possibile identificare il processo che ha scritto; se quel processo era già terminato al momento della valutazione del burst, l'acquisizione parte senza PID e viene raccolta solo la cattura dei pacchetti. L'acquisizione delle prove non viene eseguita quando l'isolamento mirato non è possibile o fallisce e si ricorre all'isolamento Air-Gap dell'intero host, né per l'isolamento dell'intero host dovuto a eventi di livello Emergency/Alert. I pacchetti di distribuzione fino alla 1.10.4 non includevano roamswitch-incident-capture, necessario per questa funzione, che quindi non veniva eseguita negli ambienti installati 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 si esegue esplicitamente roamswitch-incident-capture prune (conservazione predefinita: 30 giorni).

Le prove raccolte non vengono verificate al momento dell'acquisizione. Per controllare in seguito eventuali manomissioni, eseguire 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, ogni riga si appoggia alla precedente, 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; anche modificare o rimuovere una riga della catena, perché la catena si spezza. Non ferma un attaccante con 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.

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 — ~/.aws/credentials, ~/.ssh/id_rsa e ~/.docker/config.json, ~/.pypirc, ~/.env.backup (sotto la home directory dell'utente chiamante) — ciascuno contenente un marcatore ROAMSWITCH_HONEYTOKEN. 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 la distribuzione a cascata di ulteriori esche correlate. 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. 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 ~/.docker/config.json. La lettura da parte di qualsiasi altro programma viene rilevata come prima.

Un processo di monitoraggio in background sorveglia la lettura e l'apertura di ciascuna esca tramite fanotify di sola notifica (con monitoraggio per singolo file di FAN_OPEN/FAN_ACCESS su ogni esca). Negli ambienti in cui fanotify non è disponibile (per esempio un'esecuzione senza privilegi di root) si passa a inotify. Poiché nessun processo legittimo ha motivo di leggere questi file, qualsiasi accesso viene trattato come un forte segnale di ricognizione post-compromissione da parte di un intruso (MITRE ATT&CK T1552); RoamSwitch tenta di identificare il processo che accede e lo registra nella timeline dell'incidente con gravità Critical (non compare alcuna notifica popup sul desktop; se l'inoltro degli eventi è attivo, l'evento viene inoltrato). Non vengono però registrati gli accessi dell'eseguibile di RoamSwitch stesso e quelli dei client OpenSSH (ssh, scp, sftp, ssh-add, ssh-agent) alle esche SSH, per evitare falsi positivi. Gli accessi il cui eseguibile non può essere identificato vengono registrati. La funzione serve solo alla registrazione e non intraprende azioni aggiuntive, come congelare o bloccare il processo che accede.

Limite noto: il contenuto dell'esca ~/.ssh/id_rsa include testo leggibile e il suo corpo resta un formato non valido come corpo base64 di una vera chiave privata OpenSSH. Tuttavia, la maggior parte degli scanner di segreti segnala i file basandosi solo sulla corrispondenza con un'espressione regolare dell'intestazione -----BEGIN ... PRIVATE KEY-----, quindi riteniamo limitato l'impatto reale sul tasso di rilevamento. Inoltre, il processo che accede al file viene normalmente identificato dal PID contenuto nell'evento fanotify; fuser viene richiamato solo quando fanotify non è disponibile e si è passati a inotify, per cui per processi estremamente effimeri che terminano subito dopo la lettura può non essere possibile ottenere PID e nome del processo. 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 registrato).

monitoraggio fanotify · Quarantine Vault · ClamAV

  • Il demone monitora /home, /tmp, /var/tmp e /dev/shm con marcature a livello di file system tramite FAN_CLASS_CONTENT (modalità di permesso; se non è disponibile, FAN_CLASS_NOTIF), dalla 1.10.18 (le marcature a livello di montaggio sono l'alternativa quando quelle a livello di file system non sono accettate), mentre la notifica di scrittura completata viene impostata, sempre a livello di file system, anche su tutto /. Dalla 1.10.4, gli eventi di permesso riguardano solo l'esecuzione (FAN_OPEN_EXEC_PERM) e open è trattato come notifica (fanotify_open_perm ripristina il comportamento precedente). Gli eseguibili già analizzati come puliti vengono messi in cache per 60 secondi e i processi figli del demone sono esclusi dal monitoraggio.
  • Notifica prima, nessuna quarantena senza conferma (guardia fanotify): la guardia fanotify non mette mai in quarantena senza conferma né applica FAN_DENY per un semplice riscontro YARA / ClamAV. Una firma reale solleva una richiesta modale in /run/roamswitch/approvals.json (Quarantena/Consenti/Più tardi); nulla entra nel Vault finché l'utente non conferma. Solo un execve effettivo riceve FAN_DENY e solo quando pre_exec_blocking_enabled è attivo. Alla scadenza dell'approvazione si ha un fail-open. Il monitoraggio dei download (per impostazione predefinita ~/Downloads e ~/Desktop, ampliabile con watched_folders) è un percorso separato: un file rilevato da YARA o ClamAV (esclusa la stringa EICAR) viene spostato subito nel Vault di quarantena, senza conferma, con una notifica.
  • Le firme di test come EICAR vengono solo registrate nella cronologia: la stringa di test standard del settore non genera alcuna notifica, solo una voce nella cronologia delle notifiche, e non viene mai messa in quarantena o bloccata, sia nella protezione fanotify che nella protezione download.
  • Un elenco di percorsi scan_exclusions configurato dall'utente (percorsi assoluti, applicati in modo ricorsivo; sussume l'eredità clamav_exclusions) è rispettato sia dagli scanner YARA che da ClamAV. Sia "Consenti" nel prompt che "Ripristina" nella scheda Quarantena vengono aggiunti, quindi lo stesso file non viene contrassegnato nuovamente.
  • Il malware reale rilevato (nella guardia fanotify, quello confermato dall'utente) viene trasferito in /var/lib/roamswitch/quarantine/, accessibile solo a root (prima si trovava in ~/.local/share/roamswitch/quarantine/ di ciascun utente; dal vecchio percorso la migrazione avviene automaticamente all'avvio). La directory del Vault appartiene a root con 0700; ogni campione appartiene a root con 0400 (sola lettura, non eseguibile). L'utente può operare solo tramite l'IPC del demone e può elencare, ripristinare ed eliminare solo gli elementi che ha messo lui stesso in quarantena. Il ripristino viene scritto come file 0644 da un processo figlio con i privilegi ridotti a quelli dell'utente che lo richiede (un file esistente non viene sovrascritto).
  • Il ciclo degli eventi di permesso non legge i file di configurazione: la configurazione viene letta da un thread in background dedicato in uno snapshot Mutex<GuardConfig>, e sul percorso critico (hot path) l'accesso alla configurazione si limita a bloccare e copiare (la lettura dell'inizio del file che sta per essere eseguito, per giudicarlo, è un'altra cosa). È un'invariante di progettazione: ritardare FAN_OPEN_EXEC_PERM bloccherebbe la macchina.
  • Quando ClamAV è presente (clamav_enabled, attivo per impostazione predefinita), i nuovi file che compaiono nelle cartelle monitorate dal controllo dei download (per impostazione predefinita ~/Downloads e ~/Desktop) vengono analizzati con il database locale, dopo la scansione YARA integrata. I formati eseguibili per Windows, come .exe, generano una notifica; i formati di distribuzione Linux, come .sh, .deb e .elf, vengono solo registrati nella cronologia; i modelli AI pericolosi (.pkl, .pt) vengono notificati con l'indicazione della provenienza. ClamAV è una dipendenza opzionale; le altre difese funzionano anche se non è installato. Se è installato, l'aggiornamento quotidiano (§11) esegue freshclam per mantenere aggiornato il database delle firme.

Registratore delle esecuzioni dei processi (solo locale)

Un registratore strettamente locale che annota gli eventi exec e i cambi di uid tramite il connettore di processo netlink (proc connector). Registra il percorso dell'eseguibile, gli argomenti della riga di comando (limitati a 64 argomenti, 512 caratteri ciascuno e 4096 in totale, con i caratteri di controllo sottoposti a escape), la discendenza dei processi padre, l'uid, la directory di lavoro, l'ID del container, lo SHA-256 dell'eseguibile e altro. È attivo per impostazione predefinita, ma disattivo per impostazione predefinita sulle macchine con meno di 1 GiB di RAM (un'impostazione esplicita di exec_recorder_enabled prevale). Il proprio uso della CPU è limitato al 3% di un core (exec_recorder_cpu_budget_pct) e gira con nice 10. Se il connettore di processo non può essere aperto, ripiega su una scansione poco frequente di /proc (che perde i processi di breve durata).

  • Archiviazione — i record sono salvati in /var/lib/roamswitch/exec_log come JSON Lines suddivisi in segmenti da 8 MiB (0600). La conservazione predefinita è 200 MB / 14 giorni (exec_recorder_retention_mb / exec_recorder_retention_days). Una catena di hash rende la manomissione rilevabile ma non prevenibile (un attaccante che ottiene root può riscrivere l'intera catena). La ricerca si fa con roamswitch events search|tree|export|verify; gli strumenti MCP in sola lettura per i client IA sono search_exec_events e get_process_tree.
  • Regole di correlazione a sola notifica (EXR-001-010): una shell avviata da un servizio web, esecuzione da /tmp, da un file in memoria (memfd) o da un file eliminato, esecuzione subito dopo un download, un one-liner base64/eval, ottenimento di uid 0 senza percorso setuid, esecuzione di un binario collocato subito dopo una modifica di cron/systemd, esecuzione dopo una modifica di /etc/ld.so.preload, l'eliminazione di snapshot locali (btrfs, LVM) da parte di un programma diverso dai noti strumenti di gestione come snapper e timeshift (EXR-008, 1.10.11), la ricerca con which o simili di 8 o più tipi di strumenti di attacco entro 30 secondi da parte della stessa origine di esecuzione (EXR-009, 1.11.0) e l'avvio di 3 o più tipi di strumenti di attacco entro 2 minuti da parte di un interprete di script come python o node (EXR-010, 1.11.0; esclusi i comandi digitati da una persona sotto sshd, login o un terminale): sono 10 regole, e ciascuna riporta un ID di tecnica MITRE ATT&CK. Le regole si limitano a notificare e 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 di Air-Gap ad alta confidenza (§4).
  • Nulla lascia la macchina — il log grezzo non viene mai inoltrato. Solo se l'inoltro degli eventi (§9.4) è attivo vengono inviate le notifiche delle regole (gravità, ID regola, nome del processo, riepilogo) e gli hash di sigillo dei segmenti, e solo verso la destinazione configurata dall'operatore.

Monitoraggio dell'accesso ai file di credenziali (attivo per impostazione predefinita, solo notifica)

Attiva per impostazione predefinita (credential_access_watch_enabled è true; false la ferma, così come l'interruttore nella schermata della guardia antimalware). Notifica quando un programma inatteso apre una chiave privata SSH (~/.ssh/id_*), ~/.aws/credentials, i file di credenziali sotto ~/.config/gcloud/ (4 tipi, tra cui credentials.db) o l'archivio di credenziali/cookie di un browser comune (solo notifica). Si limita a impostare singolarmente su pochi file l'evento di notifica fanotify FAN_OPEN; non blocca, non congela e non nega mai nulla, e il kernel non attende alcuna risposta. Il nome dell'eseguibile del programma che ha aperto il file viene confrontato con allowlist per tipo (ssh / git / gpg-agent, browser, aws / terraform / kubectl, ecc.), e le notifiche ripetute per la stessa coppia (programma, file) vengono soppresse per 10 minuti. Gli strumenti di backup (restic, borg, ecc.) e gli indicizzatori (tracker, baloo, ecc.) sono consentiti fin dall'inizio. Aggiungi gli altri programmi per cui non vuoi notifiche a credential_access_watch_allow in config.json. Per le credenziali AWS sono consentiti anche gli strumenti eseguiti come python / python3 / ansible, come gli script boto3 o Ansible (per gcloud sono consentiti python / python3). Gli one-liner come python -c vengono però notificati. Il prezzo di questa autorizzazione è che nemmeno un programma che ruba credenziali eseguito come file di script python viene notificato. Altri interpreti come node o ruby e gli strumenti generici di ricerca e sincronizzazione come grep o rclone non sono nelle allowlist, perché un programma che ruba credenziali ha esattamente lo stesso aspetto; possono quindi generare notifiche. Una lettura molto breve come cat o head, in cui il programma termina prima che RoamSwitch riesca a identificarne l'eseguibile, può non essere segnalata (una lettura da parte di un programma che resta in esecuzione per un po' viene segnalata). RoamSwitch non usa il fanotify di classe permessi per evitare questo limite, perché potrebbe bloccare il sistema. Le modifiche hanno effetto in circa 30 secondi, senza riavvio.

Il percorso di intrusione nelle notifiche di manomissione dei file

Anche le notifiche del monitoraggio dei file critici (FIM) del client e del monitoraggio dei lockfile delle dipendenze riportano il processo che ha fatto la modifica (account, eseguibile, comando) e l'accesso SSH a cui appartiene (origine, ora, metodo di autenticazione). È fim_intrusion_context_enabled in config.json, true per impostazione predefinita; non viene inviato nulla all'esterno. La notifica di un accesso a un file di credenziali aggiunge una riga solo quando il processo appartiene a un accesso SSH. Il funzionamento, i valori di cui si fida e le tre risposte che tiene distinte (trovato / nessun accesso SSH trovato / percorso di accesso non determinabile) sono gli stessi della Server Edition. Ciò che non si è potuto verificare non viene mai riportato come «non è un accesso». La registrazione è conservata in /var/lib/roamswitch/fim_intrusions.json e nel campo recentIntrusionContext dello strumento MCP verify_fim.

Prelazione e rafforzamento dell'escalation dei privilegi locali del kernel (LPE).

Quando un utente malintenzionato o un malware riesce a prendere piede in un utente non privilegiato, la difesa approfondita impedisce di sfruttare le vulnerabilità del kernel Linux per ottenere privilegi root.

  • Gestione degli user namespace non privilegiati (contromisura per Frag Gap CVE-2026-53362): molte vulnerabilità di escalation di privilegi del kernel Linux moderno derivano dal fatto che utenti non privilegiati chiamano unshare(CLONE_NEWUSER) per creare uno spazio dei nomi utente, accedendo poi in modo illecito a strutture non inizializzate o a sottosistemi di rete legacy. La diagnosi controlla e mostra lo stato di user.max_user_namespaces e kernel.unprivileged_userns_clone, ma la disabilitazione può rompere browser, Flatpak, Electron, container rootless e simili, quindi l'edizione Client non la raccomanda e non la include nemmeno nel rafforzamento con un clic (se è impostato user.max_user_namespaces = 0, guida a ripristinare il valore standard). La disabilitazione è una misura provvisoria che chiude solo i percorsi di attacco che richiedono la creazione di uno spazio dei nomi utente; non ferma le altre vie. L'operazione che imposta kernel.unprivileged_userns_clone = 0 solo finché si è su una rete non attendibile viene applicata soltanto se la abiliti esplicitamente con adaptive_security.untrusted_network_actions.auto_enable_userns_restriction in guard.yaml (per impostazione predefinita è disattivata; sui kernel privi di questa voce di configurazione non fa nulla). La Server Edition la applica in modo permanente per impostazione predefinita (harden_userns_enabled). La vera soluzione è aggiornare a un kernel corretto e riavviare.
  • Yama LSM Memory Protection — L'applicazione di kernel.yama.ptrace_scope = 1 (o 2) impedisce ai processi non autorizzati di collegarsi tramite ptrace o di leggere la memoria tramite /proc/$pid/mem, salvaguardando le sessioni del browser e le chiavi private GPG/SSH.
  • Core Dump Suppression — L'impostazione fs.suid_dumpable = 0 impedisce ai processi sensibili che causano arresti anomali di scrivere dump della memoria in testo normale su disco.
  • __X6/tmp & /dev/shm noexec Mount Defense — Per prevenire attacchi drop-and-execute del payload, RoamSwitch rimonta /tmp e /dev/shm con noexec,nosuid,nodev quando si connette a reti non attendibili.

Sensore di minacce runtime eBPF e congelamento autonomo dei processi (protezione eBPF desktop)

Il demone client RoamSwitch include l'integrazione nativa con i moderni sensori eBPF nel kernel (Falco/Tetragon/BTF).

# Rilevamento delle minacce runtime eBPF e flusso di intercettazione autonoma
[Processo malevolo (LPE/malware)]
    │ Esecuzione di syscall illegale (tentativo di sfruttamento Frag Gap)
    ▼
[Kernel Linux (probe eBPF)]
    │ Buffer circolare eBPF (design compatto da 2 MB)
    ▼
[Falco (RoamSwitch Optimized)]
    │ Socket UNIX diretto in memoria (/var/run/roamswitch/events.sock)
    ▼
[roamswitch-daemon (root)]
    ├─► ① Blocco del processo attaccante in millisecondi: kill(pid, SIGSTOP) [stato: Ts]
    ├─► ② Blocco della rete: Critical solo per l'utente di quel processo (roamswitch_isolate), Emergency/Alert con nftables input/output drop [Air-Gap attivato]
    └─► ③ Avviso di emergenza desktop: modale GTK3 e notifica libnotify
  • Direct UNIX Domain Socket IPC (/var/run/roamswitch/events.sock) — Gli avvisi da Falco o altri sensori eBPF vengono ricevuti direttamente su un socket UNIX in memoria senza scrivere sui registri del disco, eliminando la latenza I/O del disco e consentendo trasferimenti di risposta in meno di un secondo.
  • Risposta autonoma in millisecondi (congelamento del processo e blocco di rete): quando l'evento ricevuto ha priorità Critical (LPE del kernel, escalation di privilegi, cifratura da ransomware, ecc.), il demone invia SIGSTOP al PID interessato per fermare completamente il processo (stato: Ts), esegue un «isolamento mirato» che blocca nella tabella roamswitch_isolate solo il traffico generato dall'utente reale di quel processo (nel caso di un container, dal suo cgroup) e invia una notifica sul desktop. Quando il PID non può essere identificato, quando il congelamento è rifiutato perché il processo è protetto, quando l'isolamento fallisce (per esempio per un processo di root) e quando la priorità è Emergency/Alert, viene attivato un policy drop (Air-Gap) nella tabella inet roamswitch e, se il PID è noto, viene inviato anche SIGSTOP. Gli eventi inferiori a Critical non vengono bloccati e sono registrati nella cronologia.
  • Ottimizzazione a basso overhead per desktop e prevenzione della crescita eccessiva dei log (RoamSwitch Optimized) Per contenere il consumo della batteria, il carico della CPU e l'esaurimento del disco su laptop e workstation, per gli ambienti in cui si installa Falco sono disponibili un profilo ottimizzato (99-roamswitch-optimized.yaml) e una configurazione di logrotate. Il pacchetto dell'edizione Client e la modalità desktop di install.sh non installano Falco e non collocano nemmeno questo profilo (lo collocano il pacchetto della Server Edition e install.sh --server). Se usi Falco con il Client, devi installarlo tu e, se serve, collocare il profilo da solo. Le impostazioni principali sono le seguenti.
    • Filtro gravità (priority: warning): filtra il 95% degli eventi di avviso/informazione di routine.
    • Kernel Early Drop (drop_failed_exit: true) — Elimina le uscite syscall non riuscite direttamente nel driver del kernel, dimezzando i cambi di contesto e il carico della CPU.
    • Buffer circolare compatto (cpus_for_each_buffer: 2) — Riduce la dimensione del buffer circolare a 2 MB, mantenendo la RAM residente a 30–50 MB e l'utilizzo della CPU <1%.
    • Limite rotazione registro obbligatorio (/etc/logrotate.d/roamswitch-falco): impone un limite rigoroso di 50 MB con rotazione compressa di 7 generazioni.
  • Robustezza ibrida: rafforzamento standalone e estensione eBPF: anche senza Falco, il sistema si difende in autonomia con i sysctl del kernel (restrizione ptrace di Yama, soppressione dei core dump, protezioni di rete; applicati a ogni cambio di profilo), i profili nftables, fanotify e il monitoraggio dei canary. Con Falco si aggiungono l'intercettazione zero-day e il congelamento dei processi tramite eventi eBPF in tempo reale, per ottenere la difesa della massima intensità.

§6

Gli eventi USB vengono monitorati; vengono monitorati i dispositivi di tipo tastiera (HID) e gli archivi di massa USB. Off per impostazione predefinita (spedizione isEnabled = false, corrispondente a macOS USBKeyboardGuard / USBStorageGuard). Accedere dalla procedura guidata di prima esecuzione o dalla scheda USB; l'abilitazione mostra una finestra di dialogo di conferma.

Tastiera USB non autorizzata (iniezione di tasti)

  • Nessuna de-autorizzazione della porta fisica a livello di kernel (importante). La vecchia implementazione (≤1.0.6) scriveva 0 in /sys/bus/usb/devices/*/authorized per una tastiera non registrata, disconnettendola fisicamente. Su un ricevitore combinato tastiera+mouse (comune su un Raspberry Pi o un mini PC) questo disconnette anche il mouse, bloccando una macchina senza input integrato. RoamSwitch ora rifiuta definitivamente la scrittura di authorized=0 su qualsiasi dispositivo che espone un'interfaccia HID (bInterfaceClass 03) e il demone autorizza nuovamente qualsiasi dispositivo HID authorized==0 all'avvio per autoriparare le macchine con una versione precedente bloccata.
  • Le sequenze di tasti vengono rilasciate nel software. Quando viene rilevata una tastiera non registrata, viene acquisito un evdev esclusivo EVIOCGRAB su /dev/input/eventN di quel dispositivo (solo i nodi con un gestore kbd in /proc/bus/input/devices, corrispondente a VID:PID). Il nodo del mouse è intatto. Questo è l'equivalente Linux di macOS CGEventTap; il dispositivo rimane alimentato.
  • Safety: la presa viene presa solo se c'è un'altra tastiera su cui digitare (integrata o USB approvata e collegata), quindi una macchina con un'unica nuova tastiera non viene mai bloccata.
  • Finestra di approvazione — insieme alla cattura, una richiesta di approvazione viene accodata in /run/roamswitch/approvals.json e la GUI mostra una finestra modale (Consenti / Nega). Consenti aggiunge il dispositivo alla whitelist e rilascia la cattura. Se nessuno approva entro 3 minuti, la tastiera non viene più rilasciata per impostazione predefinita (dalla 1.10.4; le versioni precedenti fallivano in modo aperto dopo 3 minuti, e abilitando usb_keyboard_timeout_release si ripristina quel comportamento).

Memoria di massa USB non autorizzata

Quando viene inserito un dispositivo di archiviazione di massa USB non registrato (senza HID) e la protezione è attiva, viene mantenuto con authorized=0 (sicuro perché privo di HID) e viene visualizzata una finestra di dialogo di approvazione. Non importa se la rete è attendibile o meno. Le unità già montate o in uso come swap (dischi esterni in uso, USB live, ecc.) non vengono trattenute, perché equivarrebbe a una rimozione e potrebbe danneggiarle; ci si limita a registrarle nel log. Consentire riautorizza e registra nella whitelist; negare mantiene la sospensione. La build di macOS è stata allineata a questo modello di «attesa di sola lettura + richiesta di approvazione» nella versione 1.7.4.

Identificazione e nonno

L'HID è determinato da bInterfaceClass == "03" e dalla riga Handlers= in /proc/bus/input/devices. Un ricevitore combinato (un VID:PID con sia una sezione kbd che una sezione mouse) viene unito per VID:PID. Il grandfathering, cioè la registrazione nella allowlist delle tastiere già presenti, lo esegue la GUI e non il demone. Le tastiere collegate in quel momento vengono registrate nella allowlist quando la protezione viene attivata nella procedura guidata di prima configurazione (e alla prima migrazione di un ambiente esistente in cui la protezione è attiva e la prima configurazione è già stata completata, ma nessuna tastiera collegata è nella allowlist). Se la protezione viene attivata più tardi dalla scheda USB, la registrazione non avviene. Una tastiera collegata all'avvio del demone ma non registrata viene trattata come non registrata, perché potrebbe essere stata sostituita prima dell'avvio, e diventa oggetto di grab se è disponibile un'altra tastiera con cui poter digitare.

§7

LinuxHealthChecker in roamswitch-core valuta 27 elementi e produce un punteggio da 0 a 100, un grado S / A / B / C (100 punti è S, da 85 a 99 è A, da 70 a 84 è B, meno è C) e un consiglio di miglioramento per ogni elemento. Dalla dashboard GTK, dalla CLI (roamswitch status) e da MCP (get_security_report), il titolo, i dettagli e i consigli di tutti i 27 elementi vengono visualizzati nella lingua dell'utente (10 lingue).

Categorie di valutazione
CategoriaElementi di esempio
Disco/avvioCrittografia LUKS/dm-crypt, UEFI Secure Boot
Controllo degli accessiLSM (AppArmor/SELinux), controllo SSH/sudo configurazione
Indurimento del nocciolorafforzamento del sysctl, verifica dello stato degli user namespace non privilegiati (contromisura Frag Gap; l'edizione Client non ne raccomanda la disabilitazione), restrizione ptrace di Yama, controllo dei core dump, noexec su /tmp e /dev/shm
Aggiornamenticonfigurazione automatica dell'aggiornamento di sicurezza
ReteMonitoraggio spoofing ARP, stato di blocco ARP/NDP del gateway, controllo delle porte esposte, potenza della crittografia Wi-Fi
MalwareClamAV (incluso un test reale di rilevamento con EICAR) / fanotify (controlla anche che la guardia sia effettivamente in esecuzione e che non siano stati persi eventi per overflow della coda) / monitoraggio dell'entropia / noexec / verifica dell'installazione dei programmi ausiliari (honeytoken e acquisizione delle prove)
NavigatoreImpostazioni di Navigazione sicura Firefox/Chrome/Chromium
DNS/USBDNS di protezione dalle minacce, stato zero-trust del bus USB
Container e vulnerabilitàEsposizione tramite montaggio del socket Docker, audit dei container privilegiati (--privileged), livello di isolamento del runtime dei container (runc / gVisor / Kata), esposizione a CVE note del kernel in esecuzione

Ogni controllo si basa sulla lettura di file locali e sull'output dei comandi (getenforce, mokutil, resolvectl, ss, …). Non esiste alcuna query esterna di alcun tipo.

Classificazione basata sul trust. Rafforzamento che RoamSwitch deliberatamente non applicare su una rete (domestica) attendibile: bloccando il MAC del gateway come PERMANENTE (che ostruirebbe la LAN al riavvio del router), rimontando /tmp e /dev/shm noexec (che interrompe le build dei pacchetti e alcuni programmi di installazione) e DNS di protezione dalle minacce con l'impostazione predefinita ambito: viene visualizzato in verde con una spiegazione anziché in rosso "non rafforzato" e indica che viene applicato automaticamente su una rete non attendibile. La verità del runtime proviene da /run/roamswitch/state.json (active_level / network_trusted / fanotify_ready), scritto ogni ciclo dal demone. L'elemento fanotify controlla che la guardia sia effettivamente contrassegnata e in esecuzione (/run/roamswitch/fanotify.ready), non semplicemente che il kernel la supporti. L'elemento delle porte esposte legge il profilo live del demone (l'hardcode MCP è scomparso).

Rafforzamento per elemento. Gli elementi non riusciti che hanno una correzione automatica (kernel sysctl / Yama / core dump, /tmp noexec, USB zero-trust, blocco ARP del gateway, DNS di protezione dalle minacce, riavvio del demone quando la protezione fanotify è disattivata) hanno un pulsante "🔧 Rafforza" nel dashboard GTK. Il demone viene eseguito come root, quindi non è necessario pkexec tranne che per il riavvio del demone.

Mappatura NIST CSF 2.0 / CIS Controls v8

A ciascuno dei 27 elementi viene assegnato un identificatore di funzione/categoria NIST CSF 2.0 (ad es. PR.DS-01, DE.CM-09) e, solo per gli elementi con una corrispondenza affidabile, anche un numero di controllo CIS Controls v8 (ad es. 3.11, 4.1). Gli elementi ambigui non vengono forzati in una corrispondenza, ma restano non mappati. Questa mappatura (nistCsf / cisControl) è inclusa nell'output di MCP (get_security_report) e nell'output JSON della CLI (roamswitch status --json). Non compare nella GUI né nella visualizzazione normale della CLI dell'edizione Client (compare invece nella visualizzazione di roamswitch status --server dell'edizione Server). Questa associazione consente agli operatori e ai revisori che devono garantire la conformità di associare meccanicamente ciascun controllo di RoamSwitch ai framework esistenti.

§8

roamswitch-mcp parla JSON-RPC su stdio e viene avviato da client IA come Claude Desktop, Cursor e Antigravity. Ogni strumento è di sola lettura e quasi tutti si completano in locale. L'unico strumento che può connettersi a un host esterno è run_npm_audit_signatures, disattivato per impostazione predefinita. La valutazione di get_security_report e verify_security_findings non invia nulla alla LAN e il gateway viene letto solo dalla cache ARP e da altre informazioni locali. Un ping può partire solo in altri processi che risolvono di nuovo il MAC del gateway (monitoraggio ARP del demone, ripristino di emergenza, abbinamento Sensor, get_guard_status e simili).

Principali strumenti esposti (elenco completo: Configurazione MCP)
StrumentoScopo
get_security_reportValutazione in 27 elementi, punteggio, raccomandazioni
verify_security_findingsRivaluta le voci (checkId) di get_security_report in base allo stato attuale e restituisce un verdetto per ciascuna, stillPresent (ancora applicabile), resolved (risolta, oppure non applicabile per progetto) o inconclusive (non determinabile), insieme a un codice di motivo indipendente dalla lingua. Serve a verificare, dopo una correzione, che abbia davvero avuto effetto. Una voce che non è stato possibile misurare, per esempio per privilegi mancanti, helper privilegiato non connesso o Docker non raggiungibile, non viene mai segnalata come sicura e risulta inconclusive. Omettendo checkIds vengono verificate tutte le voci (fino a 50 identificatori su Mac e 64 su Linux; su Linux è accettato anche isServer). Su Mac, host_firewall, network_stealth_mode, gateway_arp_lock, malware_scanning, dns_threat_guard e usb_zero_trust sono determinati dalle impostazioni dell'app RoamSwitch, quindi riflettono tali impostazioni e non sono una nuova misurazione dello stato reale del sistema operativo. È di sola lettura, non si connette a host esterni e non ripara nulla né modifica impostazioni. Su Linux non invia nulla nemmeno alla LAN e legge il gateway solo dalle cache locali. Su Mac, per ricavare l'indirizzo MAC del gateway, può inviare un ping ICMP, di norma uno (pochi se il MAC non si risolve), al gateway della LAN.
audit_mcp_configsLegge i file di configurazione MCP dei client IA (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Cline, Gemini CLI, più .mcp.json e file simili nelle cartelle di progetto registrate) e segnala impostazioni a rischio. Rileva gli strumenti approvati automaticamente senza conferma a ogni chiamata (auto_approve), i server http:// in chiaro su un host diverso da loopback, indicati sia nel campo url sia come argomento di avvio, come --server http://… (remote_plain_http), gli avvii con comandi shell o interprete su una riga (shell_wrapper), i launcher come npx / uvx / docker run che scaricano un pacchetto o un'immagine senza versione fissata (unpinned_package_launcher), docker run con --privileged o con il socket Docker o il file system root montati (container_host_access), le variabili d'ambiente che caricano codice aggiuntivo all'avvio, come LD_PRELOAD o --require in NODE_OPTIONS (nomi senza distinzione tra maiuscole e minuscole, valori mai restituiti; code_loading_env), le voci che puntano a HexStrike AI, un backend di strumenti d'attacco senza autenticazione (hexstrike), i file di configurazione scrivibili da gruppo o altri (world_writable_config), l'impostazione enableAllProjectMcpServers di Claude Code (auto_trust_project_servers) e un file di configurazione che contiene insieme un server che legge testi scritti da altri (pagine web, e-mail, chat, issue) e un server in grado di eseguire comandi o modificare file, la combinazione di cui ha bisogno la prompt injection indiretta (riconosciuta dai nomi di pacchetto noti, senza vedere il contenuto degli strumenti del server; injection_exposure). Non ha argomenti, è di sola lettura e non invia nulla alla rete. Non restituisce mai valori di ambiente o di intestazioni, argomenti o segreti, solo nomi di server, il nome base di un comando, un host di URL e, per un semplice nome di registro, il nome del pacchetto o dell'immagine. I file che non è stato possibile analizzare sono elencati in unreadableFiles e non sono stati verificati.
audit_agent_setupLegge i file di istruzioni e le skill dati agli agenti IA (CLAUDE.md, commands, agents e skills in ~/.claude, più ~/.codex, ~/.cursor/rules, ~/.gemini, ~/.agents, ~/.openclaw, e CLAUDE.md, AGENTS.md, .cursorrules e simili nelle cartelle di progetto registrate) e l'openclaw.json di OpenClaw, e segnala contenuti e impostazioni a rischio. Nei file di istruzioni e di skill segnala frasi nascoste in caratteri tag Unicode invisibili (hidden_tag_characters), caratteri a larghezza zero e di controllo della direzione (invisible_characters), formulazioni che annullano istruzioni precedenti o nascondono qualcosa all'utente (override_instruction), scarica-ed-esegui come curl … | sh (remote_script_exec), decodifica-ed-esegui (encoded_payload_exec), riferimenti ad archivi di chiavi e credenziali (credential_access) accanto a un invio o a un servizio di raccolta (credential_exfil_shape, exfil_endpoint), impostazione di meccanismi persistenti (persistence_instruction), codice compilato dentro una skill (skill_precompiled_code) e file scrivibili da altri (agent_file_writable). Ciò che si trova in un commento HTML è valutato più alto, perché una pagina renderizzata lo nasconde. Per OpenClaw segnala autenticazione disattivata (openclaw_auth_disabled), un'attesa raggiungibile dalla rete (openclaw_wide_bind), un token sotto i 24 caratteri (openclaw_weak_token), un'origine jolly (openclaw_origin_wildcard), interruttori dangerously… attivi (openclaw_dangerous_flag), un elenco di proxy che si fida di tutti (openclaw_broad_trusted_proxy, openclaw_proxy_trust_loosened), una politica di DM o di gruppo aperta a chiunque (openclaw_open_dm), mDNS in full (openclaw_mdns_full), rilievi di autodiagnosi nascosti (openclaw_audit_suppressed) e permessi troppo ampi (openclaw_loose_permissions). Ogni regola è deterministica: una corrispondenza significa «una persona dovrebbe leggere questa riga», non «malevolo». Non accetta argomenti, è di sola lettura e non invia nulla alla rete. Non restituisce mai il contenuto dei file né segreti (un token è riportato solo come lunghezza), ma solo un nome di regola, un numero di riga, un host di URL e un percorso di chiave di configurazione. I file non leggibili sono elencati in unreadableFiles e non sono stati verificati.
get_porte_esposteporte esposte e il relativo stato di blocco del firewall
get_guard_statuslivello di protezione attuale e stato del modulo di difesa
audit_url_sicurezzaanalisi euristica della sicurezza dell'URL (non recupera il target)
audit_secretsrilevamento di chiavi/segreti API nel testo (non trasmette il testo)
audit_security_logsaggregazione di log journald/auth locali
get_quarantine_status / get_canary_statusquarantena e stato delle Canarie
get_app_helpricerca della base di conoscenza integrata
run_active_vuln_scanVerifica attiva delle vulnerabilità, non distruttiva e limitata a 127.0.0.1. Non contatta altri host, ma invia richieste di rete: per questo è disattivata per impostazione predefinita e richiede l'attivazione esplicita nelle impostazioni.
run_package_cve_scanConfronta i pacchetti installati (Mac: Homebrew / Linux: dpkg, dnf, zypper, pacman) con una mappa CVE locale. Non avviene alcuna comunicazione di rete. Su Linux, se l'elenco dei pacchetti non può essere letto o scade il tempo, non viene riportato «nessuna corrispondenza»; complete diventa false e incompleteReason indica il motivo (un risultato incompleto non è un risultato pulito). Su Ubuntu (20.04, 22.04, 24.04, 26.04) usa i dati propri di Ubuntu, confronta per nome e versione del pacchetto sorgente e controlla solo le CVE con CVSS 7,0 o superiore per cui Ubuntu ha già pubblicato una correzione, quindi «nessuna corrispondenza» non significa che non esistano CVE note. Il risultato aggiunge ecosystem e release, ogni rilevamento riporta binary (il nome del pacchetto binario installato) e le correzioni disponibili solo con Ubuntu Pro hanno proOnly. Le derivate di Ubuntu (Linux Mint, Pop!_OS e altre) e le versioni non supportate restituiscono complete = false con il motivo in incompleteReason, senza passare ai dati di Debian.
run_package_cve_scan_languagesConfronta i file di lock delle dipendenze di npm, PyPI, crates.io, RubyGems, Packagist, Go e Maven con la stessa mappa CVE locale. Non avviene alcuna comunicazione di rete.
get_vulnerability_scan_historyRestituisce, per ciascuna sonda registrata da scan-vulns --confirm, il numero di giorni trascorsi dall'ultima esecuzione e conferma effettiva (pass_age_days).
verify_fimRicalcola gli hash di 23 file critici di sistema fissi, di tutti i file direttamente in 12 directory (autenticazione, cron, systemd, configurazione SSH, ecc.), dei binari di RoamSwitch e dell'authorized_keys di ogni utente, e li confronta con la baseline per verificare manomissioni e nuovi file. Se eseguito come root, il risultato contiene anche un campo aggiunto, recentIntrusionContext: per ogni modifica recente, il processo che l'ha fatta (account, eseguibile, comando) e l'accesso SSH a cui appartiene (origine, ora di accesso, metodo di autenticazione), oppure il motivo per cui non è stato possibile scoprirlo.
get_file_scan_guard_statusRestituisce la configurazione della guardia di scansione dei file (ClamAV) e lo stato del Vault di quarantena che utilizza (strumento per la Server Edition; non è la configurazione della guardia antimalware dell'edizione Client).
get_port_anomaly_incidentsRestituisce lo stato della baseline della protezione dalle porte anomale, le porte attualmente bloccate in automatico e fino ai 50 incidenti più recenti. La risposta indica esplicitamente che le porte attualmente bloccate sono un'istantanea dello stato attuale, priva di timestamp, distinta dalla cronologia degli incidenti, che invece è dotata di timestamp.
get_ebpf_incidentsRestituisce lo stato di isolamento attuale della guardia runtime eBPF e la cronologia degli incidenti all'origine. Nella Server Edition ogni record riporta anche il tempo dal rilevamento alla risposta in millisecondi (detect_to_receive_ms, receive_to_action_ms, detect_to_action_ms), e il risultato include lo stato dell'heartbeat di Falco (heartbeat).
get_ssh_guard_incidentsRestituisce lo stato e la cronologia della guardia contro la forza bruta SSH: se banna o si limita a rilevare (per esempio perché un altro strumento protegge già sshd), le origini bannate al momento con il tempo residuo e fino ai 200 record più recenti (un'origine è stata bannata, è stata rilevata senza ban, oppure un login è riuscito dopo una serie di fallimenti della stessa origine). I nomi utente provati non vengono salvati su Linux (solo quanti) e su Mac vengono restituiti ripuliti, fino a 5. Il Mac restituisce anche i login SSH recenti (origine, utente, metodo, impronta della chiave). Sola lettura: nessuno strumento MCP rimuove un ban. Su Linux l'elenco dei ban è leggibile solo da root, quindi chi chiama senza root riceve solo il numero. Linux: la Server Edition e la Client Edition finché sshd è in esecuzione. Mac: solo Pro.
get_resource_guard_incidentsRestituisce la cronologia della guardia contro l'esaurimento delle risorse e le anomalie di processo (memory leak, crash loop), con un livello di confidenza (questa guardia appartiene alla Server Edition; nell'edizione Client non è in esecuzione, quindi il risultato è sempre vuoto).
get_notification_historyRestituisce la cronologia delle notifiche inviate da RoamSwitch (anomalie di controllo dei log di sicurezza, rilevamenti ClickFix e altro) degli ultimi 7 giorni, le più recenti per prime.
get_incident_timelineUnisce i rilevamenti di tutte le guardie in un'unica linea temporale, con la genealogia dei processi e i tag MITRE ATT&CK (sperimentale).

La verifica attiva delle vulnerabilità registra inoltre, per ogni sonda confermata, quanti giorni sono trascorsi dall'ultima esecuzione e conferma effettiva (pass_age), distinguendo così «questo controllo esiste» da «questo controllo è stato effettivamente verificato di recente». Non esiste ancora un esecutore pianificato, quindi questo valore riflette solo la frequenza con cui la scansione è stata effettivamente eseguita. Lo stesso numero di giorni trascorsi è disponibile dalla CLI (roamswitch vuln-status), dalla GUI e da MCP (get_vulnerability_scan_history), permettendo a un operatore o a un revisore di vedere quando è stato effettivamente verificato l'ultima volta, anziché presumere una verifica recente solo perché la funzione esiste.

Il server MCP non apre alcuna rete propria. Solo get_sensor_audit_results si collega direttamente al socket Unix del demone (/run/roamswitch/roamswitch.sock); gli altri strumenti richiamano direttamente la logica di roamswitch-core o si limitano a leggere i file di stato scritti dal demone (roamswitchkit va nella direzione opposta: è l'SDK con cui la CLI e simili avviano roamswitch-mcp come processo figlio). Le eccezioni sono due strumenti disattivati per impostazione predefinita: run_active_vuln_scan invia sonde non distruttive a 127.0.0.1, cioè all'host stesso, e run_npm_audit_signatures avvia il comando npm, che interroga il registro npm (il binario MCP in sé non contiene alcuno stack HTTP / TLS). La valutazione della diagnosi (get_security_report e verify_security_findings) non invia nulla alla LAN e anche il controllo del gateway consulta solo informazioni locali come la cache ARP. Un ping può partire solo in altri processi che risolvono di nuovo il MAC del gateway (monitoraggio ARP del demone, ripristino di emergenza, abbinamento Sensor, get_guard_status e simili).

Un'attribuzione errata scoperta — e corretta — durante un'esercitazione con un LLM locale
Nel settembre 2026, durante un'esercitazione (fire drill) in cui a un LLM locale (Qwen 3.8 27B) è stato chiesto di indagare su un incidente tramite questo server MCP, abbiamo scoperto un caso in cui il modello ha interpretato male i dati restituiti da get_port_anomaly_incidents, includendo erroneamente un blocco di porta vecchio e non correlato nella narrazione di un incidente in corso. La causa: un'istantanea priva di timestamp delle porte attualmente bloccate veniva restituita nella stessa risposta della cronologia reale degli incidenti, dotata di timestamp, senza nulla che distinguesse le due cose. La correzione aggiunge campi di nota espliciti che indicano quale campo rappresenti una linea temporale e quale sia solo un'istantanea. Rieseguendo lo stesso scenario con lo stesso modello dopo la correzione, il modello ha citato il testo della nota e ha esplicitamente rifiutato di attribuire la vecchia porta all'incidente attuale. L'efficacia della correzione è stata verificata sia con un test di regressione automatizzato sia con una nuova esecuzione del vero LLM locale.

§9

Per impostazione predefinita, RoamSwitch per Linux effettua un solo tipo di richiesta in uscita: il GET di un manifesto firmato da parte dell'aggiornatore giornaliero (§11). Tutto il resto – ogni valutazione, rilevamento e verifica – si completa in locale. Le comunicazioni che avvengono solo quando un operatore o un utente le attiva o le avvia esplicitamente sono elencate nel 9.4. RoamSwitch per Linux è freeware proprietario e il suo sorgente non è pubblicato, quindi le verifiche seguenti sono state eseguite da lafine; i risultati sono nel 9.2 e i passaggi dell'appendice C sono riproducibili con il solo pacchetto distribuito.

9.1 Metodo di verifica

  1. Isolamento dei crate — uno stack client HTTP/TLS è consentito in un solo crate, roamswitch-updater, che compila il binario autonomo dell'aggiornatore a timer. scripts/audit_no_network.sh (eseguito in CI, bloccante) fa fallire la build se roamswitch-core, roamswitch-daemon, roamswitch-app, roamswitch-mcp, roamswitchkit (o i crate CLI e Server) includono uno stack client HTTP/TLS come reqwest / ureq / rustls / native-tls / openssl-sys, o se un file .rs esterno a roamswitch-updater e ai file citati nel 9.4 usa TcpStream / TcpListener / UdpSocket / to_socket_addrs / lookup_host. L'unica autorizzazione aggiuntiva riguarda tonic / hyper, consentiti solo in roamswitch-core per gRPC su socket di dominio UNIX verso il sensore eBPF (Tetragon); lo stesso script vieta di abbinarli a TLS.
  2. Ispezione dell'albero delle dipendenze: è stato cercato l'intero Cargo.lock per SDK di telemetria/analisi/segnalazione di arresti anomali (sentry, opentelemetry, …).
  3. roamswitch-updater review: il suo obiettivo di richiesta singolo, l'assenza di stringa di query e di cookie, e uno User-Agent fisso come unica intestazione che nomina il software e la verifica Ed25519 di tutto ciò che scarica.
  4. Enumerazione sottoprocessi: ogni sito di chiamata Command::new(...) è stato esaminato.

9.2 Risultati

Risultati dell'audit
Elemento di controlloRisultato
Pila HTTP/TLSPresente solo in roamswitch-updater (ureq + rustls). Non in core / daemon / app / mcp / kit
SDK di telemetria/analisi/segnalazione di arresti anomaliNon presente da nessuna parte
TcpStream / TcpListener / UdpSocketSolo in roamswitch-updater e nei quattro file delle funzioni opzionali elencati nel 9.4
API socket in usoAF_UNIX (IPC locale); AF_NETLINK (canale di verdetto NFQUEUE, controllo di nftables e ip neigh, senza emissione di pacchetti); AF_INET solo in roamswitch-updater e nelle funzioni opzionali del 9.4 (TLS/HTTP sono delegati ai sottoprocessi di sistema openssl/curl); AF_PACKET solo quando la riverifica ARP (§4) invia un singolo ARP who-has al gateway (nella LAN). Il tunnel VPN (§4), se attivato, è wg-quick (backend WireGuard) o tailscaled (backend Tailscale), un sottoprocesso e non il demone, che invia traffico all'endpoint / tailnet dell'utente
Aggiornamento quotidianoUn GET https://lafine.net/updates/v1/manifest e la firma manifest.sig, circa ogni 24 ore (i dati veri e propri vengono scaricati in più solo quando la versione è più recente). Nessuna stringa di query, nessun cookie, User-Agent: RoamSwitch-Updater fisso (l'unica intestazione che nomina il software), nessun identificatore per dispositivo. Tutti i payload sono verificati con Ed25519. updates.enabled = false lo disabilita completamente
Tunnel VPN (§4)Disattivato per impostazione predefinita. Se abilitato, wg-quick crea un tunnel WireGuard su l'endpoint nell'.conf dell'utente, non correlato a lafine. Risoluzione del nome host dell'endpoint tramite getent ahosts (glibc NSS), non un'API socket Rust
Attivazione della licenzaVerifica della firma Ed25519 offline. Nessuna comunicazione con un server di attivazione
Conclusione

Per impostazione predefinita, RoamSwitch per Linux avvia un solo tipo di comunicazione in uscita: il controllo giornaliero, in sola ricezione, di un manifest firmato, con il download di dati di rilevamento firmati quando esiste una versione più recente. Nessuno dei due contiene alcuna informazione sull'utente o sulla macchina. Salvo attivazione o azione esplicita da parte di un operatore o di un utente, nessun'altra parte del prodotto contatta un server esterno (9.4). Il ping inviato all'interno della LAN per il fissaggio della tabella dei vicini e per il monitoraggio ARP (n. 2 del 9.3) va solo a indirizzi loopback, privati, link-local e ULA e non viene mai inviato a un indirizzo pubblico.

9.3 Elenco completo delle operazioni rivolte all'esterno

Tutte le operazioni del funzionamento predefinito che possono far uscire un pacchetto dalla macchina locale, direttamente o indirettamente (la tabella seguente; le operazioni che avvengono solo quando un operatore o un utente le attiva o le avvia sono nel 9.4):

#OperazioneDestinazioneInnescoCosa viene inviato
1Aggiornamento quotidiano: GET /v1/manifest e la sua firma; i file di dati solo quando la loro versione è più recente (mappe CVE del kernel, dei pacchetti del sistema operativo e dei linguaggi controllate circa una volta ogni 30 giorni; un dispositivo Ubuntu senza la mappa CVE di Ubuntu la scarica alla successiva esecuzione giornaliera)lafine.net/updates/v1 (CDN)Timer systemd. Parte 15 minuti dopo l'avvio e poi circa ogni 24 ore (con un ritardo casuale fino a 3 ore). Non avviene quando updates.enabled = false o roamswitch-update.timer è disabilitatoun semplice HTTPS GET: nessuna stringa di query, nessun cookie, User-Agent: RoamSwitch-Updater fisso, niente sull'utente o sulla macchina. Riceve un manifest firmato + feed
1afreshclam (aggiornamento di ClamAV), richiamato dall'aggiornamento quotidiano quando ClamAV è installatoil CDN del progetto ClamAVCome parte del n. 1. Viene avviato anche quando l'utente preme nella schermata il pulsante per aggiornare subito le definizioni antivirus (freshclam)traffico dello stesso ClamAV (un progetto separato); RoamSwitch lo avvia solo
2ping -c 1 -w 1 -W 1 <IP del gateway> (un ping dello stesso tipo anche verso il router IPv6 e verso i server DNS della LAN, una volta ciascuno)Il gateway predefinito della LAN. Nella verifica del blocco ARP/NDP, il router predefinito IPv6 e, tra i server DNS elencati in resolv.conf (con systemd-resolved, /run/systemd/resolve/resolv.conf), solo quelli con indirizzo loopback, privato, link-local o ULA. Non viene inviato alcun pacchetto a un resolver pubblico (come Quad9 con protezione dalle minacce DNS)Il gateway solo quando serve il MAC (per esempio per il monitoraggio ARP) e non è presente né nella cache ARP, né nella tabella dei vicini, né in una copia di riserva di meno di 15 secondi (al massimo una volta ogni 8 secondi verso la stessa destinazione). Verso il router IPv6 e i server DNS, quando il blocco ARP del gateway (gateway_arp_lock_enabled, attivo per impostazione predefinita) ripete il fissaggio nella tabella dei vicini (al passaggio a una rete non attendibile e al cambio di impostazione)Una sola richiesta eco ICMP (con i dati standard di ping). Nessuna risoluzione DNS
3avviando il browser di sistema (xdg-open)https://roamswitch.com/linux (sito del prodotto), https://lafine.net/ (sito dell'operatore, condizioni d'uso, contatti), https://github.com/lafine1211/roamswitch-support (note di rilascio e supporto)solo quando l'utente fa clic su un pulsante come "Apri note di rilascio"il processo RoamSwitch non apre alcun socket; trasmette l'URL al browser del sistema operativo
4risolverectl dns …— (RoamSwitch non genera alcuna comunicazione propria)DNS di protezione dalle minacce su una rete non attendibile (attivo per impostazione predefinita, disattivabile). <iface> indica ogni scheda NIC fisica che trasporta un percorso predefinito (cablato + wireless contemporaneamente; lo / tailscale* / wg* / docker* / veth* esclusi); applicato anche su una rete attendibile quando dns_scope = always_on; riconciliato ogni ciclo sentinellaModifica soltanto l'impostazione del resolver del sistema operativo; RoamSwitch stesso non invia alcuna query DNS. Dopo l'impostazione, la risoluzione dei nomi di questa NIC passa al DNS con protezione dalle minacce scelto (per impostazione predefinita Quad9)
5aggiornamenti del gestore pacchettii repository apt / dnf / zypper / pacman della distribuzionequando l'utente esegue apt upgrade, ecc., oppure preme il pulsante «Aggiorna ora» della GUI (che esegue apt-get / dnf / zypper / pacman tramite pkexec)Con apt, RoamSwitch legge l'output locale di apt-cache policy e si limita ad avviare il gestore di pacchetti. Con dnf esegue dnf check-update, per esempio quando si apre la scheda degli aggiornamenti, e può interrogare i repository configurati (zypper list-updates per zypper, pacman -Qu per pacman). In nessun caso c'entra lafine
6VPN tunnel (§4) — Backend WireGuard: il tunnel wg-quick crea / Backend Tailscale: tailscale set --exit-node instrada tutto il traffico attraverso il nodo di uscital'endpoint VPN/tailnet dell'utente (WireGuard: l'endpoint in .conf; Tailscale: il nodo di uscita scelto e i relè DERP)quando vpn_on_untrusted_enabled e la rete non sono attendibili. Disattivato per impostazione predefinitatraffico WireGuard/Tailscale crittografato; la destinazione è il server VPN/tailnet dell'utente, non correlato a lafine. Risoluzione dell'endpoint WireGuard tramite getent ahosts (glibc NSS); Tailscale è guidato da tailscaled (un processo separato)

Tutte le altre funzioni di RoamSwitch – controllo di nftables, blocco ARP/NDP della tabella dei vicini (ip neigh, nessun pacchetto emesso), guardia dei link (NFQUEUE, solo AF_NETLINK), scansione delle porte (analisi dell'output di ss), monitoraggio ARP, valutazione dello stato di salute, monitoraggio dell'entropia, canary, fanotify, monitoraggio USB (grab evdev), quarantena, audit dei log, audit di URL e segreti – si completano solo con letture di file locali e analisi dell'output di sottoprocessi. Il tunnel VPN del §4 è la «comunicazione in uscita configurata esplicitamente dall'utente»; la sua destinazione è il server VPN dell'utente stesso e nessun dato fluisce verso l'infrastruttura di lafine. La destinazione di ciascuna funzione del 9.4 è indicata nel 9.4.

9.4 Operazioni che raggiungono l'esterno solo con opt-in o su azione dell'utente

La tabella del 9.3 riguarda ciò che avviene per impostazione predefinita. Di seguito sono elencati i punti del codice che aprono un socket o affidano la comunicazione a un programma esterno (curl / openssl / npm) e che funzionano solo se un operatore o un utente li attiva o li avvia esplicitamente. scripts/audit_no_network.sh consente le API socket solo in cinque file: event_forward.rs, webhook_guard.rs, active_vuln_scan.rs, web_vuln_scan.rs e sensor_pairing.rs (webhook_guard.rs, solo quando si attiva l'impostazione forward_webhook_block_private_addresses, disattivata per impostazione predefinita, risolve con il resolver di sistema il nome della destinazione del webhook per verificare che non sia un indirizzo interno, senza connettersi; inoltre tests/fleet_roundtrip.rs, solo per i test, si connette unicamente a un server simulato su 127.0.0.1), e fa fallire la build se altro codice usa TcpStream e simili. Neppure queste funzioni aggiungono crate HTTP/TLS: TLS è lasciato all'openssl di sistema e HTTP al curl di sistema, come sottoprocessi.

  • Inoltro degli eventi (event_forward.rs) — disattivato per impostazione predefinita (forward_enabled = false), e le uniche destinazioni sono quelle che l'operatore configura a mano; non esiste alcuna destinazione integrata. I sink supportati sono syslog (RFC 5424 o CEF, su UDP / TCP / TLS), un file JSON Lines e 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 come URL e firma sono passati in una configurazione sullo standard input, mai sulla riga di comando); il demone non include alcun crate TLS/HTTP. Vengono inviati solo questi campi: nome host, ID evento, timestamp, origine, gravità, regola, azione, PID, nome del processo e riepilogo (né contenuto dei file né il log grezzo delle esecuzioni). Gli eventi non consegnati via TCP / TLS / webhook sono tenuti in una coda su disco 0600 (limite predefinito 20 MB / 72 ore) e ritentati.
  • Fleet (livello Business, solo lato dispositivo) (roamswitch-core/src/fleet/) — il codice lato dispositivo è incluso nel binario, ma un dispositivo non registrato non invia nulla e non risolve nemmeno il nome host di Fleet (il worker legge soltanto un file di stato locale). La registrazione avviene solo quando un operatore esegue roamswitch fleet enroll. Il servizio di gestione non è distribuito né rilasciato, quindi al momento non c'è nulla a cui connettersi. Le garanzie di progetto presenti nel codice sono: (a) il dispositivo accetta solo dati di policy firmati dall'organizzazione, verificati con la chiave pubblica dell'organizzazione fissata alla registrazione; una policy che non supera la verifica non viene applicata. Il progetto prevede che la chiave di firma dell'organizzazione sia detenuta dal cliente, non da lafine. (b) Non esiste alcun canale di comando remoto: lato dispositivo non c'è alcun endpoint, campo o percorso di codice con cui si possa ordinare di isolare, rilasciare, raccogliere o eseguire. (c) Una policy non può indebolire il minimo locale: disattivare una protezione od ostacolare il rilascio / il ripristino di emergenza non è nemmeno esprimibile nello schema, e prevale il valore più severo. Una policy può inoltre cambiare la destinazione dell'inoltro degli eventi solo verso host consentiti dal root locale (fleet_forwarding_allowed_hosts, vuoto per impostazione predefinita). (d) Un dispositivo invia solo un riepilogo minimo dello stato — versione dell'app, famiglia di SO, stato di isolamento (normale / isolato), versione della policy applicata, punteggio di salute (0–100) ed epoca della chiave — più ID dispositivo e ID organizzazione nelle intestazioni di firma. Usa il curl di sistema (TLS) e solo per un dispositivo registrato.
  • Abbinamento con Sensor (sensor_pairing.rs) — disattivato per impostazione predefinita (sensor_pairing_enabled). Si connette solo, e direttamente, all'indirizzo del Sensor inserito dall'operatore; non esiste alcuna destinazione integrata. Supporta TLS 1.3 (tramite l'openssl di sistema, con pinning dell'impronta del certificato), ma un abbinamento precedente privo di pin ripiega sul testo in chiaro con un avviso. Il Sensor è un prodotto distinto e non è coperto dalle affermazioni Zero Telemetry di questo documento.
  • Verifica attiva delle vulnerabilità (active_vuln_scan.rs / web_vuln_scan.rs) — disattivata per impostazione predefinita e richiede l'attivazione nelle impostazioni. In questa edizione per postazioni, il percorso è limitato a sonde verso 127.0.0.1 per le porte in ascolto già osservate su questa macchina (fissato dai test propri dei moduli).
  • Il pulsante «Verifica aggiornamenti» della GUI: solo quando l'utente preme il pulsante, nelle distribuzioni basate su apt la GUI esegue un singolo GET di https://lafine.net/apt/dists/stable/main/<arch>/Packages con il curl di sistema per scoprire l'ultima versione (timeout di 4 secondi; URL pubblico privo di segreti, senza identificatori né stringa di query). Con dnf, zypper e pacman non si usa curl e si interroga ciascun gestore di pacchetti (n. 5 del 9.3).
  • Verifica delle firme npm — roamswitch audit-npm-signatures <DIR> --confirm funziona solo se l'impostazione npm_audit_signatures_enabled (false per impostazione predefinita) è attiva e l'utente lo esegue con --confirm; è quindi npm stesso a contattare registry.npmjs.org.

§10

  • Nessun account: nessuna registrazione utente, nessun accesso, nessun indirizzo email mai richiesto.
  • La configurazione è solo locale — archiviata in testo normale in ~/.config/roamswitch/config.json e non lascia mai la macchina. Non c'è sincronizzazione cloud.
  • I risultati della valutazione sono solo locali: i report sulla sicurezza e i risultati del controllo del registro vengono visualizzati solo sullo schermo e, se l'utente lo desidera, esportati in un file locale.
  • Le chiavi di licenza vengono verificate offline: i token di licenza Pro/Business contengono una firma Ed25519 e vengono verificati con una chiave pubblica. Non viene effettuata alcuna query su un server lafine.
  • No crash reporting: in caso di panico, nessuna traccia dello stack viene inviata da nessuna parte.
  • L'aggiornamento quotidiano non invia nulla su di te: nessuna stringa di query, nessun cookie, nessun identificatore per dispositivo o installazione, e uno User-Agent: RoamSwitch-Updater fisso come unica intestazione che nomina il software. Si tratta di un download, non di un report. I verdetti di protezione del collegamento, gli host bloccati e i risultati della scansione vengono scritti solo nella visualizzazione dell'attività locale/registrati nel journal, mai trasmessi.
  • Finché è in uso il DNS con protezione dalle minacce, le query DNS raggiungono un resolver esterno. Il DNS con protezione dalle minacce (§4.5; attivo per impostazione predefinita, solo sulle reti non attendibili) punta il resolver DNS del sistema operativo a Quad9 (9.9.9.9; si può scegliere anche 1.1.1.2 di Cloudflare). In questo periodo le query vengono inviate a quel resolver dal sistema operativo e non da RoamSwitch, che di per sé non invia nulla. Per fermarlo, imposta dns_enabled su false.
  • La cronologia delle notifiche viene eliminata dopo 7 giorni e ha lunghezza limitata — la cronologia delle notifiche inviate è salvata in locale e a ogni scrittura vengono eliminate le voci più vecchie di 7 giorni. Un titolo è troncato a 200 caratteri e un corpo a 2000 (un popup di notifica desktop mostra al massimo 1000 caratteri di corpo). Questa cronologia non lascia la macchina a meno che l'inoltro degli eventi (disattivato per impostazione predefinita) sia attivo.
  • Anche il registro delle esecuzioni è solo locale — il registro del registratore delle esecuzioni dei processi (§5) è salvato, per impostazione predefinita, solo sulla macchina (/var/lib/roamswitch/exec_log, 0600). Poiché contiene argomenti della riga di comando, viene eliminato automaticamente in base al periodo di conservazione (14 giorni per impostazione predefinita) e alla dimensione (200 MB per impostazione predefinita).

§11

Edizioni
EdizionePerPrezzoLicenza
Edizione comunitariaLinux, uso personale e aziendale (nessun limite al numero di dispositivi)GratuitoFreeware proprietario (EULA in bundle)
Business (pianificato)Gestione flotta, distribuzione polizze, apt interno firmato, SLA per organizzazioniPagatoLicenza commerciale. Stesso binario + cancello di livello

Quando il livello Business sarà disponibile, la politica prevista è concedere gratuitamente le funzioni Business sulle proprie macchine Linux a chi ha acquistato macOS RoamSwitch Pro Lifetime. Oggi il livello Business non viene offerto e non esiste alcun server di gestione.

Distribuzione e filiera

CanaleDettaglio
APPhttps://lafine.net/apt (stable main). Release firmato con una chiave RSA-4096
giri al minutohttps://lafine.net/rpm (Fedora / openSUSE separato). repomd.xml Firmato GPG
AURroamswitch-bin (in preparazione. Poiché l'AUR ha sospeso la registrazione di nuovi account, per ora si installa con makepkg da https://lafine.net/linux/dl/PKGBUILD; il tarball di lafine.net/linux/dl viene verificato con sha256 e firma staccata (validpgpkeys))
tarballSu https://lafine.net/linux/dl/: .tar.gz per x86_64 / aarch64 + .sha256 + .asc (firma staccata con la chiave di firma del repository). Poiché il repository dei sorgenti è privato, la fonte pubblica non è GitHub Releases ma questa
  • Pinned glibc floor — i binari di rilascio sono compilati all'interno di un contenitore Debian 12 (bookworm, glibc 2.36). Ciò impedisce mancata corrispondenza della versione del simbolo su Debian 12/Ubuntu 22.04 e versioni successive. Un passaggio CI "verifica glibc floor" fallisce la compilazione se viene introdotta una dipendenza che richiede più di 2.36.
  • Gestione delle chiavi di firma — la chiave privata per la firma del repository apt/rpm esiste solo in un segreto crittografato di GitHub Actions; solo la chiave pubblica è impegnata nel repository.

11.3 L'aggiornamento quotidiano

roamswitch-updater è un binario autonomo eseguito da un timer systemd (roamswitch-update.timer: 15 minuti dopo l'avvio e poi ogni 24 ore, con un ritardo casuale fino a 3 ore). Per impostazione predefinita è l'unico componente che raggiunge un URL di distribuzione esterno (escluse le funzioni opzionali del 9.4). A ogni esecuzione:

  1. GET https://lafine.net/updates/v1/manifest e la sua firma staccata.
  2. Verifica la firma Ed25519 del manifest con una chiave di firma del feed di minacce dedicata, separata dalla chiave di aggiornamento dell'app Sparkle. Il feed viene firmato nuovamente quotidianamente dal CI lato server, in modo che la chiave privata risieda in un segreto del CI; inserirlo nel feed significa che una perdita consente a un utente malintenzionato solo di fornire una blocklist errata (sinkhole/avviso: nessun percorso del codice, nessun percorso di aggiornamento dell'app). La chiave di firma dell'aggiornamento dell'app non lascia mai il portachiavi di macOS. Un manifest che non viene verificato viene scartato: il programma di aggiornamento non ricorre mai ai dati non firmati.
  3. Se la versione del feed delle minacce è cambiata, scarica il feed, verifica la sua firma SHA-256 ed Ed25519 e installalo atomicamente per la protezione del collegamento (§4). Gli altri dati vengono scaricati solo quando il manifest indica una versione più recente (la mappa CVE della scansione attiva e l'elenco dei pacchetti npm popolari). Le mappe CVE del kernel, dei pacchetti di sistema e degli ecosistemi dei linguaggi vengono scaricate solo quando sono passati circa 30 giorni dall'ultimo aggiornamento. La mappa CVE di Ubuntu viene scaricata solo su una versione di Ubuntu supportata; un dispositivo che non ce l'ha ancora la scarica alla successiva esecuzione giornaliera, senza attendere i 30 giorni. Tutto viene installato solo dopo la verifica della firma.
  4. Quando la versione del manifest è più recente, vengono scaricate anche le regole di rilevamento del livello applicativo web (§5). Si verificano SHA-256 e firma Ed25519 e si controlla che il file sia leggibile come regole prima di installarlo in modo atomico. Un file illeggibile non viene mai installato e le regole in uso non vengono sostituite. Non si aggiunge alcuna nuova destinazione: il recupero è incluso in quello del manifest. Nell'edizione Server segue cve_kernel_map_updates_enabled: se è disattivato, non c'è alcun contatto di rete.
  5. Se ClamAV è installato, invoca freshclam.
  6. Registra l'ultima versione dell'app per una notifica (il programma di aggiornamento non installa mai l'app; su Linux l'utente esegue apt upgrade).

Il fallimento di qualsiasi passaggio (offline, firma errata, freshclam mancante) viene registrato in lastError di updates.json e il processo esce comunque con codice 0, quindi l'esecuzione del timer non genera mai una notifica di errore di systemd. L'intero meccanismo può essere disabilitato con "enabled": false in updates.json o con systemctl disable --now roamswitch-update.timer; il prodotto viene quindi eseguito solo sui dati inclusi nel pacchetto. Il feed delle minacce viene fornito anche all'interno del pacchetto, quindi funziona fin da una nuova installazione e apt upgrade lo aggiorna anche con il programma di aggiornamento disabilitato.

§12

  • SO: Ubuntu 22.04 / 24.04 e successive, Debian 12 e successive, Linux Mint / Pop!_OS / elementary / Zorin, famiglia Fedora / RHEL, openSUSE, famiglia Arch (l'AUR è in preparazione; si installa da PKGBUILD), Raspberry Pi OS 64-bit (Bookworm), Ubuntu per Raspberry Pi
  • Architettura: x86_64 / aarch64
  • Richiesto: systemd, nftables, iproute2 (ip neigh, blocco ARP/NDP), conntrack, libnotify (notifiche desktop) (a parte systemd, inseriti automaticamente dalle dipendenze .deb / .rpm)
  • Opzionale: clamav (scansione malware), nmcli (rilevamento della crittografia Wi-Fi), iw (alternativa per le informazioni wireless), bluez (guardia Bluetooth), systemd-resolved (impostazione del DNS con protezione dalle minacce), lsof (alternativa per il rilevamento delle porte in ascolto), nmap (ausilio alla verifica attiva delle vulnerabilità), wireguard-tools (il backend WireGuard del tunnel VPN + kill-switch, §4), tailscale (il suo backend Tailscale, §4), polkit / pkexec (aggiornamento, disinstallazione e riavvio del demone dalla GUI)
  • Non supportato: distribuzioni non systemd (Alpine/Void/Devuan)
  • Display: 1280×720 o superiore (la finestra e il carattere si adattano automaticamente alle dimensioni dello schermo, supportando piccoli display Raspberry Pi)

Appendice A

CassaScopoRete
serde/serde_jsonserializzazione di configurazione e IPCnessuno
Tokyoruntime asincrono (features = ["full"]; per impostazione predefinita si usano solo socket Unix)nessuno (nessun TCP per impostazione predefinita; fa eccezione l'abbinamento Sensor opzionale)
tracciamento/tracciamento-abbonatoregistrazione (locale/journald)nessuno
questo errore / comunquegestione degli errorinessuno
espressione regolareanalisi delle stringhe (diagnostica, ridimensionamento CSS)nessuno
URLAnalisi URL (euristica audit_url_safety)nessuno (non viene recuperato)
cronogestione data/oranessuno
ed25519-dalekverifica offline delle firme delle licenze e dei dati firmati come il manifest dell'updater e il feed delle minacce (usata anche per l'abbinamento Sensor opt-in)nessuno
base64/sha2token di licenza, hash canarynessuno
libcchown / kill / chiamate di sistemanessuno
idnadecodifica host IDN/punycode per il rilevamento degli omografinessuno
nfq (solo demone)Attacchi NFQUEUE per la protezione del collegamento: pure Rust, AF_NETLINKnessuno (canale del verdetto, nessuna uscita)
apertochiama xdg-open per «Apri le note di rilascio» e per aprire i collegamenti alla EULA e alla pagina di supportoil processo stesso non comunica
GTK3/ksni (solo app)GUI e vassoionessuno
serde_yamllettura di /etc/roamswitch/guard.yaml (core)nessuno
tomlanalisi di Cargo.lock / poetry.lock (scansione CVE dei pacchetti di linguaggio, core)nessuno
md-5hash per le impronte JA3 del Link Guard (non usato per autenticazione o integrità, core)nessuno
bs58controllo Base58 delle chiavi private di criptovalute (rilevamento di segreti, core)nessuno
randgenerazione della chiave del dispositivo per l'abbinamento opt-in con il Sensor (core)nessuno
tonic / tonic-prost / prost / tower / hyper-utilgRPC verso il sensore Tetragon; solo socket di dominio Unix, senza TLS (core)nessuno (solo socket Unix)
flate2decompressione gzip della mappa CVE dei pacchetti della famiglia RHEL (solo updater; Rust puro)nessuno (solo decompressione)
inotifymonitoraggio degli accessi agli honeytoken, ossia le esche di credenziali (roamswitch-honeytokens)nessuno
ayaloader del boot gate XDP sperimentale (roamswitch-xdp-gate, referenziato dal demone)nessuno (solo bpf() / netlink)
urlencoding / gdk / gdk-pixbuf / glib / pangodecodifica degli URL nella GUI e crate GTK3 circostanti (solo app)nessuno
ureq + rugginels (solo roamswitch-updater)l'HTTPS GET dell'aggiornamento quotidiano (manifest, firma e dati aggiornati)l'unico stack HTTP/TLS nell'albero; confinato al binario di aggiornamento
Punto chiave

In roamswitch-core, roamswitch-daemon, roamswitch-app, roamswitch-mcp e roamswitchkit non compaiono né client HTTP che escano su Internet, né stack TLS, né SDK di telemetria. tonic / hyper, presenti in roamswitch-core, servono per il gRPC su socket di dominio Unix con Tetragon, non hanno TLS e non escono sulla rete. ureq + rustls sono presenti solo in roamswitch-updater (scripts/audit_no_network.sh lo impone in CI). Non esiste un SDK di telemetria da nessuna parte.

Appendice B

table inet roamswitch {                 # Firewall del profilo (open / balanced / lockdown)
    chain input {
        type filter hook input priority 0; policy <accept|drop>;
        ct state established,related accept
        iif "lo" accept
        ip protocol icmp accept            # open ha policy accept e nessuna regola. balanced / lockdown hanno policy drop (ICMP e ICMPv6 sono consentiti solo in balanced)
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

table inet roamswitch {                 # Durante l'Air-Gap (isolamento) la stessa tabella viene ricreata. input / output hanno policy drop, è consentito solo lo (nemmeno le connessioni stabilite passano)
    chain input  { type filter hook input  priority -100; policy drop; iif "lo" accept }
    chain output { type filter hook output priority -100; policy drop; oif "lo" accept }
}

table inet roamswitch_linkguard {        # §4.5 — indipendente dalla tabella dei profili
    chain output {
        type filter hook output priority 0; policy accept;
        udp dport 443 reject with icmpx type port-unreachable   # solo quando block_quic è attivo (disattivato per impostazione predefinita)
        meta l4proto udp udp dport 53      queue num 92 bypass
        meta l4proto tcp tcp dport 53      queue num 92 bypass
        meta l4proto tcp tcp dport { 80, 443 } ct state new,established ct original packets 1-14 queue num 92 bypass   # solo i primi 14 pacchetti di ogni connessione vengono inviati all'ispezione
    }
}

table inet roamswitch_dev_guard {        # §4 Guardia anomalie porte / isolamento manuale delle porte, tabella separata
    chain input {
        type filter hook input priority -10; policy accept;
        iif != "lo" tcp dport { … } drop
    }
}

table inet roamswitch_portscan {        # rilevamento e blocco delle scansioni di porte (attivo per impostazione predefinita)
    chain block  { type filter hook input priority -200; policy accept; ip saddr @blocked_ips ct state new log … drop }
    chain detect { type filter hook input priority -100; policy accept; iifname != "lo" ct state new tcp flags syn log … }
}

table inet roamswitch_egress_block {     # blocklist in uscita degli IP malevoli (attiva per impostazione predefinita)
    chain output  { type filter hook output  priority -150; policy accept; ip daddr { … } log … drop }
    chain forward { type filter hook forward priority -150; policy accept; ip daddr { … } log … drop }
}

table inet roamswitch_docker_guard {     # contromisura al bypass del firewall da parte delle porte pubblicate di Docker (solo con il backend nftables nativo di Docker)
    chain forward { type filter hook forward priority -10; policy accept; … iifname { … } drop }
}

table inet roamswitch_isolate {          # isolamento per singolo processo dopo un rilevamento eBPF (per cgroup / UID)
    chain process_block { type filter hook output priority -100; policy accept; … drop }
}

table inet roamswitch_vpn {              # §4 Kill-switch del backend WireGuard
    chain output {
        type filter hook output priority -150; policy drop;
        oifname "lo" accept
        oifname "roamswitch" accept
        meta l4proto { icmp, ipv6-icmp } accept
        udp sport 68 udp dport 67 accept          # DHCP
        ip  daddr { <endpoint v4> } udp dport <port> accept
        ip6 daddr { <endpoint v6> } udp dport <port> accept
        ct state established,related accept
    }
    chain input  { type filter hook input priority -150; policy drop; … }  # simmetrico
}

table inet roamswitch_ts {               # §4 Kill-switch del backend Tailscale (più permissivo)
    chain output {
        type filter hook output priority -150; policy drop;
        oifname "lo" accept
        oifname "tailscale0" accept
        meta l4proto { icmp, ipv6-icmp } accept
        udp sport 68 udp dport 67 accept          # DHCP (anche il lato v6 è simmetrico)
        ip  daddr 100.64.0.0/10 accept            # tailnet CGNAT
        ip6 daddr fd7a:115c:a1e0::/48 accept
        ip  daddr 100.100.100.100 udp dport 53 accept   # MagicDNS
        ip  daddr 100.100.100.100 tcp dport 53 accept
        udp dport 3478 accept                     # STUN
        udp dport 41641 accept                    # Connessione diretta WireGuard
        tcp dport 443 accept                      # Fallback DERP
        ct state established,related accept
    }
    chain input  { type filter hook input priority -150; policy drop; … }  # simmetrico
}

Il pinning ARP/NDP del gateway / router IPv6 / risolutore DNS (§4) viene eseguito nella tabella dei vicini del kernel (ip neigh replace … nud permanent), non in nftables. Le voci appuntate vengono registrate in /var/lib/roamswitch/arp_lock.json.

Appendice C

Ogni affermazione contenuta in questo documento può essere verificata sul proprio computer. I comandi seguenti vengono eseguiti su un host con il pacchetto roamswitch installato.

C.1 Verifica del traffico in uscita

I binari demone/app/MCP non collegano alcuna libreria HTTP/TLS:

for b in roamswitch-daemon roamswitch-app roamswitch-mcp; do
  ldd "$(command -v $b)" | grep -iE 'ssl|crypto|nghttp|curl' && echo "  ^ in $b"
done
# → Nessun output. Lo stack HTTP/TLS è collegato staticamente solo a roamswitch-updater.

Il demone in esecuzione non contiene socket Internet:

sudo lsof -p "$(pgrep -x roamswitch-daem)" -a -i
# → Nessun output = zero connessioni Internet TCP/UDP
sudo lsof -p "$(pgrep -x roamswitch-daem)" -a -U | grep roamswitch
# → Solo /run/roamswitch/roamswitch.sock e events.sock (per la ricezione degli eventi di rilevamento eBPF) (la guardia dei collegamenti aggiunge un socket AF_NETLINK,
#   ma è un canale del kernel, non un socket internet)

L'unica uscita attribuibile a RoamSwitch è l'aggiornamento quotidiano:

watch -n 5 'sudo ss -tupn | grep -E "roamswitch-(daem|app|mcp)" || echo "(none)"'
# → Sempre "(none)". Nessuno di questi tre apre mai un socket internet.
systemctl list-timers roamswitch-update.timer
sudo systemctl start roamswitch-update.service
journalctl -u roamswitch-update.service -n 20 --no-pager
# → Esegue solo richieste HTTPS GET a lafine.net/updates/v1 (manifest, firma e dati aggiornati), poi termina. Se ClamAV è installato, freshclam si connette anche ai mirror di ClamAV.

Disabilitalo e conferma il silenzio totale:

sudo systemctl disable --now roamswitch-update.timer
# oppure modifica /var/lib/roamswitch/updates.json impostando "enabled": false
sudo strace -f -e trace=network -p "$(pgrep -x roamswitch-daem)" 2>&1 | grep -i 'connect('
# → Si connette solo ad AF_UNIX / AF_NETLINK. AF_INET / AF_INET6 non compaiono mai.

C.2 Verifica della difesa riproducibile (Docker Test Suite)

I vari meccanismi di difesa di RoamSwitch (blocco Air-Gap, autoriparazione dopo la cancellazione forzata del firewall, rilevamento del ransomware, /tmp noexec, Yama LSM, rilevamento degli omografi e altri) possono essere riprodotti in locale con la suite di test Docker pubblicata nel repository di supporto ufficiale (roamswitch-support). La suite opera solo su un network namespace indipendente e sui file all'interno del contenitore, quindi non influisce sulla rete e sui file dell'host. Poiché però viene eseguita con --privileged, le impostazioni condivise da tutto il kernel, come ptrace_scope di Yama e suid_dumpable, si riflettono anche sull'host e non tornano indietro eliminando il contenitore. Eseguila in un ambiente usa e getta.

# Recupera e riproduci la suite di verifica (verifica automatizzata all'interno di un container isolato, usando il pacchetto di distribuzione ufficiale)
git clone https://github.com/lafine1211/roamswitch-support.git
cd roamswitch-support/test/docker
docker build -t roamswitch-test .
docker run --rm --privileged roamswitch-test

Il contenitore di test recupera automaticamente il pacchetto di distribuzione ufficiale dal repository APT (lafine.net/apt), esegue le verifiche all'interno di namespace di rete e di mount isolati e ne giudica l'esito. La suite pubblica è composta da pentest.sh (i 7 scenari PENT-1–7) e selftest.sh (autotest distruttivi del demone: Air-Gap, autoriparazione, canary, rilevamento dei burst, Vault di quarantena e simili). Gli ID nelle tabelle seguenti sono numeri progressivi del registro delle verifiche e non corrispondono uno a uno ai controlli negli script. BadUSB (SP-6), le varie modalità della guardia dei collegamenti (famiglia SP-7), il blocco ARP e i kill-switch VPN (SP-8–10), le verifiche singole del rafforzamento del kernel (famiglia SP-11) e PENT-8 e 9 non fanno parte della suite pubblica; sono voci verificate con altre procedure, come una macchina reale, netns indipendenti e l'iniezione manuale di eventi.

(1) Difesa della rete autonoma e autoriparazione del gap aereo

IDScenarioMetodo di verificaCriterio di superamento
SP-3Abilitazione Air-Gap → caduta di uscitaInvia enable_air_gap IPCpolicy drop applicato al hook di uscita; tutto il traffico in uscita bloccato
SP-3bEliminazione forzata e autoriparazione del firewallElimina regole tramite nft delete table inet roamswitchL'autoguarigione anti-clobber ripristina le regole di caduta entro 5 secondi
SP-3cChiusura con errore al riavvio del demoneTermina il demone durante l'Air-Gap (in selftest.sh con SIGTERM tramite kill) e lo riavviaLa politica di rilascio in uscita persiste senza interruzioni durante il riavvio
SP-3dRecupero Air-Gap autorizzatoInvia disable_air_gap IPC autorizzatoPolitica di rilascio revocata, tutti i file marcatori rimossi, uscita ripristinata
SP-8Blocco ARP/NDP preventivoAbilita in un ambiente gateway netns isolato → invia ARP gratuito falsificatoLa voce vicina al gateway rimane PERMANENT; ARP falsificato ignorato
SP-9Kill-switch VPN (WireGuard)Server Live WireGuard in reti separate, importa .conf → vpn_upL'handshake viene completato fino a inet roamswitch_vpn; zero perdite sulla morte del tunnel
SP-10Kill-switch VPN (Tailscale)Stub tailscale CLI + reti separate, tailscale0 IFTCP/80 con testo in chiaro fuori tunnel e DNS locale bloccati; DERP e MagicDNS sono consentiti

(2) Rafforzamento del kernel e del supporto

IDScenarioMetodo di verificaCriterio di superamento
SP-11aRafforzamento del sistema di reteEsegui apply_kernel_sysctl_hardening IPCrp_filter=1, tcp_syncookies=1, accept_redirects=0 raggiungono i valori target
SP-11bProtezione della memoria e Yama LSMVerifica ptrace_scope e suid_dumpablekernel.yama.ptrace_scope=1 (snooping dei blocchi), fs.suid_dumpable=0 (nessun core dump)
SP-11c/tmp & /dev/shm difesa noexecEsegui apply_mount_hardening IPC/tmp e /dev/shm rimontati noexec,nosuid,nodev; esecuzione negata
SP-11dEsecuzione dell'utente legittimo preservataEsegui script nella directory home standard (~/)Funziona senza ostacoli senza interrompere i flussi di lavoro legittimi di sviluppatore/amministratore

(3) Difesa e protezione da malware e ransomware multilivello

IDScenarioMetodo di verificaCriterio di superamento
SP-1Rilevamento manomissione CanaryTronca o rinomina il file canary esca (.xlsx) in ~/Documents/La manomissione viene rilevata in pochi secondi, attivando immediatamente l'Air-Gap
SP-2Rilevamento burst di crittografia in blocco ransomwareUn singolo processo genera rapidamente più file ad alta entropiaProcesso di lavoro incriminato congelato con SIGSTOP; L'Air-Gap è scattato
SP-2bNessun falso positivo su shredEsegui shred -n 3 -u sui file di testLa corrispondenza nell'elenco consentito impedisce il rilevamento o il blocco di falsi positivi
SP-2cProcessi critici mai congelatiConvalida la logica di congelamento della destinazione durante il rilevamento del burstI demoni critici (dockerd, systemd, containerd) non vengono mai bloccati
SP-4La stringa di test EICAR è di sola notificaScrivi la stringa di test EICAR in ~/Downloads/Nessuna notifica, solo una voce nella cronologia delle notifiche; il file non viene mai messo in quarantena automatica
SP-4bConferma prima la firma del malware realeScrivi una firma reale all'esterno delle cartelle controllateGenera la finestra di dialogo di approvazione modale; messo in quarantena solo previa conferma dell'utente
SP-4cRafforzamento delle autorizzazioni di Quarantine VaultIspezionare le autorizzazioni della directory del vault e dei file di esempioDirectory del vault impostata su 0700, campioni in quarantena impostati su 0400
SP-4dRipristino sicuro del file originale IPC privilegiatoInvia restore_quarantine_file IPCFile ripristinato nel percorso originale con autorizzazioni utente (0644) e aggiunto alle esclusioni

(4) Protezione passiva dei collegamenti e difesa dal phishing

IDScenarioMetodo di verificaCriterio di superamento
SP-7mode = block applicazione rigorosaConnettiti agli host elencati nei feed o blocklistExtraEliminato al livello TLS (analisi SNI); gli host sicuri non elencati raggiungono HTTP 200
SP-7bmode = warn attesa con chiusura in caso di erroreConnetti all'host di avviso senza risposta dell'utente (timeout 8 s)Rilascia i pacchetti al timeout (fail-closed); consentito solo previa esplicita approvazione
SP-7cmode = off smontaggio completoDisabilita la protezione del collegamentoLa tabella inet roamswitch_linkguard è stata completamente disinstallata

(5) Mitigazione USB difettosa e controllo del dispositivo

IDScenarioMetodo di verificaCriterio di superamento
SP-6Nonno della tastiera connessaControlla le tastiere collegate all'avvio del demoneDispositivi esistenti mantenuti; furono presi solo i successivi hot-plug

(6) Voci di Autoverifica che simulano attacchi (PENT-1–7 nella suite Docker pubblica, PENT-8 e 9 in verifiche manuali separate)

IDVettore di attaccoDettagli della simulazioneCriteri di difesa/rilevamento
PENT-1Esfiltrazione C2Tentativo di uscita da HTTP/TCP a C2 sotto Air-GapTutti i pacchetti vengono rilasciati su netfilter; zero perdite
PENT-1bCancellazione forzata del firmwareElimina regole con nft delete table inet roamswitchL'autoguarigione anti-clobber ripristina le regole in pochi secondi
PENT-1cRipristino sicuro del traferroProblema autenticato disable_air_gap IPCRegole di rilascio rimosse; marcatori cancellati; connettività ripristinata
PENT-2/tmp Porta sul retroEsegui lo script backdoor rilasciato in /tmpNegato dalla protezione del montaggio noexec (Permission denied)
PENT-2b/dev/shm Memoria condivisaEsegui il payload binario inserito in /dev/shmBloccato dalla difesa della cavalcatura noexec
PENT-2cPreservazione dello spazio utenteEsegui script nella directory utente legittima (/home/tester/)Esecuzione senza ostacoli; zero interruzioni del flusso di lavoro
PENT-3ptrace Snooping della memoriaPTRACE_ATTACH a cui mirare per estrarre segreti/tokenNegato da Yama LSM kernel.yama.ptrace_scope=1
PENT-3bRaschiatura segreta del core dumpAttiva l'arresto anomalo del processo per ottenere il core dump della memoriaImpedito da fs.suid_dumpable=0
PENT-4Manomissione delle CanarieFile esca per canarini zero-out in ~/Documents/Rilevamento in tempo reale di anomalie dimensionali e modifiche dell'hash
PENT-4bPulizia alla rinfusa delle CanarieEliminazione di massa dei file esca dei canariniLa scomparsa del file è stata rilevata immediatamente, sollevando un incidente
PENT-5Spoofing dell'indirizzo IPTrasmettere pacchetti con IP di origine falsificatoEliminato da rp_filter=1 (filtro percorso inverso)
PENT-5bSYN Flood DoSConnessioni TCP semiaperte inondateTabella degli stati protetta da tcp_syncookies=1
PENT-5cDirottamento del percorso ICMPIniettare pacchetti di reindirizzamento ICMP falsiIgnorato a causa di accept_redirects=0
PENT-6Phishing con omografo cirillicoPassare a gооgle.com (omografo cirillico)Identificato da LinkGuard, punteggio 40 (PERICOLOSO/blocco)
PENT-6bTraffico IP non elaborato e testo non crittografatoConnessione in uscita diretta all'IP non elaboratoAccuratamente contrassegnato come fattore di rischio per la sicurezza
PENT-7Perdita della chiave API OpenAIEsporre il token sk-proj-... nel codice o nei logRilevato tramite analisi entropica di Shannon e mascherato
PENT-7bPerdita di token GitHubEsporre ghp_... token personale nel codiceCorrispondente al modello di token e alla verifica entropica
PENT-8Prelazione LPE (Frag Gap) del kernelNon privilegiato unshare -U -r (tentativo CVE-2026-53362)Nella misura della v1.1.0, rifiutato immediatamente con Operation not permitted da user.max_user_namespaces = 0. L'attuale Client Edition non applica questo rifiuto per impostazione predefinita (auto_enable_userns_restriction in guard.yaml è disattivato per impostazione predefinita; la Server Edition lo applica sempre)
PENT-9Sensore di minaccia eBPF e blocco del processoAvviso critico/di emergenza inviato tramite la presa Falco eBPFProcesso congelato tramite SIGSTOP entro millisecondi (stato: Ts); Air-Gap schierato

Tipo di verifiche: nella tabella precedente, PENT-2/2b (noexec), PENT-3/3b (Yama, core dump) e PENT-5/5b/5c (rp_filter, SYN cookie, redirect ICMP) sono voci che confermano che una funzione standard del kernel o di mount configurata da RoamSwitch è effettivamente attiva. Anche PENT-8 (divieto degli user namespace) è dello stesso tipo, ma è un dato registrato alla versione v1.1.0 e l'attuale Client Edition non lo applica per impostazione predefinita. Le voci in cui la logica propria di rilevamento e contenimento di RoamSwitch è messa alla prova dal lato dell'attaccante sono PENT-1/1b/1c (Air-Gap e autoriparazione), PENT-4/4b (canary), PENT-6/6b (guardia dei collegamenti), PENT-7/7b (rilevamento dei segreti) e PENT-9 (congelamento e contenimento dopo un rilevamento eBPF). PENT-2c conferma che il lavoro normale non viene compromesso.

(7) Riepilogo delle autoverifiche (senza Falco [standalone] e con Falco [integrazione eBPF])

Riepilogo dei risultati dell'autoverifica completa, che simula attacchi, eseguita in un ambiente di verifica Docker isolato (Target: Ubuntu 24.04 + RoamSwitch Client Edition, Attacker: Nmap / curl / hping3 ecc.). La tabella seguente riporta i risultati misurati su Client v1.1.0 il 2026-09-06. Il 2026-09-25 una versione corretta, compilata dai sorgenti della serie 1.10, ha superato 11 voci su 11 della suite per la versione client (non è una versione pubblica; vedi audit/RESULTS-PENTEST-2026-09-25.ja.md nel repository pubblico).

Categoria di verificaSenza Falco (Standalone Hardened)Con Falco (eBPF potenziato)Verdetto della difesa
Resistenza scansione porta esterna (Nmap SYN)PASS (Filtrato al 100% / DROP in Lockdown)PASS (Filtrato al 100% / DROP in Lockdown)Invisibilità esterna totale
Interruzione accesso diretto esterno (TCP/80, 3306)PASS (Eliminazione timeout/risposta zero)PASS (Eliminazione timeout/risposta zero)Esposizione in entrata zero
Mitigazione LPE del kernel (Frag Gap)PASS (misurazione della v1.1.0; in un ambiente con user.max_user_namespaces = 0 viene bloccato unshare. Nell'attuale Client Edition è disattivato per impostazione predefinita)PASS (avviso eBPF + SIGSTOP congelamento + Air-Gap)Difesa in profondità verificata
Rilevamento porta non autorizzata (anomalia)PASS (0.0.0.0 vincolo contrassegnato con rischio ALTO)PASS (0.0.0.0 vincolo contrassegnato con rischio ALTO)100% rilevato e guidato
Prevenzione del sovraccarico del registro e del sovraccarico della CPUImpronta zero log (disinstallato)PASS (riduzione del rumore del 95% / buffer da 2 MB / limite di 50 MB)Ottimizzato per desktop

C.3 Limitazioni note

LimitazioneImpattoMitigazione
Su alcuni kernel, FAN_MARK_MOUNT su tmpfs (/tmp) non fornisce eventile scritture di file in /tmp non sono monitorate da fanotify/tmp è temporaneo e non è una cartella utente monitorata; il ransomware prende di mira ~/. Gli errori di contrassegno vengono registrati esplicitamente
Nessuna notifica di consegna su contenitori overlayfs/tmpfsIl rilevamento del burst di entropia all'interno del contenitore non può essere convalidatovalidato su hardware reale (ext4); il cablaggio salta automaticamente tramite una sonda
Le operazioni nftables di RoamSwitch condividono lo spazio netfilter con iptables-nft di DockerLe operazioni delle regole RoamSwitch possono interferire con le regole NAT di Dockerè isolato in tabelle dedicate (inet roamswitch e le altre tabelle roamswitch_*), ma le operazioni globali come nft flush ruleset vanno evitate
La protezione dei collegamenti vede il DoH di un browser solo tramite TLS SNI; Anche Encrypted Client Hello (ECH) nasconde questouna volta che ECH è stato ampiamente distribuito, una connessione che utilizza ECH a un sito che supporta ECH non è identificabile dal nome hostla blocklist locale e l'euristica agiscono ancora su DNS/HTTP e TLS non ECH; La protezione dalle minacce DNS (§4.5, attiva per impostazione predefinita) copre il percorso del risolutore
Se in updates.json il valore enabled è false, la freschezza del feed dipende dalla cadenza di rilasciocon gli aggiornamenti disattivati, il feed è aggiornato solo quanto l'ultimo apt upgradei rilevamenti di gravità più elevata (omografo del marchio) non necessitano di feed; riattiva o abilita il DNS di protezione dalle minacce, per una copertura sempre aggiornata
Questo documento riflette l'implementazione così come si presenta in RoamSwitch for Linux v1.11.1 (hardening del kernel contro gli LPE, protezione runtime eBPF, blocco SIGSTOP dei processi attaccanti in millisecondi, ottimizzazione a basso overhead di Falco, quarantena Vault di monitoraggio in tempo reale e ripristino IPC privilegiato all'originale, autoguarigione Air-Gap, rilevamento di tunneling DNS, acquisizione forense delle prove, honeytoken, tracciamento del pass_age nella verifica delle vulnerabilità, mappatura NIST CSF/CIS Controls, rivalutazione della diagnosi, audit della configurazione MCP). Consulta il CHANGELOG di roamswitch-support per la cronologia dettagliata delle modifiche. Per le informazioni più recenti, consulta roamswitch.com/linux.