§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).
| Principio | Dettaglio |
|---|---|
| Telemetria zero | Per 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 aggiornamenti | L'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 locale | Ogni 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 minimo | Le 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 chiuso | Air-Gap (isolamento di emergenza) sopravvive al riavvio del demone e viene ripristinato in pochi secondi se le sue regole vengono eliminate esternamente. |
| Licenza offline | Le 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
| Componente | Privilegio | Ruolo |
|---|---|---|
| roamswitch-daemon | radice (systemd, Type=notify + WatchdogSec=30) | Tutte operazioni privilegiate. In qualità di scrittore singolo, controlla nftables, DNS e unità systemd. |
| roamswitch-app | utente di accesso | GUI GTK3 + vassoio AppIndicator. Solo modifica e visualizzazione della configurazione; ogni azione privilegiata passa attraverso IPC. |
| roamswitch (CLI) | utente di accesso | Front-end a guscio sottile che utilizza lo stesso socket IPC. |
| roamswitch-mcp | processo generato dal client AI | stdio 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»).
| Profilo | Situazione | comportamento di nftables |
|---|---|---|
| aperto (attendibile) | Una rete domestica o aziendale registrata | policy accept. Traffico locale consentito. |
| bilanciato | Non registrato ma configurato come relativamente sicuro | Rifiuta 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 registrata | Tutti 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
SIGSTOPe 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 oroamswitch 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 cheairgap_degraded_lift_rfkillsia 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.jsone 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_linkguardaggancia il percorsooutpute 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 suAF_NETLINK; il demone non associa alcun socket e non genera alcun pacchetto. Le regole della coda trasportanobypass, 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
Hostin 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 marchio | Altri segnali sospetti |
|---|---|---|
off | non ispezionato | non ispezionato |
warn | connessione mantenuta fino a 8 s + una richiesta di approvazione consenti/blocca | nessuna 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
.confda 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
.confe lo memorizza in/etc/wireguard/roamswitch.conf(0600, interfacciaroamswitch). Non può vivere altrove: il profilo AppArmorwg-quicksu Ubuntu 24.04+ / Debian 13 nega awg-quickdi leggere un.confall'esterno di/etc/wireguard/— anche come root. Una configurazione a tunnel diviso (AllowedIPsnon0.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 aoifname), 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 tramitegetent ahosts), quindi non vi è alcuna finestra di perdita. Sewg-quick upfallisce, il kill switch rimane alzato.
(B) Backend Tailscale
- Per gli utenti che già utilizzano Tailscale. RoamSwitch not esegue
tailscale up/ login / installtailscaled— leggetailscale status --jsoned eseguetailscale 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 CGNAT100.64.0.0/10efd7a: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 conip -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 esclusiscan_exclusionsimpostato dall'utente, il Vault di quarantena,/dev/shme i file in memoria o già eliminati, posizioni come/proc,/syse/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/tmpo/homeche 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/tmpe/dev/shmcon marcature a livello di file system tramiteFAN_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) eopenè trattato come notifica (fanotify_open_permripristina 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_DENYper 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 unexecveeffettivo riceveFAN_DENYe solo quandopre_exec_blocking_enabledè attivo. Alla scadenza dell'approvazione si ha un fail-open. Il monitoraggio dei download (per impostazione predefinita~/Downloadse~/Desktop, ampliabile conwatched_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_exclusionsconfigurato 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 con0700; ogni campione appartiene a root con0400(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 file0644da 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: ritardareFAN_OPEN_EXEC_PERMbloccherebbe 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~/Downloadse~/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,.debe.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) eseguefreshclamper 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_logcome 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 conroamswitch events search|tree|export|verify; gli strumenti MCP in sola lettura per i client IA sonosearch_exec_eventseget_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 comesnapperetimeshift(EXR-008, 1.10.11), la ricerca conwhicho 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 esplicitamenteexec_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 diuser.max_user_namespacesekernel.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 è impostatouser.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 impostakernel.unprivileged_userns_clone = 0solo finché si è su una rete non attendibile viene applicata soltanto se la abiliti esplicitamente conadaptive_security.untrusted_network_actions.auto_enable_userns_restrictioninguard.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(o2) impedisce ai processi non autorizzati di collegarsi tramiteptraceo 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 = 0impedisce ai processi sensibili che causano arresti anomali di scrivere dump della memoria in testo normale su disco. - __X6
/tmp&/dev/shmnoexecMount Defense — Per prevenire attacchi drop-and-execute del payload, RoamSwitch rimonta/tmpe/dev/shmconnoexec,nosuid,nodevquando 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 inviaSIGSTOPal PID interessato per fermare completamente il processo (stato:Ts), esegue un «isolamento mirato» che blocca nella tabellaroamswitch_isolatesolo 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 unpolicy drop(Air-Gap) nella tabellainet roamswitche, se il PID è noto, viene inviato ancheSIGSTOP. Gli eventi inferiori aCriticalnon 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 diinstall.shnon installano Falco e non collocano nemmeno questo profilo (lo collocano il pacchetto della Server Edition einstall.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.
- Filtro gravità (
- 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
0in/sys/bus/usb/devices/*/authorizedper 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 diauthorized=0su qualsiasi dispositivo che espone un'interfaccia HID (bInterfaceClass 03) e il demone autorizza nuovamente qualsiasi dispositivo HIDauthorized==0all'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
EVIOCGRABsu/dev/input/eventNdi quel dispositivo (solo i nodi con un gestorekbdin/proc/bus/input/devices, corrispondente a VID:PID). Il nodo del mouse è intatto. Questo è l'equivalente Linux di macOSCGEventTap; 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.jsone 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 abilitandousb_keyboard_timeout_releasesi 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).
| Categoria | Elementi di esempio |
|---|---|
| Disco/avvio | Crittografia LUKS/dm-crypt, UEFI Secure Boot |
| Controllo degli accessi | LSM (AppArmor/SELinux), controllo SSH/sudo configurazione |
| Indurimento del nocciolo | rafforzamento 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 |
| Aggiornamenti | configurazione automatica dell'aggiornamento di sicurezza |
| Rete | Monitoraggio spoofing ARP, stato di blocco ARP/NDP del gateway, controllo delle porte esposte, potenza della crittografia Wi-Fi |
| Malware | ClamAV (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) |
| Navigatore | Impostazioni di Navigazione sicura Firefox/Chrome/Chromium |
| DNS/USB | DNS 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).
| Strumento | Scopo |
|---|---|
| get_security_report | Valutazione in 27 elementi, punteggio, raccomandazioni |
| verify_security_findings | Rivaluta 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_configs | Legge 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_setup | Legge 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_esposte | porte esposte e il relativo stato di blocco del firewall |
| get_guard_status | livello di protezione attuale e stato del modulo di difesa |
| audit_url_sicurezza | analisi euristica della sicurezza dell'URL (non recupera il target) |
| audit_secrets | rilevamento di chiavi/segreti API nel testo (non trasmette il testo) |
| audit_security_logs | aggregazione di log journald/auth locali |
| get_quarantine_status / get_canary_status | quarantena e stato delle Canarie |
| get_app_help | ricerca della base di conoscenza integrata |
| run_active_vuln_scan | Verifica 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_scan | Confronta 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_languages | Confronta 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_history | Restituisce, per ciascuna sonda registrata da scan-vulns --confirm, il numero di giorni trascorsi dall'ultima esecuzione e conferma effettiva (pass_age_days). |
| verify_fim | Ricalcola 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_status | Restituisce 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_incidents | Restituisce 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_incidents | Restituisce 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_incidents | Restituisce 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_incidents | Restituisce 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_history | Restituisce 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_timeline | Unisce 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
- 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 seroamswitch-core,roamswitch-daemon,roamswitch-app,roamswitch-mcp,roamswitchkit(o i crate CLI e Server) includono uno stack client HTTP/TLS comereqwest/ureq/rustls/native-tls/openssl-sys, o se un file.rsesterno aroamswitch-updatere ai file citati nel 9.4 usaTcpStream/TcpListener/UdpSocket/to_socket_addrs/lookup_host. L'unica autorizzazione aggiuntiva riguardatonic/hyper, consentiti solo inroamswitch-coreper gRPC su socket di dominio UNIX verso il sensore eBPF (Tetragon); lo stesso script vieta di abbinarli a TLS. - Ispezione dell'albero delle dipendenze: è stato cercato l'intero
Cargo.lockper SDK di telemetria/analisi/segnalazione di arresti anomali (sentry,opentelemetry, …). roamswitch-updaterreview: 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.- Enumerazione sottoprocessi: ogni sito di chiamata
Command::new(...)è stato esaminato.
9.2 Risultati
| Elemento di controllo | Risultato |
|---|---|
| Pila HTTP/TLS | Presente solo in roamswitch-updater (ureq + rustls). Non in core / daemon / app / mcp / kit |
| SDK di telemetria/analisi/segnalazione di arresti anomali | Non presente da nessuna parte |
TcpStream / TcpListener / UdpSocket | Solo in roamswitch-updater e nei quattro file delle funzioni opzionali elencati nel 9.4 |
| API socket in uso | AF_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 quotidiano | Un 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 licenza | Verifica della firma Ed25519 offline. Nessuna comunicazione con un server di attivazione |
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):
| # | Operazione | Destinazione | Innesco | Cosa viene inviato |
|---|---|---|---|---|
| 1 | Aggiornamento 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 è disabilitato | un 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 |
| 1a | freshclam (aggiornamento di ClamAV), richiamato dall'aggiornamento quotidiano quando ClamAV è installato | il CDN del progetto ClamAV | Come 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 |
| 2 | ping -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 |
| 3 | avviando 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 |
| 4 | risolverectl 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 sentinella | Modifica 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) |
| 5 | aggiornamenti del gestore pacchetti | i repository apt / dnf / zypper / pacman della distribuzione | quando 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 |
| 6 | VPN tunnel (§4) — Backend WireGuard: il tunnel wg-quick crea / Backend Tailscale: tailscale set --exit-node instrada tutto il traffico attraverso il nodo di uscita | l'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 predefinita | traffico 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_clientdi sistema e il webhook per ilcurldi 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 disco0600(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 esegueroamswitch 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 ilcurldi 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'openssldi 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 verso127.0.0.1per 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
GETdihttps://lafine.net/apt/dists/stable/main/<arch>/Packagescon ilcurldi 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> --confirmfunziona solo se l'impostazionenpm_audit_signatures_enabled(false per impostazione predefinita) è attiva e l'utente lo esegue con--confirm; è quindinpmstesso 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.jsone 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-Updaterfisso 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 anche1.1.1.2di 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, impostadns_enabledsufalse. - 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
| Edizione | Per | Prezzo | Licenza |
|---|---|---|---|
| Edizione comunitaria | Linux, uso personale e aziendale (nessun limite al numero di dispositivi) | Gratuito | Freeware proprietario (EULA in bundle) |
| Business (pianificato) | Gestione flotta, distribuzione polizze, apt interno firmato, SLA per organizzazioni | Pagato | Licenza 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
| Canale | Dettaglio |
|---|---|
| APP | https://lafine.net/apt (stable main). Release firmato con una chiave RSA-4096 |
| giri al minuto | https://lafine.net/rpm (Fedora / openSUSE separato). repomd.xml Firmato GPG |
| AUR | roamswitch-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)) |
| tarball | Su 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:
GET https://lafine.net/updates/v1/manifeste la sua firma staccata.- 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.
- 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.
- 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. - Se ClamAV è installato, invoca
freshclam. - 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 partesystemd, 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
| Cassa | Scopo | Rete |
|---|---|---|
| serde/serde_json | serializzazione di configurazione e IPC | nessuno |
| Tokyo | runtime 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-abbonato | registrazione (locale/journald) | nessuno |
| questo errore / comunque | gestione degli errori | nessuno |
| espressione regolare | analisi delle stringhe (diagnostica, ridimensionamento CSS) | nessuno |
| URL | Analisi URL (euristica audit_url_safety) | nessuno (non viene recuperato) |
| crono | gestione data/ora | nessuno |
| ed25519-dalek | verifica 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/sha2 | token di licenza, hash canary | nessuno |
| libc | chown / kill / chiamate di sistema | nessuno |
| idna | decodifica host IDN/punycode per il rilevamento degli omografi | nessuno |
| nfq (solo demone) | Attacchi NFQUEUE per la protezione del collegamento: pure Rust, AF_NETLINK | nessuno (canale del verdetto, nessuna uscita) |
| aperto | chiama xdg-open per «Apri le note di rilascio» e per aprire i collegamenti alla EULA e alla pagina di supporto | il processo stesso non comunica |
| GTK3/ksni (solo app) | GUI e vassoio | nessuno |
| serde_yaml | lettura di /etc/roamswitch/guard.yaml (core) | nessuno |
| toml | analisi di Cargo.lock / poetry.lock (scansione CVE dei pacchetti di linguaggio, core) | nessuno |
| md-5 | hash per le impronte JA3 del Link Guard (non usato per autenticazione o integrità, core) | nessuno |
| bs58 | controllo Base58 delle chiavi private di criptovalute (rilevamento di segreti, core) | nessuno |
| rand | generazione della chiave del dispositivo per l'abbinamento opt-in con il Sensor (core) | nessuno |
| tonic / tonic-prost / prost / tower / hyper-util | gRPC verso il sensore Tetragon; solo socket di dominio Unix, senza TLS (core) | nessuno (solo socket Unix) |
| flate2 | decompressione gzip della mappa CVE dei pacchetti della famiglia RHEL (solo updater; Rust puro) | nessuno (solo decompressione) |
| inotify | monitoraggio degli accessi agli honeytoken, ossia le esche di credenziali (roamswitch-honeytokens) | nessuno |
| aya | loader del boot gate XDP sperimentale (roamswitch-xdp-gate, referenziato dal demone) | nessuno (solo bpf() / netlink) |
| urlencoding / gdk / gdk-pixbuf / glib / pango | decodifica 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 |
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
| ID | Scenario | Metodo di verifica | Criterio di superamento |
|---|---|---|---|
| SP-3 | Abilitazione Air-Gap → caduta di uscita | Invia enable_air_gap IPC | policy drop applicato al hook di uscita; tutto il traffico in uscita bloccato |
| SP-3b | Eliminazione forzata e autoriparazione del firewall | Elimina regole tramite nft delete table inet roamswitch | L'autoguarigione anti-clobber ripristina le regole di caduta entro 5 secondi |
| SP-3c | Chiusura con errore al riavvio del demone | Termina il demone durante l'Air-Gap (in selftest.sh con SIGTERM tramite kill) e lo riavvia | La politica di rilascio in uscita persiste senza interruzioni durante il riavvio |
| SP-3d | Recupero Air-Gap autorizzato | Invia disable_air_gap IPC autorizzato | Politica di rilascio revocata, tutti i file marcatori rimossi, uscita ripristinata |
| SP-8 | Blocco ARP/NDP preventivo | Abilita in un ambiente gateway netns isolato → invia ARP gratuito falsificato | La voce vicina al gateway rimane PERMANENT; ARP falsificato ignorato |
| SP-9 | Kill-switch VPN (WireGuard) | Server Live WireGuard in reti separate, importa .conf → vpn_up | L'handshake viene completato fino a inet roamswitch_vpn; zero perdite sulla morte del tunnel |
| SP-10 | Kill-switch VPN (Tailscale) | Stub tailscale CLI + reti separate, tailscale0 IF | TCP/80 con testo in chiaro fuori tunnel e DNS locale bloccati; DERP e MagicDNS sono consentiti |
(2) Rafforzamento del kernel e del supporto
| ID | Scenario | Metodo di verifica | Criterio di superamento |
|---|---|---|---|
| SP-11a | Rafforzamento del sistema di rete | Esegui apply_kernel_sysctl_hardening IPC | rp_filter=1, tcp_syncookies=1, accept_redirects=0 raggiungono i valori target |
| SP-11b | Protezione della memoria e Yama LSM | Verifica ptrace_scope e suid_dumpable | kernel.yama.ptrace_scope=1 (snooping dei blocchi), fs.suid_dumpable=0 (nessun core dump) |
| SP-11c | /tmp & /dev/shm difesa noexec | Esegui apply_mount_hardening IPC | /tmp e /dev/shm rimontati noexec,nosuid,nodev; esecuzione negata |
| SP-11d | Esecuzione dell'utente legittimo preservata | Esegui 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
| ID | Scenario | Metodo di verifica | Criterio di superamento |
|---|---|---|---|
| SP-1 | Rilevamento manomissione Canary | Tronca o rinomina il file canary esca (.xlsx) in ~/Documents/ | La manomissione viene rilevata in pochi secondi, attivando immediatamente l'Air-Gap |
| SP-2 | Rilevamento burst di crittografia in blocco ransomware | Un singolo processo genera rapidamente più file ad alta entropia | Processo di lavoro incriminato congelato con SIGSTOP; L'Air-Gap è scattato |
| SP-2b | Nessun falso positivo su shred | Esegui shred -n 3 -u sui file di test | La corrispondenza nell'elenco consentito impedisce il rilevamento o il blocco di falsi positivi |
| SP-2c | Processi critici mai congelati | Convalida la logica di congelamento della destinazione durante il rilevamento del burst | I demoni critici (dockerd, systemd, containerd) non vengono mai bloccati |
| SP-4 | La stringa di test EICAR è di sola notifica | Scrivi 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-4b | Conferma prima la firma del malware reale | Scrivi una firma reale all'esterno delle cartelle controllate | Genera la finestra di dialogo di approvazione modale; messo in quarantena solo previa conferma dell'utente |
| SP-4c | Rafforzamento delle autorizzazioni di Quarantine Vault | Ispezionare le autorizzazioni della directory del vault e dei file di esempio | Directory del vault impostata su 0700, campioni in quarantena impostati su 0400 |
| SP-4d | Ripristino sicuro del file originale IPC privilegiato | Invia restore_quarantine_file IPC | File ripristinato nel percorso originale con autorizzazioni utente (0644) e aggiunto alle esclusioni |
(4) Protezione passiva dei collegamenti e difesa dal phishing
| ID | Scenario | Metodo di verifica | Criterio di superamento |
|---|---|---|---|
| SP-7 | mode = block applicazione rigorosa | Connettiti agli host elencati nei feed o blocklistExtra | Eliminato al livello TLS (analisi SNI); gli host sicuri non elencati raggiungono HTTP 200 |
| SP-7b | mode = warn attesa con chiusura in caso di errore | Connetti all'host di avviso senza risposta dell'utente (timeout 8 s) | Rilascia i pacchetti al timeout (fail-closed); consentito solo previa esplicita approvazione |
| SP-7c | mode = off smontaggio completo | Disabilita la protezione del collegamento | La tabella inet roamswitch_linkguard è stata completamente disinstallata |
(5) Mitigazione USB difettosa e controllo del dispositivo
| ID | Scenario | Metodo di verifica | Criterio di superamento |
|---|---|---|---|
| SP-6 | Nonno della tastiera connessa | Controlla le tastiere collegate all'avvio del demone | Dispositivi 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)
| ID | Vettore di attacco | Dettagli della simulazione | Criteri di difesa/rilevamento |
|---|---|---|---|
| PENT-1 | Esfiltrazione C2 | Tentativo di uscita da HTTP/TCP a C2 sotto Air-Gap | Tutti i pacchetti vengono rilasciati su netfilter; zero perdite |
| PENT-1b | Cancellazione forzata del firmware | Elimina regole con nft delete table inet roamswitch | L'autoguarigione anti-clobber ripristina le regole in pochi secondi |
| PENT-1c | Ripristino sicuro del traferro | Problema autenticato disable_air_gap IPC | Regole di rilascio rimosse; marcatori cancellati; connettività ripristinata |
| PENT-2 | /tmp Porta sul retro | Esegui lo script backdoor rilasciato in /tmp | Negato dalla protezione del montaggio noexec (Permission denied) |
| PENT-2b | /dev/shm Memoria condivisa | Esegui il payload binario inserito in /dev/shm | Bloccato dalla difesa della cavalcatura noexec |
| PENT-2c | Preservazione dello spazio utente | Esegui script nella directory utente legittima (/home/tester/) | Esecuzione senza ostacoli; zero interruzioni del flusso di lavoro |
| PENT-3 | ptrace Snooping della memoria | PTRACE_ATTACH a cui mirare per estrarre segreti/token | Negato da Yama LSM kernel.yama.ptrace_scope=1 |
| PENT-3b | Raschiatura segreta del core dump | Attiva l'arresto anomalo del processo per ottenere il core dump della memoria | Impedito da fs.suid_dumpable=0 |
| PENT-4 | Manomissione delle Canarie | File esca per canarini zero-out in ~/Documents/ | Rilevamento in tempo reale di anomalie dimensionali e modifiche dell'hash |
| PENT-4b | Pulizia alla rinfusa delle Canarie | Eliminazione di massa dei file esca dei canarini | La scomparsa del file è stata rilevata immediatamente, sollevando un incidente |
| PENT-5 | Spoofing dell'indirizzo IP | Trasmettere pacchetti con IP di origine falsificato | Eliminato da rp_filter=1 (filtro percorso inverso) |
| PENT-5b | SYN Flood DoS | Connessioni TCP semiaperte inondate | Tabella degli stati protetta da tcp_syncookies=1 |
| PENT-5c | Dirottamento del percorso ICMP | Iniettare pacchetti di reindirizzamento ICMP falsi | Ignorato a causa di accept_redirects=0 |
| PENT-6 | Phishing con omografo cirillico | Passare a gооgle.com (omografo cirillico) | Identificato da LinkGuard, punteggio 40 (PERICOLOSO/blocco) |
| PENT-6b | Traffico IP non elaborato e testo non crittografato | Connessione in uscita diretta all'IP non elaborato | Accuratamente contrassegnato come fattore di rischio per la sicurezza |
| PENT-7 | Perdita della chiave API OpenAI | Esporre il token sk-proj-... nel codice o nei log | Rilevato tramite analisi entropica di Shannon e mascherato |
| PENT-7b | Perdita di token GitHub | Esporre ghp_... token personale nel codice | Corrispondente al modello di token e alla verifica entropica |
| PENT-8 | Prelazione LPE (Frag Gap) del kernel | Non 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-9 | Sensore di minaccia eBPF e blocco del processo | Avviso critico/di emergenza inviato tramite la presa Falco eBPF | Processo 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 verifica | Senza 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 CPU | Impronta zero log (disinstallato) | PASS (riduzione del rumore del 95% / buffer da 2 MB / limite di 50 MB) | Ottimizzato per desktop |
C.3 Limitazioni note
| Limitazione | Impatto | Mitigazione |
|---|---|---|
Su alcuni kernel, FAN_MARK_MOUNT su tmpfs (/tmp) non fornisce eventi | le 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/tmpfs | Il rilevamento del burst di entropia all'interno del contenitore non può essere convalidato | validato su hardware reale (ext4); il cablaggio salta automaticamente tramite una sonda |
Le operazioni nftables di RoamSwitch condividono lo spazio netfilter con iptables-nft di Docker | Le 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 questo | una volta che ECH è stato ampiamente distribuito, una connessione che utilizza ECH a un sito che supporta ECH non è identificabile dal nome host | la 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 rilascio | con gli aggiornamenti disattivati, il feed è aggiornato solo quanto l'ultimo apt upgrade | i 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 |