§1
RoamSwitch para Linux é um aplicativo residente de segurança de rede e diagnóstico de sistema que traz o modelo de defesa de rede de confiança zero do macOS RoamSwitch para Linux (ambientes systemd + nftables).
Esta edição tem como alvo máquinas clientes (estações de trabalho, laptops, computadores de placa única) e assume um comportamento autônomo que segue a máquina conforme ela se move entre redes. Uma edição de servidor reforçado/infraestrutura headless (RoamSwitch Server Edition — negação padrão sempre ativa, integridade FIM de caminho crítico, webhooks remotos e aplicação de política estática) é entregue como uma construção de servidor dedicado.
Ele identifica a rede Wi-Fi/com fio conectada por seu endereço MAC de gateway e alterna autonomamente o perfil de firewall nftables ativo. Além disso, ele fornece três camadas de defesa man-in-the-middle em redes não confiáveis - um túnel VPN + kill-switch (WireGuard ou Tailscale, a defesa primária), um bloqueio ARP/NDP preventivo e ARP spoof Detecção - além de detecção de ransomware comportamental (fanotify + entropia de Shannon + canários), um protetor de link passivo que sinaliza ou bloqueia conexões de phishing (NFQUEUE + SNI), Port Anomaly Guard, mitigação de BadUSB, preempção de escalação de privilégio local (LPE) kernel (mitigação Frag Gap CVE-2026-53362), eBPF sensor de ameaça de tempo de execução (Falco / Tetragon) integração com congelamento de processo em sub-segundos (SIGSTOP) e rede Air-Gap isolamento, uma avaliação de integridade de segurança de 24 itens e um servidor Model Context Protocol (MCP) integrado.
Este whitepaper descreve a arquitetura, o modelo de ameaça e o design de cada mecanismo de defesa — e, em particular, como o princípio do “processamento totalmente local; a única coisa enviada pela máquina é uma solicitação de dados de detecção assinados, que não carregam nada sobre você” é garantido no nível do código. Evita linguagem de marketing; as declarações podem ser verificadas em relação ao comportamento real do pacote distribuído (binário) e à autoverificação no apêndice. RoamSwitch para Linux é freeware proprietário e seu código-fonte não é publicado (§11).
| Princípio | Detalhe |
|---|---|
| Zero telemetria | O produto não envia nada sobre você ou sua máquina para ninguém. Não há telemetria, análises, relatórios de falhas, métricas de uso, configuração remota ou servidor de ativação de licença. |
| Atualizações somente para recebimento | A única solicitação de saída que o produto faz é um GET diário de um manifesto fixo e assinado de lafine.net/updates/v1 – a lista de sites fraudulentos para o link guard, a versão da assinatura ClamAV, a versão mais recente do aplicativo, os mapas de CVE de pacotes (Homebrew/npm/PyPI/crates.io/RubyGems/Packagist/Go/Maven), o mapa de CVE da verificação ativa de vulnerabilidades, e a lista de nomes de pacotes npm populares usada para a deteção de typosquatting. Ele não contém nenhuma string de consulta, nenhum cookie, nenhum cabeçalho de identificação e nenhuma informação sobre você. updates.enabled = false desliga totalmente; o produto então funciona com dados agrupados sem nenhum contato externo. |
| Processamento totalmente local | Cada avaliação, detecção e auditoria são executadas até a conclusão na máquina local. A análise de segurança de URL e a verificação secreta avaliam sua entrada heuristicamente, sem transmiti-la a lugar nenhum. |
| Privilégio mínimo | As operações privilegiadas estão confinadas a um daemon root; a UI, CLI e servidor MCP os solicitam por meio de um IPC de soquete de domínio Unix. O daemon não abre nenhum soquete TCP/UDP; o link guard usa apenas o canal de veredicto AF_NETLINK NFQUEUE (kernel ↔ espaço do usuário, sem saída de pacote). |
| Falha fechada | Air-Gap (isolamento de emergência) sobrevive a uma reinicialização do daemon e é reafirmado em segundos se suas regras forem excluídas externamente. |
| Licenciamento off-line | As licenças Pro/Business são verificadas exclusivamente pela verificação de assinatura Ed25519. Nenhuma chamada para um servidor de ativação ocorre. |
§2
RoamSwitch para Linux tem como alvo principal o laptop/estação de trabalho móvel que transita entre redes não confiáveis.
Atacantes no escopo
- Um invasor na mesma LAN — varreduras de portas em Wi-Fi compartilhado, Spoofing de ARP/NDP (incluindo anúncios de roteadores não autorizados) em busca de um man-in-the-middle, DHCP não autorizado, detecção passiva após inundação de MAC, acesso não autorizado a um servidor de desenvolvimento ou API LLM local acidentalmente vinculada a
0.0.0.0. - Dispositivos USB maliciosos (BadUSB) — personificando um teclado e injetando pressionamentos de tecla antes que a tela seja bloqueada; armazenamento em massa USB não registrado para exfiltração de dados ou para transportar malware.
- Ransomware/malware sem arquivo — entrando por meio de um arquivo baixado e criptografando em massa os documentos do usuário.
- C2 baseado em DNS e phishing — malware que usa resolução de nomes para chegar a um servidor C2 ou site de phishing.
Fora do escopo
- Um invasor que já possui root; rootkits em nível de kernel ou firmware.
- Ataques que removem fisicamente o disco (o domínio da criptografia completa do disco; o RoamSwitch diagnostica se a criptografia está presente, mas não criptografa).
- Controle de compartilhamento sem fio de proximidade estilo AirDrop (sem mecanismo nativo do sistema operacional no Linux; não suportado).
§3
RoamSwitch para Linux é implementado em Rust em sete caixas. Toda a comunicação com o daemon passa por /run/roamswitch/roamswitch.sock (um soquete de domínio Unix). Não há soquete TCP/UDP de escuta no daemon, no aplicativo ou no servidor MCP. A única coisa que o aplicativo binds é um único soquete Unix para evitar uma segunda instância.
# Espaço do usuário (sem privilégios) roamswitch-app Bandeja GTK3 + AppIndicator roamswitch (CLI) Frontend leve de shell roamswitch-mcp stdio JSON-RPC (iniciado por clientes de IA) │ │ AF_UNIX /run/roamswitch/roamswitch.sock (apenas IPC local) ▼ roamswitch-daemon root / systemd Type=notify + WatchdogSec=30 · Controle de perfis nftables (tabela inet roamswitch) · Loop sentinel (detecção de rede e aplicação de perfis) · Túnel VPN + kill-switch (WireGuard: wg-quick / roamswitch_vpn, Tailscale: roamswitch_ts) · Bloqueio preventivo ARP/NDP (fixação da tabela neigh) / detecção · Guarda de anomalias de porta (roamswitch_dev_guard) · Guarda fanotify (ransomware / malware) · Guarda de link (NFQUEUE — inet roamswitch_linkguard) · Motor de canário / Vault de quarentena · Endurecimento do kernel contra LPE (desativação de userns, Yama LSM) · Integração em tempo de execução eBPF (/var/run/roamswitch/events.sock) · Congelamento SIGSTOP de processos atacantes em milissegundos e vínculo com Air-Gap · Monitoramento USB (evdev EVIOCGRAB — BadUSB) / fila de aprovação · Verificação de licença Ed25519 roamswitch-updater Um helper autônomo de timer systemd. O único crate com uma pilha HTTP/TLS (GET diário de manifesto assinado) roamswitch-core Biblioteca de lógica pura (compartilhada por todos os crates) roamswitchkit SDK de cliente MCP
| Componente | Privilégio | Função |
|---|---|---|
| roamswitch-daemon | raiz (sistemad, Type=notify + WatchdogSec=30) | Todas as operações privilegiadas. Como gravador único, controla nftables, DNS e unidades systemd. |
| aplicativo roamswitch | usuário de login | GUI GTK3 + bandeja AppIndicator. Apenas edição e visualização de configuração; toda ação privilegiada passa pelo IPC. |
| roamswitch (CLI) | usuário de login | Front-end de casca fina usando o mesmo soquete IPC. |
| roamswitch-mcp | processo gerado pelo cliente AI | stdio JSON-RPC. Fornece informações de diagnóstico somente leitura. |
Toda a comunicação com o daemon passa por /run/roamswitch/roamswitch.sock (um diretório com permissões equivalentes a 0700). A configuração do usuário é armazenada como JSON de texto simples em ~/.config/roamswitch/config.json. O daemon lê /home/*/.config/roamswitch/config.json com privilégios de root (ele roda com HOME=/root). A configuração nunca sai da máquina; não há registro de conta nem sincronização na nuvem.
§4
O loop sentinela (ciclo de 5 segundos por padrão) lê o MAC do gateway padrão de /proc/net/arp e ip neigh e compara-o com os MACs registrados em config.json. O MAC é usado em vez do SSID porque um SSID é falsificado trivialmente. A notificação da área de trabalho mostrada quando o perfil é alternado informa o motivo da alternância (“uma rede não registrada” / “sua configuração para a rede registrada X” / “sem conexão de rede”).
| Perfil | Situação | Comportamento nftables |
|---|---|---|
| aberto (confiável) | Uma rede doméstica ou corporativa registrada | policy accept. Tráfego local permitido. |
| equilibrado | Não registrado, mas configurado como relativamente seguro | Entrada de negação padrão. Somente conexões estabelecidas e lo são permitidas; exposição da porta exposta monitorada. |
| bloqueio (proteção fora de casa) | Wi-Fi público/rede não registrada | Todos os pacotes de entrada foram descartados furtivamente. Compartilhamento de serviços interrompido automaticamente (opt-in). |
Todas as regras são isoladas em uma tabela nftables dedicada chamada inet roamswitch e nunca colidem com outras configurações de firewall (ufw/firewalld). A expiração da substituição manual (1 hora/2 horas/4 horas/até meia-noite/até ser liberada/até a próxima desconexão) é imposta pelo daemon como o gravador único. A GUI nunca grava uma expiração; ele relê o config.json que o daemon reescreveu no próximo tick e o reflete na UI (evitando um conflito).
Air-Gap (isolamento de rede de emergência)
Um modo de isolamento total que define policy drop (prioridade -100) nos ganchos de entrada e saída da tabela inet roamswitch, permitindo apenas lo.
- Fail closed — na reinicialização do daemon, a detecção do arquivo marcador (
/run/roamswitch/airgap.active) reafirma o isolamento. - Self-healing — se um processo externo excluir as regras com
nft delete table, o loop sentinela será reaplicado em segundos. - Expiração automática — 600 segundos após a ativação (idêntico ao macOS
maxAirGapDuration) o isolamento é eliminado automaticamente, evitando uma perda permanente de conectividade devido a um clique errado.
table inet roamswitch {
chain input {
type filter hook input priority -100; policy <accept|drop>;
iif "lo" accept
ct state established,related accept
# balanced/lockdown: tudo o mais é descartado (drop)
}
chain output {
type filter hook output priority -100; policy <accept|drop>;
# policy drop somente durante o Air-Gap; apenas lo e ct established são permitidos
}
}
Proteção de link passivo (detecção de conexão de phishing)
O daemon monitora para onde as conexões de saída estão indo e sinaliza – ou, para o conjunto restrito de “claramente phishing”, bloqueia – conexões com hosts perigosos. Ele substitui o antigo verificador de colar um URL, que exigia que o usuário agisse.
- Interception — uma tabela nftables dedicada
inet roamswitch_linkguardconecta o caminhooutpute enfileira perguntas de DNS e conexões da web novas/antigas para um trabalhador do espaço do usuário via NFQUEUE. NFQUEUE é um canal de veredicto do kernel↔userspace sobreAF_NETLINK; o daemon não vincula nenhum soquete e não origina nenhum pacote. As regras de fila carregambypass, portanto, se o trabalhador não estiver em execução, o tráfego passa desimpedido (o Air-Gap continua sendo a parada brusca separada). - Hostname source — o nome da pergunta DNS, o TLS ClientHello SNI (portanto, um navegador que usa DoH ainda é coberto pelo nome do host) e o cabeçalho HTTP
Hostde texto simples. O ClientHello criptografado (ECH), uma vez amplamente implantado, é um ponto cego conhecido. - Verdict (
roamswitch-core, lógica pura) — um host é verificado em relação ao feed local de phishing/ameaça e um conjunto de heurísticas off-line: IDN/homógrafo confusível de uma marca protegida, token de marca em um domínio não oficial, host de IP bruto, TLD de alto risco.
Modo (linkGuard.mode) | Sucesso no feed ou homógrafo da marca | Outros sinais suspeitos |
|---|---|---|
off | não inspecionado | não inspecionado |
warn | conexão mantida por até 8 s + um prompt de permissão/bloqueio de aprovação | apenas notificação |
block (padrão) | conexão descartada + uma caixa de diálogo de permissão (uma vez por host) | apenas notificação |
Somente um hit de feed ou um host homógrafo de marca é bloqueado; tudo o que a heurística sinaliza sozinha é um aviso. Modo warn segura a conexão com um host perigoso por até 8 segundos enquanto pergunta ao usuário; se a retenção expirar sem resposta, é fail-closed (os pacotes são descartados e o veredicto não é armazenado em cache) - não responde “este site está OK?” não deve significar “sim” e a próxima tentativa solicitará novamente. O modo block mostra uma notificação e uma caixa de diálogo de permissão por host (um navegador que abre muitas conexões com o mesmo host bloqueado não inunda mais as notificações). Em uma caixa de diálogo de aprovação perigosa, o botão padrão é “Continuar bloqueando” e “Permitir” é denominado como a opção silenciosa e não padrão. Um host bloqueado pode ser permitido permanentemente ou por cinco minutos a partir da notificação (linkGuard.allowlist).
DNS é inspecionado, nunca forjado: uma pergunta DNS na lista de bloqueio produz apenas uma notificação; a aplicação ocorre na conexão TCP subsequente, portanto, o RoamSwitch nunca injeta uma resposta DNS sintética. O feed é uma lista compilada de domínios de phishing/fraude, enviada no pacote e atualizada pelo atualizador diário (§11). Sem nenhum feed presente, a heurística offline ainda funciona. A assinatura Ed25519 do arquivo de feed é verificada antes do carregamento; um feed ruim ou ausente falha em “apenas heurística”, nunca em “permitir tudo”.
Túnel VPN + kill switch (a principal defesa MITM)
Proteção anti-MITM que não depende da integridade da camada 2. Em uma rede não confiável, um túnel criptografado sempre ativo para um endpoint confiável torna irrelevantes a falsificação ARP/NDP local, o DHCP não autorizado e a detecção passiva — o invasor vê apenas o texto cifrado. O bloqueio ARP/NDP e a detecção abaixo são secundários a este recurso.
Dois backends para escolher — “WireGuard (arquivo de configuração)” ou “Tailscale (nó de saída)”. RoamSwitch não implementa criptografia própria e não é um provedor de VPN. Ambos os back-ends são dependências opcionais; se estiver ausente, a GUI mostra o comando de instalação.
(A) Back-end do WireGuard
- Nenhum servidor VPN é fornecido — usa uma configuração WireGuard fornecida pelo usuário: um
.confde um provedor como Mullvad, IVPN ou Proton VPN, ou o próprio servidor WireGuard do usuário (um VPS ou um servidor doméstico). Nenhuma configuração padrão é enviada. - Config — o daemon valida o
.confe o armazena em/etc/wireguard/roamswitch.conf(0600, interfaceroamswitch). Ele não pode residir em outro lugar: o perfil AppArmorwg-quickno Ubuntu 24.04+/Debian 13 negawg-quicklendo um.conffora de/etc/wireguard/- mesmo como root. Uma configuração de túnel dividido (AllowedIPse não0.0.0.0/0/::/0) é sinalizada como “nem todo o tráfego está protegido”. - Kill-switch — uma tabela nftables dedicada
inet roamswitch_vpn(policy drop, ambos os ganchos) descarta tudo, exceto loopback, a interface do túnel (roamswitch, correspondida poroifname), UDP para o IP do endpoint fixado, DHCP e ICMP. Ele é instalado antes de o túnel ser ativado (o endpoint é resolvido para IPs fixos viagetent ahostsprimeiro), portanto, não há janela de vazamento. Sewg-quick upfalhar, o interruptor de interrupção permanece ativo.
(B) Back-end da escala de cauda
- Para usuários que já executam o Tailscale. RoamSwitch not executa
tailscale up/ login / instalatailscaled- ele lêtailscale status --jsone executatailscale set --exit-node=<node>/--exit-node=(limpo). - É necessário um nó de saída — a proteção anti-MITM só é válida com um nó de saída que roteia todo o tráfego. Sem nenhum configurado, a proteção não é armada e a UI avisa “não protegido”. Se o nó escolhido ficar off-line, a proteção será desarmada automaticamente e o usuário notificado (rotear tudo através de um nó morto prejudicaria a conexão).
- Kill-switch (
inet roamswitch_ts,policy drop) — O transporte do Tailscale não pode ser fixado em um único IP de endpoint (relés DERP + peers de roaming), então este é mais solto do que o WireGuard. Permite: loopback, interface de túnel (tailscale0), faixas CGNAT100.64.0.0/10efd7a:115c:a1e0::/48, STUN (udp/3478), DERP (tcp/443),udp/41641, DHCP, ICMP, MagicDNS (100.100.100.100:53), conexões estabelecidas. Todo o resto — UDP arbitrário, DNS para um resolvedor local, SMB, mDNS, HTTP de texto simples, TCP arbitrário — foi descartado. - Advertências sobre o switch kill - (1) lista de permissões mais ampla do que o back-end do WireGuard (
udp/3478,tcp/443, todo o CGNAT/10). (2) Um invasor no caminho ainda vê metadados (que o Tailscale está em uso, região DERP, tempo) e pode descartar/atrasar - mas não consegue ler a carga útil do túnel. (3) Quando o UDP direto é bloqueado, a confidencialidade depende do próprio DERP TLS com certificado fixado da Tailscale. (4) Com o MagicDNS desativado + um resolvedor local, o DNS é interrompido enquanto estiver armado (aviso da interface do usuário). (5) Os portais cativos devem estar logados antes de serem ativados.
Common — armado somente quando vpn_on_untrusted_enabled e a rede não for confiável (level != open). Em uma rede confiável, o nó de saída é limpo e o kill switch removido (nunca tailscale down). Desativado por padrão, aceite. O túnel WireGuard/transporte Tailscale vai para o endpoint VPN/tailnet do próprio usuário e não está relacionado à infraestrutura do lafine (§9).
Bloqueio ARP/NDP preventivo
No momento em que uma rede não confiável é ingressada, os endereços MAC da infraestrutura no link que um invasor MITM precisaria representar são fixados na tabela vizinha do kernel como PERMANENT entradas (estáticas). Uma vez fixado, o kernel ignora respostas ARP falsificadas/anúncios vizinhos.
- O que está fixado — (1) o gateway padrão IPv4, (2) o roteador padrão IPv6 (geralmente um endereço
fe80::local de link), (3) qualquer resolvedor DNS no link (um resolvedor público é alcançado através do gateway e é ignorado). Vizinhos IPv6 são fixados comip -6 neigh replace ... nud permanent. - Redes confiáveis nunca são fixadas — portanto, a reinicialização do roteador doméstico não pode prejudicar o usuário. Fixado novamente em cada switch de rede (TOFU); um failover legítimo é detectado na próxima reconexão. As entradas fixadas são registradas em
/var/lib/roamswitch/arp_lock.json. - Ativado por padrão (política do Community Edition; mesmo detalhamento da versão do macOS Pro). Alternar:
gateway_arp_lock_enabled.
Detecção de falsificação de ARP (notificar primeiro)
Monitora /proc/net/arp para um mapeamento de IP para vários MACs e para uma mudança repentina suspeita do MAC do gateway. Em bloqueio: cortar todo o tráfego (Air-Gap) + notificar. Em redes balanceadas/confiáveis: notificar sem cortar; o usuário aciona o Air-Gap de emergência do RoamSwitch. Isso (a) impede que um invasor transforme em arma um único pacote ARP falsificado em uma interrupção autoinfligida e (b) impede que a reinicialização do roteador ou o roaming do ponto de acesso o acione por engano. Alternar: arp_spoof_guard_enabled.
Guarda de anomalias portuárias
Contém automaticamente uma porta de escuta recentemente exposta à LAN externa (um backdoor, C2 ou um servidor de desenvolvimento/API LLM local acidentalmente vinculado a 0.0.0.0). O veredicto é baseline-relative, não “número de porta perigoso”. Ao ativar, os executáveis atualmente expostos são reconhecidos como “conhecidos”; depois disso, apenas um executável exposto recém-aparecido é sinalizado (identidade = o caminho do executável, snaps normalizados para snap:<name>). Os daemons do sistema estão fora do escopo. A contenção é feita por meio da tabela inet roamswitch_dev_guard: coloque o TCP nessa porta de qualquer lugar, exceto lo — a própria máquina continua usando-o. Após a contenção automática, uma notificação imediata na área de trabalho e uma caixa de diálogo de confirmação (“Permitir esta porta” / “Manter bloqueada”) são apresentadas, permitindo a inclusão permanente na lista de permissões com um clique. Qualquer porta também pode ser permitida individualmente e a linha de base recapturada na tela de diagnóstico de porta. Ativado por padrão. Alternar: port_anomaly_guard_enabled. Portado do macOS.
Deteção de riscos do Docker e proteção contra bypass de firewall
As portas publicadas pelo Docker (-p) têm um problema conhecido de bypass da firewall padrão do anfitrião, pelo que são inseridas permanentemente regras de inspeção na cadeia DOCKER-USER, bloqueando qualquer tráfego externo não explicitamente permitido (não requer configuração, ativo por predefinição). Além disso, o docker events é monitorizado em tempo real para detetar instantaneamente o início de um contentor --privileged ou a montagem de /var/run/docker.sock — um risco de fuga do contentor — e notificá-lo de imediato. Como isto assinala uma configuração de risco e não um comprometimento confirmado, trata-se apenas de uma notificação, sem bloqueio automático.
Scanner de fuga de segredos e chaves API
Deteta chaves API (OpenAI, Anthropic, AWS, GitHub, Slack, Stripe e outras) e chaves privadas SSH através de entropia de Shannon e correspondência de padrões. O comando roamswitch audit-secrets <text|ficheiro|diretório> analisa texto e ficheiros, além de um diretório inteiro de forma recursiva (ignorando .git, node_modules, etc.; ficheiros com mais de 2MB ou que pareçam binários são ignorados). A mesma funcionalidade também está disponível através da ferramenta MCP audit_secrets e do método audit_secrets_directory() do SDK Python. O conteúdo nunca é transmitido para qualquer lugar.
Assistente de configuração inicial e UI de confirmação
O pacote é enviado /etc/xdg/autostart/roamswitch.desktop, então o assistente de configuração é iniciado no primeiro login após a instalação (depois ele é residente apenas na bandeja). Ativar/desativar um guarda de segurança mostra uma caixa de diálogo de confirmação para os mesmos guardas da versão do macOS (anomalia de porta, contenção automática de ARP, armazenamento USB, teclado BadUSB, Bluetooth). As decisões em tempo de evento (um teclado USB desconhecido, uma unidade USB não registrada, uma porta desconhecida bloqueada automaticamente, um Air-Gap de emergência) são apresentadas como diálogos modais.
§5
Detecção de explosão de entropia Shannon · Varredura YARA
Os eventos de gravação são rastreados por PID. Se um único processo criar 20 ou mais arquivos distinct com entropia ≥ 7,92 em 5 segundos, ele será tratado como criptografia em massa de ransomware: o processo será congelado com SIGSTOP e o Air-Gap será acionado.
- Separação completa entre verificação YARA e rastreamento de entropia — A inspeção de malware YARA é aplicada a todos os arquivos gravados, sem exceção (ignorar zero, verificação
~/.cache/,node_modules/,/tmp/e arquivos). Enquanto isso, a análise de explosão de entropia de ransomware exclui formatos compactados naturalmente (.zip,.tgz,.tar.gz,.gz,.png,.jpg,.mp4,.wasm) e gerenciadores de pacotes verificados (npm,npx,cargo,rustc,dpkg,apt,tar) operando em árvores de construção/cache para eliminar congelamentos SIGSTOP falso-positivos. - Sobregravações repetidas do mesmo arquivo (por exemplo, três passagens de
shred) contam como um evento. - Proteção de processos críticos — Os PIDs são verificados em uma lista NEVER_FREEZE codificada (
systemd,dockerd,NetworkManager,sshd,gnome-shell,…) e nunca são congelados, evitando um travamento em toda a máquina.
Canários (arquivos chamariz)
Os arquivos chamariz são colocados nas pastas Documentos, Área de Trabalho, Downloads e Imagens do usuário e em um diretório dedicado, e comparados com uma linha de base SHA-256 em um ciclo de 3 segundos. Qualquer renomeação, exclusão, truncamento ou alteração de conteúdo aciona o Air-Gap imediatamente. Como o daemon é executado como root, ele enumera /home/* e implanta canários nos diretórios de cada usuário real.
monitoramento fanotify · Cofre de quarentena · ClamAV
- O daemon monitora
/home,/var/tmp,/dev/shmno nível de montagem e/no nível do sistema de arquivos viaFAN_CLASS_CONTENT(fallbackFAN_CLASS_NOTIF). - Notify-first, sem quarentena silenciosa. Um hit YARA / ClamAV simples é nunca colocado em quarentena silenciosa ou
FAN_DENY’d. Uma assinatura real gera uma solicitação modal em/run/roamswitch/approvals.json(Quarentena/Permitir/Mais tarde); nada entra no cofre até que o usuário confirme. Somente umexecvereal éFAN_DENY, e somente quandopre_exec_blocking_enabledestá ativado. A aprovação expira em falha aberta. - Assinaturas de teste como EICAR são apenas registradas no histórico — a string de teste padrão do setor não gera nenhuma notificação, apenas um registro no histórico de notificações, e nunca é colocada em quarentena ou bloqueada, tanto no fanotify guard quanto no download guard.
- Uma lista de caminhos
scan_exclusionsconfigurada pelo usuário (caminhos absolutos, aplicados recursivamente; inclui o legadoclamav_exclusions) é respeitada pelos scanners YARA e ClamAV. “Permitir” no prompt e “Restaurar” na guia Quarentena são anexados a ele, para que o mesmo arquivo não seja sinalizado novamente. - O malware real detectado e confirmado é realocado para
~/.local/share/roamswitch/quarantine/. O cofre é0700; cada amostra é0400(somente leitura, não executável). Na restauração, o arquivo retorna para0644e é transferido para o proprietário do diretório de destino. - O loop de evento de permissão faz zero E/S do sistema de arquivos — a configuração é lida por um thread de segundo plano dedicado em um instantâneo
Mutex<GuardConfig>; acessadores de hot-path apenas bloqueiam e copiam. Este é um projeto invariante: o bloqueio emFAN_OPEN_PERMcongelaria a máquina. - Quando o ClamAV está presente, executáveis recém-baixados (
.sh,.deb,.elf,…) e modelos de IA perigosos (.pkl,.pt) são verificados no banco de dados local. ClamAV é uma dependência opcional; as outras defesas funcionam sem ele. Quando instalado, o atualizador diário (§11) invocafreshclampara que o banco de dados de assinaturas permaneça atualizado.
Preempção e fortalecimento do escalonamento de privilégios locais (LPE) do kernel
Quando um invasor ou malware consegue uma posição de usuário sem privilégios, a defesa profunda evita a exploração de vulnerabilidades do kernel Linux para obter privilégios de root.
- Desativando namespaces de usuários não privilegiados (Frag Gap CVE-2026-53362 Mitigation) — Muitas vulnerabilidades modernas de LPE do kernel Linux dependem de usuários não privilegiados chamando
unshare(CLONE_NEWUSER)para criar namespaces de usuários isolados, acessando subsequentemente estruturas de kernel não inicializadas ou subsistemas de rede legados com recursos de namespace elevados. RoamSwitch avalia e aplicauser.max_user_namespaces = 0(oukernel.unprivileged_userns_clone = 0) por meio de proteção com um clique ou assistente de configuração. Isso fecha a superfície de ataque de processos sem privilégios, mesmo com kernel de dia zero sem correção. - Yama LSM Memory Protection — A aplicação de
kernel.yama.ptrace_scope = 1(ou2) evita que processos não autorizados sejam anexados viaptraceou leiam memória via/proc/$pid/mem, protegendo sessões do navegador e chaves privadas GPG/SSH. - Core Dump Suppression — A configuração
fs.suid_dumpable = 0evita que processos sensíveis travados gravem dumps de memória de texto simples no disco. /tmpe/dev/shmnoexecMount Defense — Para evitar ataques de queda e execução de carga útil, o RoamSwitch remonta/tmpe/dev/shmcomnoexec,nosuid,nodevao se conectar a redes não confiáveis.
Sensor de ameaças em tempo de execução eBPF e congelamento autônomo de processos (desktop eBPF Guard)
O daemon cliente RoamSwitch inclui integração nativa com sensores eBPF modernos no kernel (Falco/Tetragon/BTF).
# Detecção de ameaças em tempo de execução eBPF e fluxo de interceptação autônoma [Processo malicioso (LPE/malware)] │ Execução de syscall ilegal (tentativa de exploração do Frag Gap) ▼ [Kernel Linux (sonda eBPF)] │ Buffer circular eBPF (design compacto de 2 MB) ▼ [Falco (RoamSwitch Optimized)] │ Socket UNIX direto em memória (/var/run/roamswitch/events.sock) ▼ [roamswitch-daemon (root)] ├─► ① Congelamento do processo atacante em milissegundos: kill(pid, SIGSTOP) [estado: Ts] ├─► ② Corte completo de rede: nftables input/output drop [Air-Gap acionado] └─► ③ Alerta de emergência no desktop: modal GTK3 e notificação libnotify
- Direct UNIX Domain Socket IPC (
/var/run/roamswitch/events.sock) — Alertas do Falco ou de outros sensores eBPF são recebidos diretamente em um soquete UNIX na memória sem gravar nos logs do disco, eliminando a latência de E/S do disco e permitindo transferências de resposta em menos de um segundo. - Mitigação autônoma de múltiplas camadas (congelamento de processo e air-gap) — Quando um evento com prioridade
CriticalouEmergency(como LPE de kernel, escape de namespace ou execução de ransomware) é recebido, o daemon executaSIGSTOPno PID ofensor para pausar imediatamente a execução do processo (estado:Ts), implantapolicy dropeminet roamswitch(Air-Gap) e aciona uma notificação imediata na área de trabalho. - Ajuste de baixo overhead para desktop e prevenção de inchaço de logs (RoamSwitch Optimized) — para conter o consumo de bateria, a carga de CPU e o esgotamento de disco em laptops e estações de trabalho, um perfil otimizado (
99-roamswitch-optimized.yaml) é incluído:- Filtragem de gravidade (
priority: warning) — Filtra 95% dos eventos de aviso/informação de rotina. - Kernel Early Drop (
drop_failed_exit: true) — Descarta saídas de syscall com falha diretamente no driver do kernel, reduzindo pela metade as alternâncias de contexto e a carga da CPU. - Compact Ring Buffer (
cpus_for_each_buffer: 2) — Reduz o tamanho do ring buffer para 2 MB, mantendo a RAM residente em 30–50 MB e o uso da CPU <1%. - Limite obrigatório de rotação de log (
/etc/logrotate.d/roamswitch-falco) — Impõe um teto estrito de 50 MB com rotação compactada de 7 gerações.
- Filtragem de gravidade (
- Resiliência híbrida (modo autônomo vs. eBPF) — Sem Falco, RoamSwitch protege o sistema via kernel sysctl (
user.max_user_namespaces = 0), perfis de roaming nftables, rastreamento de entropia fanotify e canários. Com o Falco, a telemetria comportamental profunda no kernel intercepta explorações avançadas de dia zero e congela os processos do invasor em tempo real.
§6
Os eventos USB são monitorados; dispositivos de classe de teclado (HID) e armazenamento em massa USB são rastreados. Desligado por padrão (envio isEnabled = false, correspondente ao macOS USBKeyboardGuard / USBStorageGuard). Ative no assistente de primeira execução ou na guia USB; a ativação mostra uma caixa de diálogo de confirmação.
Teclado USB não autorizado (injeção de teclas)
- Sem desautorização de porta física em nível de kernel (importante). A implementação antiga (≤1.0.6) escreveu
0em/sys/bus/usb/devices/*/authorizedpara um teclado não registrado, desconectando-o fisicamente. Em um receptor combinado de teclado + mouse (comum em um Raspberry Pi ou mini PC), isso também desconecta o mouse, bloqueando uma máquina sem entrada integrada. RoamSwitch agora se recusa a gravarauthorized=0em qualquer dispositivo que exponha uma interface HID (bInterfaceClass 03), e o daemon reautoriza qualquer dispositivo HIDauthorized==0na inicialização para auto-curar máquinas com uma versão anterior bloqueada. - As teclas digitadas são descartadas no software. Ao detectar um teclado não registrado, um evdev
EVIOCGRABexclusivo é obtido no/dev/input/eventNdesse dispositivo (apenas nós com um manipuladorkbdem/proc/bus/input/devices, correspondendo a VID:PID). O nó do mouse permanece intacto. Este é o equivalente Linux do macOSCGEventTap; o dispositivo permanece ligado. - Segurança — a captura só é realizada se houver outro teclado para digitar (um integrado ou um USB aprovado e conectado), portanto, uma máquina com um único teclado novo nunca fica bloqueada.
- Diálogo de aprovação — junto com a captura, uma solicitação de aprovação é enfileirada em
/run/roamswitch/approvals.jsone a GUI mostra um modal (Permitir/Negar). Permitir coloca o dispositivo na lista de permissões e libera a captura. Falha na abertura após 3 minutos.
Armazenamento em massa USB não autorizado
Quando um dispositivo de armazenamento em massa USB não registrado (sem interface HID) é inserido, a proteção é ativada e a rede não é confiável/em bloqueio, ela é mantida com authorized=0 (seguro para um dispositivo não HID) e uma caixa de diálogo de aprovação é mostrada. Permitir reautorizações e listas de permissões; Deny mantém o controle. A versão do macOS foi alinhada a este modelo “reter somente leitura + prompt de aprovação” na versão 1.7.4.
Identificação e direitos adquiridos
HID é determinado a partir de bInterfaceClass == "03" e da linha Handlers= em /proc/bus/input/devices. Um receptor combo (um VID:PID com uma seção kbd e uma seção de mouse) é mesclado por VID:PID. Um teclado não registrado presente na inicialização do daemon é adquirido; apenas um teclado conectado a quente depois é capturado.
§7
O LinuxHealthChecker em roamswitch-core avalia 24 itens e produz uma pontuação de 0 a 100, uma nota A a F e conselhos por item. No painel GTK, CLI (roamswitch status) e MCP (get_security_report), o título, detalhes e recomendação de todos os 24 itens aparecem no idioma do usuário (10 idiomas).
| Categoria | Itens de exemplo |
|---|---|
| Disco / inicialização | Criptografia LUKS/dm-crypt, inicialização segura UEFI |
| Controle de acesso | Auditoria de configuração LSM (AppArmor/SELinux), SSH/sudo |
| Endurecimento do kernel | fortalecimento de sysctl, desativação de namespaces de usuários sem privilégios (mitigação de Frag Gap), controle de core-dump, noexec em /tmp e /dev/shm |
| Atualizações | configuração automática de atualização de segurança |
| Rede | Monitoramento de falsificação de ARP, estado de bloqueio ARP/NDP do gateway, auditoria de porta exposta, força de criptografia Wi-Fi |
| Programas maliciosos | ClamAV / fanotify (também verifica se o guarda está realmente em execução) / monitoramento de entropia / noexec |
| Navegador | Configurações de navegação segura do Firefox / Chrome / Chromium |
| DNS/USB | DNS de proteção contra ameaças, estado de confiança zero do barramento USB |
Cada verificação é baseada na leitura de arquivos locais e saída de comando (getenforce, mokutil, resolvectl, ss,…). Não há consulta externa de qualquer tipo.
Classificação com reconhecimento de confiança. O fortalecimento do RoamSwitch deliberadamente não se aplica a uma rede confiável (doméstica) - fixando o MAC do gateway como PERMANENTE (o que prejudicaria a LAN na reinicialização do roteador), remontando /tmp e /dev/shm noexec (que quebra compilações de pacotes e alguns instaladores) e DNS de proteção contra ameaças no escopo padrão - é mostrado em verde com uma explicação em vez do que um vermelho “não reforçado” e observa que é aplicado automaticamente em uma rede não confiável. A verdade do tempo de execução vem de /run/roamswitch/state.json (active_level / network_trusted / fanotify_ready), escrito a cada ciclo pelo daemon. O item fanotify verifica se o guard está realmente marcado e em execução (/run/roamswitch/fanotify.ready), e não apenas se o kernel o suporta. O item de portas expostas lê o perfil ativo do daemon (o código rígido do MCP desapareceu).
Proteção por item. Itens com falha que possuem uma correção automatizada (kernel sysctl / Yama / core dumps, /tmp noexec, USB zero-trust, gateway ARP pinning, DNS de proteção contra ameaças, reinicialização do daemon quando o fanotify guard está inativo) carregam um botão “🔧 Harden” no painel GTK. O daemon é executado como root, portanto, nenhum pkexec é necessário, exceto para reiniciar o daemon.
§8
roamswitch-mcp fala JSON-RPC sobre stdio e é gerado por clientes de IA como Claude Desktop, Cursor e Antigravity. Cada ferramenta é somente leitura e totalmente local.
| Ferramenta | Objetivo |
|---|---|
| get_security_report | Avaliação de 24 itens, pontuação, recomendações |
| get_exposed_ports | portas expostas e seu status de bloqueio de firewall |
| get_guard_status | nível de proteção atual e estado do módulo de defesa |
| audit_url_safety | análise heurística de segurança de URL (não busca o alvo) |
| audit_secrets | detecção de chaves/segredos de API em texto (não transmite o texto) |
| audit_security_logs | agregação de logs locais de diário/auth |
| get_quarantine_status /get_canary_status | quarentena e estado canário |
| get_app_help | pesquisa da base de conhecimento integrada |
| run_active_vuln_scan | Verificação ativa de vulnerabilidades, não destrutiva e limitada a 127.0.0.1. A única ferramenta que usa a rede: desativada por predefinição, exige ativação nas definições. |
| run_package_cve_scan | Compara os pacotes instalados (Mac: Homebrew / Linux: dpkg, dnf, zypper, pacman) com um mapa de CVE local. Não ocorre qualquer comunicação de rede. |
| run_package_cve_scan_languages | Compara os ficheiros de bloqueio de dependências do npm, PyPI, crates.io, RubyGems, Packagist, Go e Maven com o mesmo mapa de CVE local. Não ocorre qualquer comunicação de rede. |
| verify_fim | Volta a calcular o hash de cerca de 150 ficheiros críticos do sistema e compara-o com a linha de base guardada para verificar a integridade. |
| get_file_scan_guard_status | Devolve a configuração do File Scan Guard (ClamAV) e o estado do cofre de quarentena que utiliza. |
| get_port_anomaly_incidents | Devolve o estado da linha de base da proteção contra portas anómalas, as portas atualmente bloqueadas automaticamente e até aos 50 incidentes mais recentes. A resposta indica explicitamente que as portas atualmente bloqueadas são uma captura do estado atual, sem marca temporal, distinta do histórico de incidentes, que tem marca temporal. |
| get_ebpf_incidents | Devolve o estado de isolamento atual da guarda de execução eBPF e o historial de incidentes que lhe deu origem. |
| get_resource_guard_incidents | Devolve o historial da guarda de esgotamento de recursos e anomalias de processos (fugas de memória, ciclos de falha), com um nível de confiança. |
| get_notification_history | Devolve o histórico das notificações enviadas pelo RoamSwitch (anomalias de auditoria de registos de segurança, deteções de ClickFix e outras) dos últimos 7 dias, mais recentes primeiro. |
| get_incident_timeline | Reúne as deteções de todas as guardas numa única linha cronológica, com a ascendência dos processos e etiquetas MITRE ATT&CK (experimental). |
O servidor MCP não abre qualquer ligação de rede própria: liga-se ao socket Unix do daemon através do roamswitchkit ou chama diretamente a lógica do roamswitch-core. A única exceção é o run_active_vuln_scan, desativado por predefinição, que envia sondas não destrutivas para 127.0.0.1, ou seja, o próprio anfitrião.
Uma atribuição incorreta descoberta — e corrigida — num exercício com um LLM local
Em setembro de 2026, durante um exercício (fire drill) em que um LLM local (Qwen 3.8 27B) investigava um incidente através deste servidor MCP, descobrimos um caso em que o modelo interpretou mal os dados devolvidos por get_port_anomaly_incidents e incorporou, por engano, um bloqueio de porta antigo e sem relação na narrativa de um incidente em curso. A causa: uma captura sem marca temporal das portas atualmente bloqueadas era devolvida na mesma resposta que o histórico real de incidentes, esse sim com marca temporal, sem nada que distinguisse os dois. A correção acrescenta campos de nota explícitos que indicam qual dos campos é uma linha temporal e qual é apenas uma captura. Ao executar novamente o mesmo cenário com o mesmo modelo após a correção, este citou o texto da nota e recusou explicitamente atribuir a porta antiga ao incidente atual. A eficácia da correção foi verificada tanto por um teste de regressão automatizado como por uma nova execução do LLM local real.
§9
RoamSwitch para Linux faz exatamente um tipo de solicitação de saída: o GET do atualizador diário de um manifesto assinado (§11). Todo o resto – cada avaliação, detecção e auditoria – é concluído localmente. RoamSwitch para Linux é freeware proprietário e sua fonte não é publicada, portanto as inspeções abaixo foram realizadas por lafine; os resultados estão em 9.2 e as etapas no Apêndice C são reproduzíveis apenas no pacote distribuído.
9.1 Método de auditoria
- Isolamento de caixa — uma pilha HTTP/TLS é permitida em apenas uma caixa,
roamswitch-updater, que cria o binário atualizador autônomo acionado por temporizador.scripts/audit_no_network.sh(executado em CI, gating) falha na compilação seroamswitch-core,roamswitch-daemon,roamswitch-app,roamswitch-mcpouroamswitchkitpuxarreqwest/hyper/ureq/rustls/native-tls/openssl, ou se qualquer arquivo.rsfora deroamswitch-updaterusarTcpStream/TcpListener/UdpSocket/to_socket_addrs/lookup_host. - Inspeção de árvore de dependência — o
Cargo.lockcompleto foi pesquisado para SDKs de telemetria/análise/relatório de falhas (sentry,opentelemetry,…). roamswitch-updaterreview — seu alvo de solicitação único, a ausência de uma string de consulta/cookies/cabeçalhos de identificação e sua verificação Ed25519 de tudo o que baixa.- Subprocess enumeration — cada site de chamada
Command::new(...)foi revisado.
9.2 Resultados
| Item de auditoria | Resultado |
|---|---|
| Pilha HTTP/TLS | Presente apenas em roamswitch-updater (ureq + rustls). Não em core / daemon / app / mcp / kit |
| SDK de telemetria/análise/relatório de falhas | Não presente em nenhum lugar |
TcpStream / TcpListener / UdpSocket | Nenhuma ocorrência fora de roamswitch-updater |
| APIs de soquete em uso | AF_UNIX (IPC local); AF_NETLINK (canal de veredicto NFQUEUE, nftables e controle ip neigh — sem saída); AF_INET apenas em roamswitch-updater. O túnel VPN (§4), quando habilitado, é wg-quick (backend WireGuard) ou tailscaled (backend Tailscale) — um subprocesso, não o daemon — enviando tráfego para o próprio endpoint/tailnet do usuário |
| Atualizador diário | Um GET https://lafine.net/updates/v1/manifest por ~24 h. Sem string de consulta, sem cookies, User-Agent genérico, sem identificadores. Todas as cargas úteis verificadas pelo Ed25519. updates.enabled = false desativa totalmente |
| Túnel VPN (§4) | Desativado por padrão. Quando ativado, wg-quick cria um túnel WireGuard para o endpoint no próprio .conf do usuário – não relacionado ao lafine. Resolução de nome de host de endpoint via getent ahosts (glibc NSS), não uma API de soquete Rust |
| Ativação de licença | Verificação de assinatura Ed25519 offline. Nenhuma comunicação com um servidor de ativação |
O RoamSwitch para Linux inicia exatamente um tipo de solicitação de saída: um download diário, somente para recebimento, de dados de detecção assinados que não carregam informações sobre o usuário ou a máquina. Nada mais no produto entra em contato com um servidor externo.
9.3 Lista completa de operações externas
Toda operação que possa fazer com que um pacote saia da máquina local, direta ou indiretamente:
| # | Operação | Destino | Gatilho | O que é enviado |
|---|---|---|---|---|
| 1 | Daily updater — GET /v1/manifest, e o arquivo de feed quando sua versão foi alterada | lafine.net/updates/v1 (CDN) | systemd temporizador, uma vez a cada ~24 h com um atraso aleatório. Desligado quando updates.enabled = false ou roamswitch-update.timer está desabilitado | um GET HTTPS simples: sem string de consulta, sem cookies, User-Agent genérico, nada sobre o usuário ou a máquina. Recebe um manifesto + feed assinado |
| 1a | freshclam (atualizador próprio do ClamAV), invocado pelo atualizador diário quando o ClamAV é instalado | CDN do projeto ClamAV | como parte do #1 | tráfego do próprio ClamAV (um projeto separado); RoamSwitch apenas o inicia |
| 2 | ping -c1 -W1 | o gateway padrão da LAN (link-local) | para atualizar o cache ARP durante a detecção de falsificação de ARP | Somente solicitação de eco ICMP. Sem resolução de DNS, sem carga útil |
| 3 | iniciando o navegador do sistema (xdg-open) | https://lafine.net/... (site do produto) | somente quando o usuário clica em um botão como “Abrir notas de versão” | o processo RoamSwitch não abre nenhum soquete; ele entrega o URL ao navegador do sistema operacional |
| 4 | resolvectl dns | - (nenhuma comunicação ocorre) | DNS de proteção contra ameaças em uma rede não confiável (opt-in). <iface> é toda NIC física que carrega uma rota padrão (com fio + sem fio ao mesmo tempo; lo / tailscale* / wg* / docker* / veth* excluídos); também aplicado em uma rede confiável quando dns_scope = always_on; reconciliou cada ciclo sentinela | apenas altera a configuração do resolvedor do sistema operacional; O próprio RoamSwitch não envia nenhuma consulta DNS |
| 5 | atualizações do gerenciador de pacotes | os repositórios apt / dnf / zypper da distro | quando o usuário executa apt upgrade, etc., ou clica no botão “Atualizar agora” da GUI (que executa apt-get / dnf / zypper via pkexec) | RoamSwitch lê a saída apt-cache policy local e inicia o gerenciador de pacotes; não relacionado à lafina |
| 6 | Túnel VPN (§4) — Backend WireGuard: o túnel wg-quick constrói / Backend Tailscale: tailscale set --exit-node roteia todo o tráfego através do nó de saída | o endpoint VPN / tailnet do próprio usuário (WireGuard: o endpoint no .conf; Tailscale: o nó de saída escolhido e relés DERP) | quando vpn_on_untrusted_enabled e a rede não é confiável. Desativado por padrão | tráfego criptografado WireGuard/Tailscale; o destino é o próprio servidor VPN/tailnet do usuário, não relacionado ao lafine. Resolução de endpoint WireGuard via getent ahosts (glibc NSS); Tailscale é conduzido por tailscaled (um processo separado) |
Todos os outros recursos do RoamSwitch - controle nftables, fixação ARP / NDP da tabela vizinha (ip neigh, sem saída de pacote), proteção de link (NFQUEUE, apenas AF_NETLINK), varredura de porta (análise de saída ss), monitoramento ARP, avaliação de saúde, monitoramento de entropia, canários, fanotify, monitoramento USB (evdev grab), quarentena, auditoria de log, URL e auditoria secreta - completa usando somente leituras de arquivos locais e análise da saída do subprocesso. O túnel VPN §4 é a única “comunicação de saída que o usuário configurou explicitamente” e seu destino é o próprio servidor VPN do usuário – nenhum fluxo de dados para a infraestrutura do lafine.
§10
- Sem conta — nenhum registro de usuário, nenhum login, nenhum endereço de e-mail solicitado.
- A configuração é apenas local — armazenada em texto simples em
~/.config/roamswitch/config.jsone nunca sai da máquina. Não há sincronização na nuvem. - Os resultados da avaliação são apenas locais — os relatórios de segurança e os resultados da auditoria de log são exibidos apenas na tela e, se o usuário escolher, exportados para um arquivo local.
- As chaves de licença são verificadas offline — Os tokens de licença Pro/Business contêm uma assinatura Ed25519 e são verificados com uma chave pública. Nenhuma consulta é feita a um servidor lafine.
- Sem relatório de falhas — em caso de pânico, nenhum rastreamento de pilha é enviado a lugar nenhum.
- O atualizador diário não envia nada sobre você — nenhuma string de consulta, nenhum cookie, nenhum dispositivo ou identificador de instalação, um
User-Agentgenérico. É um download, não um relatório. Veredictos de proteção de link, hosts bloqueados e resultados de varredura são gravados apenas na visualização de atividade local/diário, nunca transmitidos.
§11
| Edição | Para | Preço | Licença |
|---|---|---|---|
| Edição comunitária | Uso pessoal do Linux | Grátis | Freeware proprietário (EULA incluído) |
| Negócios (planejado) | Gestão de frota, distribuição de políticas, apt interno assinado, SLA para organizações | Pago | Licença comercial. Mesmo binário + portão de nível |
Os compradores do macOS RoamSwitch Pro Lifetime recebem recursos empresariais gratuitamente em suas próprias máquinas Linux.
Distribuição e cadeia de suprimentos
| Canal | Detalhe |
|---|---|
| APTO | https://lafine.net/apt (stable main). Release assinado com uma chave RSA-4096 |
| RPM | https://lafine.net/rpm (Fedora/openSUSE separado). repomd.xml assinado por GPG |
| AUR | roamswitch-bin (busca o tarball do GitHub Releases, verifica o sha256) |
| tarball | amd64 / aarch64 .tar.gz + .sha256 nas versões do GitHub |
- Pinned glibc floor — os binários de lançamento são construídos dentro de um contêiner Debian 12 (bookworm, glibc 2.36). Isso evita incompatibilidades de versões de símbolos no Debian 12/Ubuntu 22.04 e posterior. Uma etapa de CI “verificar glibc floor” falha na construção se uma dependência que requer mais de 2.36 for introduzida.
- Signing key management — a chave privada para assinatura do repositório apt/rpm existe apenas em um segredo criptografado do GitHub Actions; apenas a chave pública é confirmada no repositório.
11.3 O atualizador diário
roamswitch-updater é um binário independente executado por um temporizador systemd (roamswitch-update.timer, OnCalendar=daily com um atraso aleatório de 3 horas e Persistent=true). É o único componente que chega à rede. Cada corrida:
GET https://lafine.net/updates/v1/manifeste sua assinatura separada.- Verifique a assinatura Ed25519 do manifesto com uma chave de assinatura de feed de ameaças dedicada – separada da chave de atualização do aplicativo Sparkle. O feed é assinado novamente diariamente pelo CI do lado do servidor, para que a chave privada permaneça em um segredo do CI; colocar o escopo no feed significa que um vazamento apenas permite que um invasor forneça uma lista de bloqueio inválida (sinkhole / warning - sem caminho de código, sem caminho de atualização de aplicativo). A chave de assinatura de atualização de aplicativo nunca sai das chaves do macOS. Um manifesto que não verifica é descartado — o atualizador nunca recorre a dados não assinados.
- Se a versão do feed de ameaças for alterada, baixe o feed, verifique sua assinatura SHA-256 e Ed25519 e instale-o atomicamente para o link guard (§4).
- Se o ClamAV estiver instalado, invoque
freshclam. - Registre a versão mais recente do aplicativo para uma notificação (o atualizador never instala o aplicativo; no Linux o usuário executa
apt upgrade).
A falha de qualquer etapa - offline, assinatura incorreta, freshclam ausente - é registrada como updates.lastError e o processo ainda sai de 0, portanto, a execução do cronômetro nunca gera uma notificação de falha do systemd. Todo o mecanismo é desabilitado por updates.enabled = false em updates.json ou por systemctl disable --now roamswitch-update.timer; o produto é executado com base nos dados incluídos no pacote. O feed de ameaças também é enviado dentro do pacote, portanto, uma nova instalação tem um feed funcional antes da execução do atualizador e apt upgrade o atualiza mesmo com o atualizador desabilitado.
§12
- OS: Ubuntu 22.04 / 24.04 e posterior, Debian 12 e posterior, Linux Mint / Pop!_OS / elementary / Zorin, Raspberry Pi OS 64 bits (Bookworm), Ubuntu para Raspberry Pi
- Arquitetura: x86_64/aarch64
- Required:
systemd,nftables,iproute2(ip neigh— o bloqueio ARP/NDP) (obtido automaticamente pelas dependências.deb/.rpm) - Opcional:
clamav(verificação de malware),nmcli(detecção de criptografia de Wi-Fi),libnotify(notificações de desktop),wireguard-tools(o backend WireGuard do túnel VPN + kill-switch, §4),tailscale(seu backend Tailscale, §4),polkit/pkexec(executando uma atualização da GUI) - Não suportado: distros não systemd (Alpine/Void/Devuan)
- Display: 1280×720 ou maior (a janela e a fonte são dimensionadas automaticamente para o tamanho da tela, suportando pequenos monitores Raspberry Pi)
Apêndice A
| Caixa | Objetivo | Rede |
|---|---|---|
| serde / serde_json | serialização de configuração e IPC | nenhum |
| Tóquio | tempo de execução assíncrono (features = ["full"], mas apenas soquetes Unix são usados) | nenhum (sem TCP) |
| rastreamento / rastreamento-assinante | registro (local / diário) | nenhum |
| este erro / de qualquer maneira | tratamento de erros | nenhum |
| expressão regular | análise de string (diagnóstico, dimensionamento CSS) | nenhum |
| url | Análise de URL (heurística audit_url_safety) | nenhum (não busca) |
| crono | tratamento de data/hora | nenhum |
| ed25519-dalek | verificação offline de assinaturas de licença | nenhum |
| base64/sha2 | tokens de licença, hashes canários | nenhum |
| libc | chown / kill / chamadas de sistema | nenhum |
| idna | decodificação de hosts IDN/punycode para detecção homográfica | nenhum |
| nfq (somente daemon) | Ligações NFQUEUE para o link guard - Rust puro, AF_NETLINK | nenhum (canal de veredicto, sem saída) |
| aberto | chama xdg-open para “Notas de lançamento abertas” | o processo em si não se comunica |
| GTK3 / ksni (somente aplicativo) | GUI e bandeja | nenhum |
| ureq + rustls (somente roamswitch-updater) | o HTTPS GET do atualizador diário | a única pilha HTTP/TLS na árvore; confinado ao binário do atualizador |
Nenhum cliente HTTP, pilha TLS ou SDK de telemetria aparece em roamswitch-core, roamswitch-daemon, roamswitch-app, roamswitch-mcp ou roamswitchkit. ureq + rustls estão presentes apenas em roamswitch-updater (scripts/audit_no_network.sh impõe isso no CI). Não há SDK de telemetria em lugar nenhum.
Apêndice B
table inet roamswitch { # Firewall de perfil (Air-Gap / balanced / lockdown)
chain input {
type filter hook input priority -100; policy <accept|drop>;
iif "lo" accept
ct state established,related accept
# balanced/lockdown: tudo o mais é descartado (drop)
}
chain output {
type filter hook output priority -100; policy <accept|drop>;
# policy drop somente durante o Air-Gap; apenas lo e ct established são permitidos
}
}
table inet roamswitch_linkguard { # §4.5 — independente da tabela de perfil
chain output {
type filter hook output priority 0; policy accept;
udp dport 53 queue num 92 bypass
tcp dport 53 queue num 92 bypass
tcp dport { 80, 443 } ct state new,established queue num 92 bypass
}
}
table inet roamswitch_dev_guard { # §4 Guarda de anomalias de porta / isolamento manual de porta, tabela separada
chain input {
type filter hook input priority -10; policy accept;
iif != "lo" tcp dport { … } drop
}
}
table inet roamswitch_vpn { # §4 Kill-switch do 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; … } # simétrico
}
table inet roamswitch_ts { # §4 Kill-switch do backend Tailscale (mais 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 (o lado v6 também é simétrico)
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 # Conexão direta WireGuard
tcp dport 443 accept # Fallback DERP
ct state established,related accept
}
chain input { type filter hook input priority -150; policy drop; … } # simétrico
}
A fixação ARP/NDP do gateway/roteador IPv6/resolvedor DNS (§4) é feita na tabela vizinha do kernel (ip neigh replace … nud permanent), não em nftables. As entradas fixadas são registradas em /var/lib/roamswitch/arp_lock.json.
Apêndice C
Cada reivindicação neste documento pode ser verificada em sua própria máquina. Os comandos abaixo são executados em um host com o pacote roamswitch instalado.
C.1 Verificação de tráfego de saída
Os binários daemon/app/MCP não vinculam nenhuma biblioteca 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
# → Nenhuma saída. A pilha HTTP/TLS está vinculada estaticamente apenas ao roamswitch-updater.
O daemon em execução não possui soquete de internet:
sudo lsof -p "$(pgrep -x roamswitch-daemon)" -a -i
# → Nenhuma saída = zero conexões de internet TCP/UDP
sudo lsof -p "$(pgrep -x roamswitch-daemon)" -a -U | grep roamswitch
# → Apenas /run/roamswitch/roamswitch.sock (a guarda de link adiciona um socket AF_NETLINK,
# mas isso é um canal do kernel, não um socket de internet)
A única saída atribuível ao RoamSwitch é o atualizador diário:
watch -n 5 'sudo ss -tupn | grep -E "roamswitch-(daemon|app|mcp)" || echo "(none)"'
# → Sempre "(none)". Nenhum desses três jamais abre um socket de internet.
systemctl list-timers roamswitch-update.timer
sudo systemctl start roamswitch-update.service
journalctl -u roamswitch-update.service -n 20 --no-pager
# → Um único HTTPS GET para lafine.net/updates/v1, depois encerra.
Desative e confirme o silêncio total:
sudo systemctl disable --now roamswitch-update.timer
# ou edite /var/lib/roamswitch/updates.json definindo "enabled": false
sudo strace -f -e trace=network -p "$(pgrep -x roamswitch-daemon)" 2>&1 | grep -i 'connect('
# → Conecta-se apenas a AF_UNIX / AF_NETLINK. AF_INET / AF_INET6 nunca aparecem.
C.2 Verificação de defesa reproduzível (Docker Test Suite)
Todos os mecanismos de detecção, controle e defesa do RoamSwitch (aplicação de Air-Gap, autocura após destruição de firewall, adulteração de canário de ransomware, /tmp noexec, Yama LSM, interceptação de phishing homógrafo e detecção de vazamento de credencial - 17 itens no total) podem ser reproduzidos com segurança e verificados 100% em um contêiner isolado sem afetar redes de host ou arquivos, usando o conjunto de testes oficial do Docker fornecido no repositório de suporte público (roamswitch-suporte
# Obtenha e reproduza o conjunto de verificação (verificação automatizada dentro de um contêiner isolado, usando o pacote de distribuição oficial)
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
O contêiner de teste busca automaticamente o pacote de distribuição oficial do repositório APT (lafine.net/apt) e avalia cada um dos seguintes itens de teste sistemáticos (SP-1–11, PENT-1–7) dentro de uma rede isolada e montando namespace.
(1) Defesa de Rede Autônoma e Autocura de Air Gap
| ID | Cenário | Método de verificação | Critério de aprovação |
|---|---|---|---|
| SP-3 | Ativação de Air-Gap → queda de saída | Enviar enable_air_gap IPC | policy drop aplicado ao gancho de saída; todo o tráfego de saída bloqueado |
| SP-3b | Limpeza forçada e autocorreção do firewall | Eliminar regras via nft delete table inet roamswitch | Anti-Clobber Self-Healing restaura regras de drop em 5 segundos |
| SP-3c | Fechamento com falha na reinicialização do daemon | Daemon hard-kill (SIGKILL) no meio do Air Gap e reinicie | A política de queda de saída persiste perfeitamente durante a reinicialização |
| SP-3d | Recuperação autorizada de Air-Gap | Enviar disable_air_gap IPC autorizado | Política de descarte suspensa, todos os arquivos de marcadores removidos, saída restaurada |
| SP-8 | Fixação preventiva de ARP/NDP | Habilitar em ambiente de gateway netns isolado → enviar ARP gratuito falsificado | A entrada vizinha do gateway permanece PERMANENT; ARP falsificado ignorado |
| SP-9 | Interruptor de interrupção VPN (WireGuard) | Servidor Live WireGuard em redes separadas, importe .conf → vpn_up | O handshake é concluído por meio de inet roamswitch_vpn; zero vazamentos na morte do túnel |
| SP-10 | Kill-switch VPN (Tailscale) | Stub tailscale CLI + redes separadas, tailscale0 SE | TCP/80 de texto simples fora do túnel e DNS local bloqueados; DERP e MagicDNS permitidos |
(2) Endurecimento de Kernel e Montagem
| ID | Cenário | Método de verificação | Critério de aprovação |
|---|---|---|---|
| SP-11a | Proteção sysctl de rede | Execute apply_kernel_sysctl_hardening IPC | rp_filter=1, tcp_syncookies=1, accept_redirects=0 atingem valores alvo |
| SP-11b | Proteção de memória e Yama LSM | Verifique ptrace_scope e suid_dumpable | kernel.yama.ptrace_scope=1 (bloqueia espionagem), fs.suid_dumpable=0 (sem core dump) |
| SP-11c | /tmp e /dev/shm defesa noexec | Execute apply_mount_hardening IPC | /tmp e /dev/shm remontados noexec,nosuid,nodev; execução negada |
| SP-11d | Execução legítima do usuário preservada | Execute scripts no diretório inicial padrão (~/) | Funciona sem impedimentos, sem interromper fluxos de trabalho legítimos de desenvolvedores/administradores |
(3) Defesa e canários multicamadas contra malware e ransomware
| ID | Cenário | Método de verificação | Critério de aprovação |
|---|---|---|---|
| SP-1 | Detecção de violação canário | Truncar ou renomear arquivo canário chamariz (.xlsx) em ~/Documents/ | Violação detectada em segundos, acionando Air-Gap imediato |
| SP-2 | Detecção de explosão de criptografia em massa de ransomware | Um único processo gera rapidamente vários arquivos de alta entropia | Processo de trabalho ofensivo congelado com SIGSTOP; Air-Gap acionado |
| SP-2b | Nenhum falso positivo em shred | Execute shred -n 3 -u em arquivos de teste | A correspondência da lista de permissões evita detecção ou congelamento de falsos positivos |
| SP-2c | Processos críticos nunca congelados | Validar a lógica do alvo congelado durante a detecção de burst | Daemons críticos (dockerd, systemd, containerd) nunca são congelados |
| SP-4 | A string de teste EICAR é somente para notificação | Grave a string de teste EICAR em ~/Downloads/ | Nenhuma notificação, apenas um registro no histórico de notificações; o arquivo nunca é colocado em quarentena automática |
| SP-4b | Assinatura de malware real, confirme primeiro | Escreva uma assinatura real fora das pastas monitoradas | Aumenta o diálogo de aprovação modal; colocado em quarentena somente após a confirmação do usuário |
| SP-4c | Proteção de permissão do Quarantine Vault | Inspecione as permissões do diretório do vault e dos arquivos de amostra | Diretório do Vault definido como 0700, amostras em quarentena definidas como 0400 |
| SP-4d | Restauração de arquivo original seguro IPC privilegiado | Enviar restore_quarantine_file IPC | Arquivo restaurado para o caminho original com permissões de usuário (0644) e adicionado às exclusões |
(4) Proteção passiva de link e defesa contra phishing
| ID | Cenário | Método de verificação | Critério de aprovação |
|---|---|---|---|
| SP-7 | mode = block aplicação rigorosa | Conecte-se a hosts listados em feeds ou blocklistExtra | Caiu na camada TLS (SNI analisado); hosts seguros não listados alcançam HTTP 200 |
| SP-7b | mode = warn retenção com falha fechada | Conecte-se ao host de aviso sem resposta do usuário (tempo limite de 8s) | Descarta pacotes após o tempo limite (fail-closed); permitido apenas com aprovação explícita |
| SP-7c | mode = off desmontagem completa | Desativar proteção de link | A tabela inet roamswitch_linkguard foi completamente desinstalada |
(5) Mitigação BadUSB e controle de dispositivos
| ID | Cenário | Método de verificação | Critério de aprovação |
|---|---|---|---|
| SP-6 | Proteção de teclado conectado | Verifique os teclados conectados na inicialização do daemon | Dispositivos existentes adquiridos; apenas os hot-plugs subsequentes foram capturados |
(6) Conjunto de testes de penetração em ambiente Docker (17 verificações)
| ID | Vetor de ataque | Detalhe da Simulação | Critérios de Defesa/Detecção |
|---|---|---|---|
| PENT-1 | Exfiltração C2 | Tentativa de saída HTTP/TCP para C2 sob Air-Gap | Todos os pacotes descartados no netfilter; vazamento zero |
| PENT-1b | Exclusão de Força FW | Eliminar regras com nft delete table inet roamswitch | Anti-Clobber Self-Healing restaura regras em segundos |
| PENT-1c | Restauração segura de air-gap | Problema autenticado disable_air_gap IPC | Regras de descarte removidas; marcadores excluídos; conectividade restaurada |
| PENT-2 | /tmp Porta dos fundos | Execute o script backdoor descartado em /tmp | Negado pela proteção de montagem noexec (Permission denied) |
| PENT-2b | /dev/shm Memória Compartilhada | Execute a carga binária colocada em /dev/shm | Bloqueado por noexec defesa de montagem |
| PENT-2c | Preservação do espaço do usuário | Execute o script no diretório de usuário legítimo (/home/tester/) | Execução desimpedida; interrupção zero do fluxo de trabalho |
| PENT-3 | espionagem de memória ptrace | PTRACE_ATTACH para direcionar para extrair segredos/tokens | Negado por Yama LSM kernel.yama.ptrace_scope=1 |
| PENT-3b | Raspagem secreta do Core Dump | Acionar falha no processo para obter dump principal de memória | Impedido por fs.suid_dumpable=0 |
| PENT-4 | Adulteração de Canário | Arquivo isca canário zerado em ~/Documents/ | Detecção em tempo real de anomalia de tamanho e alteração de hash |
| PENT-4b | Limpeza em massa Canary | Exclusão em massa de arquivos canary decoy | Desaparecimento de arquivo detectado imediatamente, gerando incidente |
| PENT-5 | Falsificação de endereço IP | Transmitir pacotes com IP de origem falsificado | Eliminado por rp_filter=1 (filtro de caminho reverso) |
| PENT-5b | SYN Flood DoS | Inundar conexões TCP semiabertas | Tabela de estados protegida por tcp_syncookies=1 |
| PENT-5c | Sequestro de rota ICMP | Injetar pacotes falsos de redirecionamento ICMP | Ignorado devido a accept_redirects=0 |
| PENT-6 | Phishing homógrafo cirílico | Navegue até gооgle.com (homógrafo cirílico) | Identificado pelo LinkGuard, pontuação 40 (PERIGOSO/bloqueio) |
| PENT-6b | IP bruto e tráfego de texto simples | Conexão de saída direta para IP bruto | Sinalizado com precisão como fator de risco de segurança |
| PENT-7 | Vazamento de chave de API OpenAI | Expor o token sk-proj-... no código ou nos logs | Detectado através da análise de entropia de Shannon e mascarado |
| PENT-7b | Vazamento de token do GitHub | Expor o token pessoal ghp_... no código | Correspondido por padrão de token e verificação de entropia |
| PENT-8 | Preempção de Kernel LPE (Frag Gap) | unshare -U -r sem privilégios (tentativa CVE-2026-53362) | Rejeitado imediatamente por user.max_user_namespaces = 0 (Operation not permitted) |
| PENT-9 | Sensor de ameaças eBPF e congelamento de processos | Alerta crítico/emergência injetado via soquete Falco eBPF | Processo congelado via SIGSTOP em milissegundos (estado: Ts); Air Gap implantado |
(7) Resumo do teste de penetração (reforçado autônomo vs. eBPF aprimorado)
Resumo dos resultados do teste de penetração completo executado em um ambiente Docker isolado (Alvo: Ubuntu 24.04 com RoamSwitch Client Edition; Atacante: Nmap / curl / hping3; consulte o relatório oficial docs/CLIENT_PENTEST_REPORT.ja.md):
| Categoria de verificação | Sem Falco (Autônomo Endurecido) | Com Falco (eBPF aprimorado) | Veredicto de Defesa |
|---|---|---|---|
| Resistência à varredura de porta externa (Nmap SYN) | PASS (100% filtrado/DROP em Lockdown) | PASS (100% filtrado/DROP em Lockdown) | Furtividade Externa Total |
| Corte de acesso direto externo (TCP/80, 3306) | PASS (queda de tempo limite/resposta zero) | PASS (queda de tempo limite/resposta zero) | Zero exposição de entrada |
| Mitigação de Kernel LPE (Frag Gap) | PASS (unshare bloqueado via sysctl) | PASS (alerta eBPF + congelamento SIGSTOP + Air-Gap) | Defesa em profundidade verificada |
| Detecção de porta não autorizada (anomalia) | PASS (0.0.0.0 bind sinalizado risco ALTO) | PASS (0.0.0.0 bind sinalizado risco ALTO) | 100% detectado e guiado |
| Inchaço de log e prevenção de sobrecarga de CPU | Zero pegada de log (desinstalado) | PASS (corte de ruído de 95% / buffer de 2 MB / limite de 50 MB) | Desktop otimizado |
C.3 Limitações conhecidas
| Limitação | Impacto | Mitigação |
|---|---|---|
Em alguns kernels, FAN_MARK_MOUNT em tmpfs (/tmp) não entrega eventos | gravações de arquivos em /tmp não são monitoradas pelo fanotify | /tmp é efêmero e não é uma pasta de usuário monitorada; ransomware tem como alvo ~/. As falhas de marcação são registradas explicitamente |
| Nenhuma entrega fanotify em contêiner overlayfs / tmpfs | a detecção de explosão de entropia no contêiner não pode ser validada | validado em hardware real (ext4); o chicote salta automaticamente por meio de uma sonda |
As operações nftables do RoamSwitch compartilham o espaço netfilter com o iptables-nft do Docker | As operações de regras RoamSwitch podem interferir nas regras NAT do Docker | documentado como uma advertência operacional; isolado na tabela inet roamswitch dedicada, mas operações com todo o conjunto de regras, como nft flush ruleset, devem ser evitadas |
| O link guard vê o DoH de um navegador apenas via TLS SNI; Encrypted Client Hello (ECH) também esconde isso | uma vez que o ECH é amplamente implantado, uma conexão que usa ECH a um site que suporta ECH não é identificável pelo nome do host | a lista de bloqueio local e a heurística ainda atuam em DNS/HTTP e TLS não-ECH; A proteção contra ameaças de DNS (§4.5, opt-in) cobre o caminho do resolvedor |
A atualização do atualizador diário depende da cadência de lançamento quando updates.enabled = false | com as atualizações desativadas, o feed é tão atual quanto o último apt upgrade | as detecções de maior gravidade (homógrafo de marca) não precisam de feed; opte novamente ou ative o DNS de proteção contra ameaças para obter cobertura sempre atualizada |