Projeto de Sistemas Lafine · Documento técnico

RoamSwitch para Linux
Artigo de arquitetura e segurança

Um documento técnico que explica o que o RoamSwitch para Linux faz em seu limite de privilégios. Evita linguagem de marketing; as declarações podem ser verificadas em relação ao comportamento real do pacote distribuído e à autoverificação no apêndice.

Versão v1.11.1 Escopo RoamSwitch for Linux 1.11.1 (endurecimento do kernel contra LPE / proteção em tempo de execução eBPF / congelamento SIGSTOP de processos atacantes / otimização de baixo overhead do Falco / detecção de tunelização DNS / captura forense de provas / honeytokens / acompanhamento do pass_age na verificação de vulnerabilidades / mapeamento NIST CSF/CIS Controls / reavaliação do diagnóstico / auditoria da configuração do MCP) Requisitos systemd + nftables Publicado 2026-10-08

§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 pelo endereço MAC do gateway e alterna de forma autônoma o perfil de firewall nftables ativo. Além disso, oferece três camadas de defesa contra ataques man-in-the-middle (MITM) em redes não confiáveis: um túnel VPN + kill-switch (WireGuard ou Tailscale, a defesa principal), um bloqueio ARP/NDP preventivo e a detecção de ARP spoofing. Oferece ainda detecção comportamental de ransomware (fanotify + entropia de Shannon + canários), um guarda de link passivo que sinaliza ou bloqueia conexões de phishing (NFQUEUE + SNI), Port Anomaly Guard, mitigação de BadUSB, contramedidas contra escalação de privilégio local (LPE) no kernel (negar namespaces de usuário sem privilégios em redes não confiáveis, como mitigação do Frag Gap CVE-2026-53362, é opcional e vem desativado por padrão), integração com o sensor de ameaças em tempo de execução eBPF (Falco / Tetragon), com congelamento de processos em milissegundos (SIGSTOP) e isolamento de rede Air-Gap, uma avaliação de integridade de segurança de 27 itens e um servidor Model Context Protocol (MCP) integrado.

Este whitepaper descreve a arquitetura, o modelo de ameaças e o design de cada mecanismo de defesa — e, em particular, como o princípio de “processamento totalmente local; por padrão, a única coisa que sai da máquina é uma solicitação de dados de detecção assinados, que não contém nada sobre você” é garantido no nível do código. As funcionalidades opcionais que um operador precisa ativar explicitamente (como o encaminhamento de eventos) são exceções a esse comportamento padrão; estão todas listadas na tabela seguinte e no §9.4. Evita linguagem de marketing; as afirmaçõ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 o seu código-fonte não é publicado (§11).

Princípios de design
PrincípioDetalhe
Zero telemetriaPor padrão, o produto não envia nada sobre você ou sobre a sua máquina. Não existem telemetria, análises, relatórios de falhas, estatísticas de utilização, configuração remota nem servidor de ativação de licenças. Só podem comunicar as funcionalidades opcionais que um operador ativa explicitamente, indicadas na linha seguinte («Exceções ao comportamento padrão») e em 9.4.
Exceções ao comportamento padrão (todas opcionais)Os recursos a seguir só se comunicam com o exterior se um operador os ativar explicitamente. Os destinos são apenas os que o operador configura; não há destinos embutidos. (1) Encaminhamento de eventos (forward_enabled, desativado por padrão) — envia eventos de detecção para um syslog / arquivo JSON Lines / webhook assinado indicado pelo operador. (2) Fleet (camada Business, apenas código do lado do dispositivo) — um dispositivo não registrado não envia nada nem resolve o nome de host do Fleet; o serviço de gerenciamento não está implantado nem lançado. Todo o resto, incluindo verificações de atualização acionadas pelo usuário, está listado no 9.4.
Atualizações somente para recebimentoA única comunicação de saída que o produto realiza por padrão é, uma vez por dia, em uma URL fixa (lafine.net/updates/v1), um GET de um manifesto assinado. Quando o ClamAV está instalado, cada execução também inicia o atualizador do próprio ClamAV, o freshclam; esse tráfego pertence ao ClamAV e está listado separadamente no §9.4. Do manifesto, ele obtém a lista de sites fraudulentos do guarda de link, a versão mais recente do aplicativo, os mapas de CVE do kernel, dos pacotes do SO (Debian / Ubuntu / Arch / família RHEL / SUSE) e dos pacotes de linguagens (npm / PyPI / crates.io / RubyGems / Packagist / Go / Maven), o mapa de CVE da análise ativa de vulnerabilidades e a lista de nomes de pacotes npm populares usada na detecção de typosquatting. Todos os dias, ele obtém apenas o manifesto e sua assinatura (dois GET); a lista do guarda de link, o mapa de CVE da análise ativa e a lista npm só são obtidos quando o manifesto indica uma versão mais recente, e os mapas de CVE do kernel e dos pacotes só quando se passaram cerca de 30 dias desde a última atualização. O mapa de CVE do Ubuntu só é obtido por dispositivos com uma versão do Ubuntu suportada; um dispositivo que ainda não o tem obtém-no na execução diária seguinte, sem esperar os 30 dias. Não há cadeia de consulta nem cookies, o único cabeçalho que nomeia o software é um User-Agent: RoamSwitch-Updater fixo, e nenhuma informação sobre o usuário é incluída. updates.enabled = false o desativa por completo; o produto então funciona apenas com os dados incluídos no pacote e, a menos que você ative um recurso opt-in, não tem nenhum contato externo.
Processamento totalmente localCada 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ínimoAs operações privilegiadas ficam confinadas a um daemon root; a interface, a CLI e o servidor MCP solicitam-nas através de IPC por socket de domínio Unix. Por padrão, o daemon não abre qualquer socket TCP/UDP (as funcionalidades opcionais ativadas por um operador são a exceção, §9.4); o guarda de links usa apenas o canal de veredicto NFQUEUE sobre AF_NETLINK (kernel ↔ espaço de usuário, sem emissão de pacotes). As chamadas IPC que alteram o estado só são aceites de root ou de um UID com sessão de início local (desde a 1.10.4; as chamadas só de leitura não mudam).
Falha fechadaAir-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-lineAs 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

O RoamSwitch para Linux é implementado em Rust, e o pacote inclui oito executáveis (roamswitch-daemon, roamswitch-app, roamswitch (CLI), roamswitch-mcp, roamswitch-updater, roamswitch-honeytokens, roamswitch-incident-capture e o experimental roamswitch-xdp-gate, desativado por padrão). A lógica compartilhada fica na biblioteca roamswitch-core, e o roamswitchkit é o SDK de cliente. 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 nem no servidor MCP. A única coisa que o aplicativo faz bind é um único soquete Unix para impedir 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
  · Contramedidas contra LPE no kernel (negar userns sem privilégios é opcional, desativado por padrão)
  · 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-honeytokens          Processo auxiliar iniciado pelo daemon (posiciona e monitora credenciais isca)
roamswitch-incident-capture     Processo auxiliar iniciado pelo daemon (coleta um pacote de evidências na contenção)
roamswitch-core     Biblioteca de lógica pura (compartilhada por todos os crates)
roamswitchkit       SDK de cliente MCP
Separação de privilégios
ComponentePrivilégioFunção
roamswitch-daemonraiz (sistemad, Type=notify + WatchdogSec=30)Todas as operações privilegiadas. Como gravador único, controla nftables, DNS e unidades systemd.
aplicativo roamswitchusuário de loginGUI GTK3 + bandeja AppIndicator. Apenas edição e visualização de configuração; toda ação privilegiada passa pelo IPC.
roamswitch (CLI)usuário de loginFront-end de casca fina usando o mesmo soquete IPC.
roamswitch-mcpprocesso gerado pelo cliente AIstdio JSON-RPC. Fornece informações de diagnóstico somente leitura.

Toda a comunicação com o daemon passa por /run/roamswitch/roamswitch.sock (o diretório é 0755 e o soquete é 0666). O controle de acesso é feito verificando quem chama, e não pelas permissões de arquivo: o daemon obtém o UID do par com SO_PEERCRED, recusa contas de sistema (UIDs de 1 a 999 e nobody) e aceita chamadas que alteram o estado apenas de root ou de um UID que possui uma sessão de login local (§1). A configuração do usuário é armazenada como JSON em 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), aceitando apenas um arquivo que pertença à própria conta, que não seja um link simbólico e no qual outros usuários não possam gravar. 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 3 segundos) lê o MAC do gateway padrão em /proc/net/arp e ip neigh e o compara com os MACs registrados em config.json. Usa-se o MAC em vez do SSID porque um SSID é falsificado trivialmente. A notificação da área de trabalho exibida 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”).

Perfis
PerfilSituaçãoComportamento nftables
aberto (confiável)Uma rede doméstica ou corporativa registradapolicy accept. Tráfego local permitido.
equilibradoNão registrado, mas configurado como relativamente seguroEntrada negada por padrão. Somente conexões estabelecidas, lo e ICMP / ICMPv6 são permitidos; a exposição de portas públicas é monitorada.
bloqueio (proteção fora de casa)Wi-Fi público/rede não registradaTodos os pacotes de entrada são descartados em modo furtivo. Os serviços de partilha são parados automaticamente e repostos no regresso a uma rede de confiança (ativado por predefinição, pode ser desativado a qualquer momento).

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.
  • Os acionadores de baixa confiança são desfeitos por um temporizador curto. Um Air-Gap manual simples, acionado sem contexto de alerta (um botão da GUI ou roamswitch airgap), é um acionador de baixa confiança e é totalmente restaurado após um temporizador curto (airgap_low_confidence_secs, 600 segundos por padrão), para que um clique por engano ou um falso positivo não deixe a máquina sem rede por muito tempo. Uma anomalia ARP fora do lockdown, uma anomalia de portas e um evento eBPF de nível Error ou inferior também são classificados como de baixa confiança na máquina de estados compartilhada, mas o Client nunca aciona o Air-Gap automaticamente por eles; eles geram apenas uma notificação ou um registro, ou bloqueiam a porta em questão.
  • Os acionadores de alta confiança nunca são restaurados em silêncio. Adulteração de canário, um evento eBPF Emergency/Alert (um evento Critical somente quando não foi possível conter o processo individual), ARP spoofing envolvendo o gateway durante o lockdown e um Air-Gap acionado em resposta a um alerta ARP (além de, apenas com opt-in, uma regra de alta gravidade do registrador de execuções; §5) não são restaurados automaticamente, por mais tempo que passe. A partir do temporizador curto, cada ciclo reverifica se a causa persiste (o PID congelado ainda vivo, o canário reescrito novamente, a anomalia ARP continuando e assim por diante) e libera, com notificação, somente quando se verifica que a causa desapareceu. Uma verificação inconclusiva conta como “não resolvido”. Uma rajada de entropia de ransomware também aciona o Air-Gap, além do congelamento do processo por SIGSTOP e de uma notificação, e é tratada igualmente como gatilho de alta confiança.
  • Modo DEGRADED — se um isolamento de alta confiança atingir o limite (airgap_hard_cap_secs, 3600 segundos por padrão; 0 = sem limite) sem liberação pelo usuário e sem causa verificada como eliminada, ele passa a DEGRADED. No cliente, DEGRADED é tão rígido quanto FULL: tudo exceto loopback é descartado, inclusive conexões estabelecidas; a diferença é que nunca reabre por temporizador. A liberação é feita pelo usuário local (botão da GUI ou roamswitch emergency-restore) ou por uma reverificação que comprove o fim da causa. O Air-Gap também bloqueia por padrão o rádio Wi-Fi via rfkill (airgapAutoRfkill); o DEGRADED o mantém bloqueado, a menos que airgap_degraded_lift_rfkill seja true.
  • Transições registradas e notificadas — a passagem de FULL para DEGRADED, cada tipo de liberação automática e uma liberação pelo usuário são gravadas na linha do tempo de incidentes e geram uma notificação na área de trabalho. O estado do isolamento é guardado em /run/roamswitch/airgap_isolation.json e sobrevive à reinicialização do daemon (uma reinicialização zera a contagem de verificações ARP consecutivas, o que pende para manter o 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 é policy accept sem regras; balanced / lockdown são policy drop (ICMP / ICMPv6 são permitidos apenas em balanced)
    }
    chain output {
        type filter hook output priority -100; policy <accept|drop>;
        # Durante o Air-Gap, a mesma tabela é recriada: policy drop em input e output, somente lo é permitido (conexões estabelecidas também não passam)
    }
}

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_linkguard conecta o caminho output e 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 sobre AF_NETLINK; o daemon não vincula nenhum soquete e não origina nenhum pacote. As regras de fila carregam bypass, 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 Host de 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 marcaOutros sinais suspeitos
offnão inspecionadonão inspecionado
warnconexão mantida por até 8 s + um prompt de permissão/bloqueio de aprovaçãosem notificação e sem bloqueio
block (padrão)conexão descartada + uma caixa de diálogo de permissão (uma vez por host)sem notificação e sem bloqueio

Somente um acerto no feed ou um host homógrafo de marca é bloqueado de forma definitiva (hard-block); tudo o que apenas a heurística sinaliza permanece como um veredito de nível de aviso, sem bloqueio e sem notificação. O modo warn retém por até 8 segundos a conexão com um host perigoso enquanto pergunta ao usuário; se a retenção expirar sem resposta, o comportamento é fail-closed (os pacotes são descartados e o veredito não é armazenado em cache). Não responder a “este site é seguro?” não pode significar “sim”, e a próxima tentativa pergunta novamente. O modo block também mostra uma única notificação e uma única 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 de alvo perigoso, o botão padrão é “Continuar bloqueando” e “Permitir” é estilizado como a opção discreta e não padrão. Ao escolher “Permitir” na caixa de diálogo de aprovação, o host bloqueado é adicionado permanentemente a linkGuard.allowlist e passa a ser liberado até ser removido nas configurações.

O DNS é inspecionado, mas nunca forjado: uma consulta DNS que consta na lista de bloqueio gera apenas uma notificação; a aplicação ocorre na conexão TCP subsequente, e o RoamSwitch nunca injeta uma resposta DNS sintética. O feed é uma lista compilada de domínios de phishing/fraude, distribuída no pacote e atualizada pelo atualizador diário (§11). Sem nenhum feed presente, a heurística offline continua funcionando. A assinatura Ed25519 e o SHA-256 do feed são verificados pelo atualizador diário antes de instalar qualquer coisa, e dados que não correspondem não são adotados. O guarda de link carrega o feed instalado (ou o distribuído no pacote); sem nenhum dos dois, ele funciona com “apenas heurística”, nunca com “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 .conf de 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 .conf e o armazena em /etc/wireguard/roamswitch.conf (0600, interface roamswitch). Ele não pode residir em outro lugar: o perfil AppArmor wg-quick no Ubuntu 24.04+/Debian 13 nega wg-quick lendo um .conf fora de /etc/wireguard/ - mesmo como root. Uma configuração de túnel dividido (AllowedIPs e não 0.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 por oifname), 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 via getent ahosts primeiro), portanto, não há janela de vazamento. Se wg-quick up falhar, 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 / instala tailscaled - ele lê tailscale status --json e executa tailscale 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 CGNAT 100.64.0.0/10 e fd7a:115c:a1e0::/48, STUN (udp/3478), DERP (tcp/443), udp/41641, DHCP, ICMP, MagicDNS (100.100.100.100:53), 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.

Comum. Ativado somente quando vpn_on_untrusted_enabled está definido e o perfil é lockdown (proteção fora de casa). Em open e balanced o túnel não é estabelecido e, com o Tailscale, o nó de saída é limpo e o kill-switch removido (nunca tailscale down). Desativado por padrão, opt-in. O destino da comunicação de saída, tanto no WireGuard quanto no Tailscale, é o próprio endpoint VPN / tailnet do usuário, sem nenhuma relação com a infraestrutura da 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 com ip -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 em busca de um MAC que responde por vários IPs (MAC duplicado) e de uma mudança repentina suspeita do MAC do gateway. Em lockdown: quando o gateway está envolvido, corta todo o tráfego (Air-Gap) e notifica. Em redes balanceadas / confiáveis: apenas notifica, sem cortar; o usuário aciona o Air-Gap de emergência pelo RoamSwitch. Isso (a) impede que um invasor transforme 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. Configuração: arp_spoof_guard_enabled.

Verificação da causa antes de liberar um Air-Gap (reverificação ARP)

Um Air-Gap causado por spoofing ARP (detectado sob lockdown ou acionado em resposta a um alerta ARP) só é liberado como “causa eliminada” quando tudo o que se segue é verdadeiro. O daemon lê a tabela de vizinhos do kernel (ip -j neigh show, com /proc/net/arp como alternativa) e a rota padrão; o MAC do gateway padrão deve ser igual ao MAC confiável registrado na ativação (estado REACHABLE, STALE, DELAY, PROBE ou PERMANENT, sem que outro host IPv4 do mesmo dispositivo reivindique esse MAC), por arp_recheck_consecutive verificações consecutivas (3 por padrão) que abranjam ao menos arp_recheck_min_secs segundos (60 por padrão). Somente quando a entrada de vizinho está ausente ou em estado de falha e o link está ativo (carrier presente, operstate up, Wi-Fi não bloqueado por rfkill), ele envia um único ARP who-has ao gateway (com pelo menos 20 segundos de intervalo, via AF_PACKET) para verificar (arp_recheck_active_probe, true por padrão). Se qualquer resposta trouxer um MAC diferente do confiável, é uma divergência. Divergência, entrada ausente, MAC duplicado, falha de leitura ou ausência de MAC confiável registrado contam sempre como “não resolvido” (fail-closed) e zeram a contagem. Limitação conhecida: um atacante que clone o MAC do gateway real não pode ser detectado por nenhuma comparação de MAC, em cache ou ativa.

A detecção de ARP spoofing aciona o Air-Gap automático somente quando o gateway está envolvido: o MAC do gateway mudou, ou o IP do gateway compartilha um MAC com outro IP. Se o MAC de outro host mudar, ou se uma máquina responder por vários IPs com um só MAC (um PC que executa máquinas virtuais, um NAS com endereço alias), isso é apenas notificado e a rede não é cortada. A mesma regra vale para a verificação de liberação: só um duplicado que envolva o gateway conta como “a causa persiste”. Quando esta regra foi introduzida na 1.10.7 (2026-09-26), em uma LAN real, um ARP falso do gateway enviado por outra máquina acionou o Air-Gap em cerca de 1 segundo, e ele foi liberado automaticamente cerca de 11 minutos depois de o ataque parar (os 10 minutos padrão mais 60 segundos de confirmação), mesmo enquanto dois IPs sem relação continuavam a responder com um só MAC. Antes desta regra, esse duplicado inofensivo bastava para causar um corte total que nunca era liberado.

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 tunelização DNS / exfiltração

Funciona o tempo todo como parte do caminho DNS já existente do Link Guard passivo (não há uma opção de ativação separada). Conta como “suspeita quanto à forma” uma consulta que corresponda a algum dos padrões característicos de ferramentas de tunelamento DNS como iodine e dnscat2 (rótulo com mais de 40 bytes E entropia de Shannon superior a 3,8, ou um qtype de registro TXT/NULL) e só notifica quando 10 ou mais dessas consultas ocorrem em 60 segundos (para o mesmo apex domain há um período de espera de 300 segundos entre notificações repetidas, que passa a 600, 1200 e, no máximo, 1800 segundos enquanto as notificações continuam, e volta a 300 segundos se não houver notificações por pelo menos o dobro do período de espera). Somente o DNS (porta 53 UDP/TCP) é inspecionado; nenhum outro tráfego é examinado. A funcionalidade só notifica e nunca bloqueia automaticamente. Uma consulta TXT para clamav.net não é contada, pois o freshclam a usa para verificar a atualidade. A regra NFQUEUE subjacente tem a flag bypass, de modo que, mesmo que o processo de detecção esteja parado ou sem resposta, o tráfego DNS passa sem ser afetado. A resolução de nomes nunca é bloqueada por uma falha desta funcionalidade (o mesmo design fail-open do próprio Link Guard passivo).

Limitação conhecida. Nomes de host longos separados por hífens (por exemplo, prod-us-east-1-web-server-node-0042) e um único contador global que não distinguia os domínios de destino nas rajadas de TXT/NULL provocavam falsos positivos aqui, o que foi confirmado por medição. Ambos foram corrigidos em 2026-09-23. A correção consiste em uma lista de permissões de entropia configurável e na separação do contador de rajadas por apex domain (uma aproximação simples pelos dois últimos rótulos do QNAME, e não uma implementação estrita da Public Suffix List). A lista de permissões já vem por padrão com 16 sufixos de CDN/nuvem e PaaS (cloudfront.net, akamaiedge.net, akamaized.net, edgekey.net, edgesuite.net, fastly.net, azureedge.net, googleusercontent.com, amazonaws.com, r2.dev, workers.dev, vercel.app, netlify.app, supabase.co, firebaseapp.com, web.app), e os operadores podem adicionar seus próprios domínios internos em LinkGuardCfg.dns_tunnel_entropy_allowlist (as configurações do guarda de link do roamswitch-app têm um campo dedicado, separado por vírgulas, para isso). A lista de permissões dispensa apenas a verificação de suspeita por tamanho do rótulo mais entropia; a detecção por qtype TXT/NULL se aplica independentemente de haver ou não a lista de permissões, o que impede que um atacante disfarce um túnel sob um domínio permitido (a única exceção é o TXT sob clamav.net, que o freshclam usa para verificar a atualidade, por meio de uma pequena lista de exceção separada). O contador de rajadas agora é separado por apex domain, o que elimina os falsos positivos causados por um único contador global que ignorava o domínio de destino (a lógica da janela deslizante de 60 segundos em si não foi alterada; o período de espera para nova notificação começa em 300 segundos e chega a no máximo 1800 segundos enquanto as notificações continuam). O tráfego malicioso real de ferramentas como iodine ou dnscat2 apenas faz a rotação de rótulos e qtypes sob um único domínio controlado pelo atacante, portanto restringir a contagem por apex domain não reduz a eficácia da detecção.

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.

Cobertura — esta inspeção da cadeia DOCKER-USER cobre o backend iptables (IPv4), IPv6 e o backend nftables nativo do Docker, e volta a verificar-se e a reparar-se a cada 60 segundos (também na edição de ambiente de trabalho, desde a 1.10.4; as versões anteriores cobriam apenas o backend iptables (IPv4)).

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 deteção também está disponível através da ferramenta MCP audit_secrets (com text ou path) e do cliente Rust roamswitch-linux-kit do SDK público (audit_secrets() para texto, audit_secrets_path() para um ficheiro ou diretório). O conteúdo nunca é transmitido para qualquer lugar.

Deteção e bloqueio de força bruta SSH (apenas enquanto o sshd estiver em execução)

O daemon da Client Edition executa o mesmo motor da Server Edition (ssh_guard_enabled, true por predefinição). Há três diferenças. Primeiro, trabalha apenas enquanto o sshd estiver em execução: todos os minutos verifica se algo escuta nas portas SSH e pára após três ausências seguidas. Numa máquina sem sshd não segue o registo nem toca em nenhuma tabela do nftables. Segundo, as definições são as mesmas 10 chaves (ssh_guard_*) com os mesmos valores predefinidos, guardadas em config.json e aplicadas em cerca de um minuto. Terceiro, as notificações são notificações de ambiente de trabalho e uma entrada no registo de incidentes do cliente; nunca inicia o Air-Gap. Uma origem cujas falhas atingem o limiar (por predefinição 6 em 600 segundos) perde as novas ligações, e as ligações SSH que já tinha abertas são cortadas ao mesmo tempo (uma sessão autenticada e uma origem isenta nunca são cortadas). Quando o fail2ban (com uma jail sshd ativa) ou o CrowdSec já protege o sshd, limita-se a detetar e notificar. Os bloqueios listam-se e levantam-se com sudo roamswitch bans e sudo roamswitch unban <IP>; não há ecrã GTK para isso, apenas config.json e os comandos. Nos níveis de proteção predefinidos do cliente o tráfego de entrada está fechado, pelo que as ligações podem nem chegar ao SSH.

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 distintos com entropia ≥ 7,92 em até 5 segundos, ele é tratado como criptografia em massa de ransomware e o processo é congelado com SIGSTOP. Em seguida, vêm uma notificação na área de trabalho, o registro na linha do tempo do incidente e a coleta de um pacote de evidências. Esta detecção também aciona o Air-Gap (o corte de toda a rede), como gatilho de alta confiança tal como a adulteração de canário e os eventos eBPF de alta gravidade.

Medido em uma máquina real (Ubuntu 24.04, kernel 7.0) com um programa de reprodução que reescreve 40 arquivos (medição de 2026-09-26, contra o daemon da versão que traz a detecção de criptografia parcial, a 1.10.7). Uma criptografia completa foi detectada depois do 20.º arquivo e o processo foi congelado no 31.º. Ela continua sendo detectada quando o processo se renomeia para python3 ou git: a lista de permissões agora verifica se o executável e o nome do script concordam com o nome, e não apenas o nome (antes, todos os 40 arquivos passavam despercebidos). cp -r, rsync -a, tar -x e scripts Python são identificados no momento em que a gravação é vista, portanto não geram mais uma detecção falsa mesmo terminando rápido (antes, já tinham encerrado quando a rajada era avaliada e eram notificados como “processo desconhecido”). A criptografia parcial (16 bytes de cada 32, o modo de gravação de ransomware do tipo LockBit) fica abaixo da média de 7,92 do arquivo inteiro e por isso antes não era detectada; desde a 1.10.7 ela é detectada. A regra procura blocos de 16 bytes que alternam entre aparência aleatória e aparência de texto; ela detectou 94% dos arquivos simulados e gerou 2 falsos positivos (0,01%) entre 19.997 arquivos comuns. Um programa de reprodução que grava em velocidade máxima, sem pausa entre os arquivos, termina os 40 arquivos antes de o limite de 20 ser avaliado, de modo que o processo já havia encerrado quando foi interrompido; com 0,2 segundo por arquivo, ele parou no 20.º. A criptografia apenas do início, a criptografia com trechos pulados e os dados criptografados gravados em Base64 não são contados como detecção: são apenas registrados em log e nada é congelado, pois classificariam arquivos comuns por engano. Um ransomware escrito com um programa genuíno da lista de permissões (como python3) também não pode ser distinguido pelo nome.

  • Separação entre a varredura YARA e o rastreamento de entropia. A inspeção de malware YARA é aplicada aos arquivos gravados, sem exclusão por extensão, incluindo ~/.cache/, node_modules/ e arquivos compactados (exceto os scan_exclusions definidos pelo usuário, o Vault de quarentena, /dev/shm e arquivos em memória ou excluídos, locais como /proc, /sys e /dev, e o que é gravado por processos do sistema, como o próprio daemon, o ClamAV e a base do desktop; apenas os primeiros 256 KB de um arquivo são lidos). Já a análise de rajada de entropia de ransomware exclui formatos naturalmente compactados (.zip, .tgz, .tar.gz, .gz, .png, .jpg, .mp4, .wasm etc.), arquivos que ainda começam com um cabeçalho legítimo de compactação ou de mídia (desde a 1.10.25) e o trabalho de cache de gerenciadores de pacotes verificados (npm, npx, cargo, rustc, dpkg, apt, tar etc.), para evitar congelamentos SIGSTOP por falsos positivos durante a extração por ferramentas legítimas.
  • Sobregravações repetidas do mesmo arquivo (por exemplo, três passagens de shred) contam como um evento.
  • Proteção de processos críticos. Os processos são comparados com uma lista fixa no código de processos protegidos, como systemd / dockerd / NetworkManager / sshd / gnome-shell (PROTECTED_PROCESS_NAMES); um processo cujo nome corresponde e cujo executável fica em uma área do sistema (como /usr ou /opt) nunca é congelado (evitando o travamento da máquina inteira). Um programa com nome parecido em /tmp ou /home não é protegido.

Regras da camada de aplicação web (web shells e código injetado)

Regras dedicadas detetam web shells PHP, JavaScript, JSP e ASP, código ofuscado injetado, PHP escondido em imagens e o sequestro da execução através de .htaccess. São independentes da deteção integrada de ELF e shell e escrevem-se numa linguagem pequena de literais, sequências de literais com afastamento limitado e duas estatísticas (uma linha extremamente longa, um bloco Base64 enorme). Sem expressões regulares, a avaliação não pode crescer descontroladamente nem bloquear; mesmo um ficheiro de regras hostil só pode causar falsos positivos. Nenhuma assinatura provém de uma amostra real nem está ligada a uma família de malware com nome: as 18 regras descrevem apenas técnicas documentadas publicamente (executar a saída de um descodificador, valores do pedido passados diretamente à execução, PHP executável em diretórios de envio, PHP dentro de uma imagem, atribuições de manipuladores em .htaccess, scripts injetados construídos com unescape e bibliotecas de mineração no navegador).

As regras têm três camadas: integradas, instaladas pelo atualizador diário (11.3) após a verificação da assinatura, e os seus próprios ficheiros em /etc/roamswitch/rules.d/*.json (só são lidos ficheiros pertencentes a root que nem o grupo nem outros possam escrever). As gravidades são critical, high, medium e low; critical e high passam por aprovação ou quarentena, e medium e inferiores são apenas comunicadas. A quarentena automática limita-se a critical por predefinição (quarantine em /etc/roamswitch/web_rules.conf); na edição Cliente, uma deteção ao escrever ou executar continua a ser decidida pelo utilizador na caixa de aprovação. Tudo o que está sob tests/, fixtures/ e .git/ é ignorado por predefinição, porque o código de teste cita as mesmas construções.

Após uma deteção, um executável colocado em /etc/roamswitch/hooks/post-detection recebe os resultados em JSON na entrada padrão (só é executado um ficheiro pertencente a root, não escrevível e num diretório seguro; sem shell, ambiente mínimo e paragem aos 30 segundos). A resposta opcional sobre contas (desativada por predefinição) revoga a shell de início de sessão do dono de um ficheiro posto em quarentena. Não faz nada com root, contas abaixo de account_min_uid (1000 por predefinição), utilizadores com sessão iniciada nem o utilizador do daemon; a shell anterior é registada e roamswitch web-rules unlock-account repõe-na. Falsos positivos medidos: 0 no núcleo do WordPress (2409 ficheiros), no phpMyAdmin (3356 ficheiros) e em dependências npm (14 743 ficheiros). Limites: só são lidos os primeiros 256 KB (ao escrever/executar) e 4 MB (análise), nomes fragmentados como 'ev'.'al' não são seguidos e, por falta de amostras reais, não se incluem assinaturas de malware concreto.

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.

Pacotes de captura forense de provas

Somente quando a detecção de rajada de entropia de ransomware é acionada, ou quando um evento de gravidade Critical do sensor de ameaças em tempo de execução eBPF resulta em uma contenção pontual bem-sucedida por processo (SIGSTOP + isolamento de rede), o roamswitch-incident-capture é iniciado e salva um pacote de evidências em <diretório de estado>/incident-evidence/<carimbo de data/hora>-<ID curto>/. O que é coletado: uma captura de pacotes limitada de 30 segundos com o tshark na interface de rota padrão (registrada como ignorada se o tshark não estiver instalado) e, quando um PID é repassado, um snapshot de /proc/<pid> do processo alvo (status, cmdline, maps, valores que parecem segredos substituídos por [REDACTED] no environ, uma lista dos destinos dos links dos descritores de arquivo abertos, sem coletar o conteúdo, e a cadeia de processos ancestrais). A falha na coleta de um único artefato não é tratada como erro fatal; ela é registrada e a coleta do restante continua. Nos eventos eBPF, o PID do processo alvo é repassado. Na detecção de rajada de entropia, o PID é repassado quando foi possível identificar o processo que gravou; se esse processo já tinha encerrado quando a rajada foi avaliada, a ferramenta é iniciada sem PID e coleta apenas os pacotes. A coleta de evidências não é executada quando a contenção pontual é impossível ou falha e recorre ao isolamento Air-Gap de todo o host, nem em eventos de nível Emergency/Alert que acionam diretamente o isolamento de todo o host. Os pacotes de distribuição até a 1.10.4 não incluíam o roamswitch-incident-capture, necessário para esta funcionalidade, e por isso ela não funcionava em instalações feitas a partir dos pacotes. Ele é incluído desde a 1.10.5.

Os resultados são reunidos em manifest.json, que também incorpora o hash SHA-256 de cada um dos restantes ficheiros do pacote (para permitir detetar adulterações posteriores). Os pacotes nunca são eliminados automaticamente — os pacotes antigos só são removidos quando o comando roamswitch-incident-capture prune (retenção predefinida: 30 dias) é executado explicitamente.

As provas recolhidas não são verificadas no momento da captura. Para confirmar posteriormente se houve adulteração, execute roamswitch-incident-capture verify <diretório do pacote> [--json]. O comando recalcula o hash de cada ficheiro atualmente presente no pacote e compara-o com os valores registados em manifest.json, reportando cada discrepância individualmente como modificado, em falta ou não registado (um ficheiro presente que o manifesto nunca registou).

Limitação conhecida — esta verificação compara o SHA-256 de manifest.json com registos guardados fora do pacote, em dois sítios: uma cadeia de hashes fora do pacote (evidence_anchor.log, cada linha apoia-se na anterior, só de acrescentar quando o sistema de ficheiros o permite) e o registo do sistema (etiqueta roamswitch-evidence). Reescrever manifest.json após a captura faz falhar o verify; editar ou remover uma linha da cadeia também, porque a cadeia se quebra. Não trava um atacante com root que consiga reescrever tanto a cadeia como o destino para onde o registo do sistema é reencaminhado; recomenda-se reencaminhar o registo do sistema para outra máquina. A assinatura não está implementada. Um pacote sem registo (capturado por uma versão anterior) é indicado como «sem registo», ou seja, não tem confirmação externa.

Honeytokens (iscos de credenciais)

No arranque do daemon, o RoamSwitch implementa de forma idempotente ficheiros de credenciais-isco em caminhos reais conhecidos, efetivamente vasculhados por atacantes e ferramentas automatizadas de roubo de credenciais — ~/.aws/credentials, ~/.ssh/id_rsa e ~/.docker/config.json, ~/.pypirc, ~/.env.backup (na pasta pessoal do utilizador que invoca o processo) —, cada um contendo um marcador ROAMSWITCH_HONEYTOKEN. Um ficheiro real já existente em qualquer um destes caminhos nunca é substituído (a implementação verifica sempre primeiro se o ficheiro já existe). O acesso real a um honeytoken pode também desencadear a implementação em cascata de iscos adicionais relacionados. Os pacotes até à 1.10.4 não incluíam roamswitch-honeytokens, necessário para esta funcionalidade, pelo que não era executada nas instalações a partir dos pacotes. Está incluído desde a 1.10.5. Desde a 1.11.1, os comandos docker que o próprio RoamSwitch executa são iniciados com um diretório dedicado vazio como DOCKER_CONFIG, pelo que não acionam o isco ~/.docker/config.json. A leitura por qualquer outro programa continua a ser detetada como antes.

Um monitor em segundo plano usa fanotify de classe de notificação (marcas FAN_OPEN/FAN_ACCESS por arquivo em cada isca) para vigiar as leituras e aberturas de cada isca; onde o fanotify não está disponível (por exemplo, quando não é executado como root), ele recorre ao inotify. Como nenhum processo legítimo tem motivo para ler esses arquivos, um acesso é tratado como um forte sinal de reconhecimento pós-comprometimento por um invasor (MITRE ATT&CK T1552); o RoamSwitch tenta identificar o processo que acessou e registra o evento na linha do tempo do incidente com gravidade Critical (nenhum pop-up na área de trabalho é exibido; ele é encaminhado se o encaminhamento de eventos estiver ativado). Os acessos pelos próprios executáveis do RoamSwitch e, nas iscas SSH, pela família de clientes OpenSSH (ssh, scp, sftp, ssh-add, ssh-agent) não são registrados, para evitar falsos alarmes; um acesso cujo executável não pode ser identificado é registrado. A funcionalidade só registra; ela mesma não congela nem bloqueia o processo que acessou.

Limitação conhecida. O corpo da isca ~/.ssh/id_rsa contém texto legível e continua sendo um corpo base64 inválido para uma chave privada OpenSSH real. No entanto, a maioria dos scanners de segredos sinaliza arquivos apenas pela correspondência com o cabeçalho -----BEGIN ... PRIVATE KEY-----, por isso avaliamos que o impacto real na taxa de detecção é limitado. Além disso, o processo que acessou normalmente é identificado pelo PID que o evento do fanotify carrega; somente quando o fanotify não está disponível e o monitor recorre ao inotify é usada uma chamada a fuser, que pode não obter o PID nem o nome de um processo de vida muito curta que encerra logo após ler o arquivo. Nesse caso o processo é simplesmente registrado como desconhecido, o que não afeta a detecção do acesso em si (o evento continua sendo disparado e registrado).

monitoramento fanotify · Cofre de quarentena · ClamAV

  • Com FAN_CLASS_CONTENT (modo de permissão; se indisponível, FAN_CLASS_NOTIF), o daemon monitora /home, /tmp, /var/tmp e /dev/shm com marcas no nível do sistema de arquivos (desde a 1.10.18; uma marca no nível de montagem serve apenas de alternativa quando uma marca de sistema de arquivos é recusada) e acompanha a conclusão de gravações em / como um todo, também no nível do sistema de arquivos. Desde a 1.10.4, os eventos de permissão abrangem apenas a execução (FAN_OPEN_EXEC_PERM) e open é tratado como notificação (fanotify_open_perm restaura o comportamento anterior). Os executáveis já analisados como limpos ficam em cache por 60 segundos e os processos filhos do próprio daemon são excluídos do monitoramento.
  • Notify-first, sem quarentena antes da confirmação (guarda fanotify). No guarda fanotify, um simples acerto de YARA / ClamAV nunca resulta em quarentena sem confirmação nem em FAN_DENY. Uma detecção de assinatura real coloca uma solicitação modal (Quarentena / Permitir / Mais tarde) em /run/roamswitch/approvals.json; nada entra no Vault até que o usuário confirme. Somente um execve real, e apenas quando pre_exec_blocking_enabled está ativado, recebe FAN_DENY. O tempo limite da aprovação é fail-open. O monitoramento de downloads (por padrão ~/Downloads e ~/Desktop, mais watched_folders) é um caminho separado: um arquivo que o YARA ou o ClamAV detecta (exceto o EICAR) é movido imediatamente para o Vault de quarentena, sem confirmação, e uma notificação é emitida.
  • 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_exclusions configurada pelo usuário (caminhos absolutos, aplicados recursivamente; inclui o legado clamav_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 (no guarda fanotify, apenas o que o usuário confirmou) é movido para o diretório /var/lib/roamswitch/quarantine/, exclusivo do root (antes era o ~/.local/share/roamswitch/quarantine/ de cada usuário; o local antigo é migrado automaticamente na inicialização). O diretório do Vault pertence ao root com 0700; cada amostra pertence ao root com 0400 (somente leitura, não executável). Os usuários só podem operar o Vault por meio do IPC do daemon e só listam, restauram e excluem os itens que eles mesmos colocaram em quarentena. Uma restauração é gravada por um processo filho que rebaixou seus privilégios aos do usuário que fez a chamada, como um arquivo 0644 (um arquivo existente nunca é sobrescrito).
  • O loop de eventos de permissão nunca lê o arquivo de configuração. A configuração é lida por uma thread dedicada em segundo plano para um snapshot Mutex<GuardConfig>; os acessadores de configuração do caminho crítico apenas bloqueiam e copiam (a leitura do início do arquivo prestes a ser executado, para julgá-lo, é uma etapa separada). É um invariante de projeto: atrasar o FAN_OPEN_EXEC_PERM congelaria a máquina.
  • Quando o ClamAV está presente (clamav_enabled, ativado por padrão), os arquivos novos que aparecem nas pastas monitoradas pelo monitoramento de downloads (por padrão ~/Downloads e ~/Desktop) são verificados no banco de dados local, após a varredura YARA integrada. Executáveis no estilo Windows, como .exe, geram uma notificação; formatos de distribuição do Linux (.sh, .deb, .elf etc.) são apenas registrados no histórico, e modelos de IA perigosos (.pkl, .pt) geram uma notificação com a origem. O ClamAV é uma dependência opcional; as demais defesas funcionam sem ele. Quando instalado, o atualizador diário (§11) executa o freshclam para manter o banco de dados de assinaturas atualizado.

Registrador de execuções de processos (somente local)

Um registrador estritamente local que grava eventos exec e mudanças de uid por meio do conector de processos do netlink (proc connector). Ele registra o caminho do executável, os argumentos da linha de comando (limitados a 64 argumentos, 512 caracteres cada e 4096 no total, com caracteres de controle escapados), a linhagem de processos pais, o uid, o diretório de trabalho, o ID do contêiner, o SHA-256 do executável etc. Fica ativado por padrão, mas desativado por padrão em máquinas com menos de 1 GiB de RAM (uma configuração explícita de exec_recorder_enabled prevalece). O uso de CPU do próprio registrador é limitado a 3% de um núcleo (exec_recorder_cpu_budget_pct) e ele roda com nice 10. Se o conector de processos não puder ser aberto, ele recua para uma varredura pouco frequente de /proc (que perde processos de curta duração).

  • Armazenamento — os registros ficam em /var/lib/roamswitch/exec_log como JSON Lines divididos em segmentos de 8 MiB (0600). A retenção padrão é de 200 MB / 14 dias (exec_recorder_retention_mb / exec_recorder_retention_days). Uma cadeia de hashes torna a adulteração detectável, mas não evitável (um invasor que obtenha root pode reescrever toda a cadeia). A busca é feita com roamswitch events search|tree|export|verify; as ferramentas MCP somente leitura para clientes de IA são search_exec_events e get_process_tree.
  • Regras de correlação somente de notificação (EXR-001 a EXR-010). São dez regras: um shell iniciado por um serviço web, execução a partir de /tmp, de um arquivo em memória (memfd) ou de um arquivo excluído, execução logo após um download, um one-liner base64/eval, obtenção de uid 0 sem um caminho setuid, execução de um binário colocado logo após uma alteração de cron/systemd, execução após uma alteração em /etc/ld.so.preload, um snapshot local do sistema de arquivos (btrfs, LVM) excluído por algo que não seja uma ferramenta de gerenciamento conhecida, como snapper ou timeshift (EXR-008, 1.10.11), um mesmo originador procurando 8 ou mais ferramentas de ataque com which e semelhantes em até 30 segundos (EXR-009, 1.11.0) e um interpretador de scripts como python ou node iniciando 3 ou mais ferramentas de ataque em até 2 minutos (EXR-010, 1.11.0; o que uma pessoa digita sob sshd, login ou um terminal é excluído). Cada regra traz um ID de técnica do MITRE ATT&CK. As regras apenas notificam e nunca isolam por conta própria; somente se um operador ativar explicitamente exec_rules_isolation_optin (false por padrão) uma regra de alta gravidade é tratada como acionador de Air-Gap de alta confiança (§4).
  • Nada sai da máquina — o log bruto nunca é encaminhado. Somente se o encaminhamento de eventos (§9.4) estiver ativado são enviadas as notificações de regras (gravidade, ID da regra, nome do processo, resumo) e os hashes de selagem dos segmentos, e apenas para o destino que o operador configurou.

Monitorização do acesso a ficheiros de credenciais (ativada por predefinição, apenas notificação)

Ativado por padrão (credential_access_watch_enabled é true; defina como false para desativar, ou use a chave na tela do guarda de malware). Notifica quando um programa inesperado abre uma chave privada SSH (~/.ssh/id_*), ~/.aws/credentials, os arquivos de credenciais em ~/.config/gcloud/ (quatro tipos, como credentials.db) ou o repositório de credenciais/cookies de um navegador importante (somente notificação). Ele apenas define o evento de classe de notificação do fanotify FAN_OPEN em poucos arquivos individuais, nunca bloqueia, congela nem nega nada, e o kernel não espera resposta. O nome do executável do programa que abriu o arquivo é comparado com listas de permissões por tipo (ssh / git / gpg-agent, navegadores, aws / terraform / kubectl etc.), e as notificações repetidas para o mesmo par (programa, arquivo) são suprimidas por 10 minutos. Ferramentas de backup (restic, borg etc.) e indexadores (tracker, baloo etc.) são permitidos desde o início. Adicione a credential_access_watch_allow, no config.json, qualquer outro programa para o qual você não queira notificações. Para as credenciais da AWS, as ferramentas que rodam como python, python3 ou ansible, como scripts boto3 e playbooks do Ansible, são permitidas (para o gcloud, python e python3); um one-liner como python -c ainda notifica. O preço dessa permissão é que um programa de roubo de credenciais escrito como arquivo de script python também não gera notificação. Outros interpretadores, como node ou ruby, e ferramentas genéricas de busca e sincronização, como grep ou rclone, deliberadamente não são permitidos, porque um programa que rouba credenciais tem exatamente o mesmo aspecto; por isso podem gerar notificações. Uma leitura muito curta, como cat ou head, em que o programa termina antes que o RoamSwitch consiga identificar o seu executável, pode não ser notificada (uma leitura feita por um programa que continua em execução por algum tempo é notificada). O RoamSwitch não usa o fanotify de classe de permissão para evitar esse limite, porque isso poderia travar o sistema. As alterações têm efeito em cerca de 30 segundos, sem reinício.

A via de intrusão nas notificações de adulteração de ficheiros

As notificações da vigilância de ficheiros críticos (FIM) do cliente e da vigilância de ficheiros de bloqueio de dependências também indicam o processo que fez a alteração (conta, executável, comando) e o início de sessão SSH a que pertence (origem, hora, método de autenticação). É fim_intrusion_context_enabled em config.json, true por predefinição; não é enviado nada para o exterior. A notificação de um acesso a um ficheiro de credenciais acrescenta uma linha apenas quando o processo pertence a um início de sessão SSH. O funcionamento, os valores em que confia e as três respostas que distingue (encontrado / não foi encontrado nenhum início de sessão SSH / não foi possível determinar o caminho de acesso) são os mesmos da Server Edition. O que não pôde ser verificado nunca é apresentado como «não é um início de sessão». O registo fica em /var/lib/roamswitch/fim_intrusions.json e no campo recentIntrusionContext da ferramenta MCP verify_fim.

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.

  • Tratamento de namespaces de usuário sem privilégios (mitigação do Frag Gap CVE-2026-53362). Muitas das vulnerabilidades LPE modernas do kernel Linux se baseiam em usuários sem privilégios que chamam unshare(CLONE_NEWUSER) para criar namespaces de usuário isolados e, em seguida, acessam estruturas do kernel não inicializadas ou subsistemas de rede legados com capacidades elevadas dentro do namespace. O diagnóstico verifica e exibe o estado de user.max_user_namespaces e kernel.unprivileged_userns_clone, mas, como desativá-los pode quebrar navegadores, Flatpak, Electron e contêineres rootless, a edição Client não recomenda isso e o deixa fora do reforço com um clique (se user.max_user_namespaces = 0 estiver definido, o relatório orienta a restaurar o valor padrão). A desativação é uma medida provisória que só fecha os caminhos de ataque que precisam de um namespace de usuário; ela não impede outras vias. A configuração de kernel.unprivileged_userns_clone = 0 somente enquanto se está em uma rede não confiável é aplicada apenas quando você a ativa explicitamente em guard.yaml, na opção adaptive_security.untrusted_network_actions.auto_enable_userns_restriction (desativada por padrão; não faz nada em kernels sem essa configuração). A edição Server a aplica de forma permanente por padrão (harden_userns_enabled). A solução real é atualizar para um kernel corrigido e reiniciar.
  • Yama LSM Memory Protection — A aplicação de kernel.yama.ptrace_scope = 1 (ou 2) evita que processos não autorizados sejam anexados via ptrace ou 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 = 0 evita que processos sensíveis travados gravem dumps de memória de texto simples no disco.
  • /tmp e /dev/shm noexec Mount Defense — Para evitar ataques de queda e execução de carga útil, o RoamSwitch remonta /tmp e /dev/shm com noexec,nosuid,nodev ao 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 2MB)
    ▼
[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]
    ├─► ② Bloqueio de rede: Critical bloqueia apenas o usuário desse processo (roamswitch_isolate), Emergency/Alert usam 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.
  • Resposta autônoma em milissegundos (congelamento de processo e bloqueio de rede). Quando um evento com prioridade Critical (como LPE do kernel, escalação de privilégio ou criptografia por ransomware) é recebido, o daemon envia SIGSTOP ao PID em questão para parar o processo por completo (estado: Ts), faz um “isolamento pontual” que bloqueia, na tabela roamswitch_isolate, apenas o tráfego do usuário real desse processo (ou do seu cgroup, no caso de um contêiner), e emite uma notificação na área de trabalho. Se nenhum PID puder ser identificado, se o congelamento for recusado porque o processo é protegido, se o isolamento falhar (por exemplo, em um processo root) ou se a prioridade for Emergency / Alert, o daemon aplica, na tabela inet roamswitch, policy drop (Air-Gap) e, se o PID for conhecido, também envia SIGSTOP. Eventos abaixo de Critical não bloqueiam nada e são apenas registrados no histórico.
  • Ajuste de baixo overhead para desktop e prevenção de inchaço de logs (RoamSwitch Optimized) Para reduzir o consumo de bateria, a carga de CPU e o esgotamento de disco em laptops e estações de trabalho, um perfil otimizado do Falco (99-roamswitch-optimized.yaml) e uma política de logrotate são fornecidos para ambientes que executam o Falco. Os pacotes da edição Client e o modo desktop do install.sh não instalam o Falco e não colocam esse perfil (os pacotes da edição Server e o install.sh --server o fazem). Se você usa o Falco com o Client, instala o Falco por conta própria e, se quiser, coloca o perfil você mesmo. As principais configurações são as seguintes.
    • 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.
  • Resiliência híbrida (reforço autônomo vs. extensão eBPF). Mesmo sem o Falco instalado, o sistema se defende de forma autônoma com sysctl do kernel (restrição de ptrace do Yama, supressão de core dump e proteções de rede, aplicadas a cada troca de perfil), perfis nftables, fanotify e monitoramento de canários. Com o Falco instalado, somam-se a interceptação de zero-days e o congelamento de processos por eventos eBPF em tempo real, para a defesa de máxima intensidade.

§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 0 em /sys/bus/usb/devices/*/authorized para 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 gravar authorized=0 em qualquer dispositivo que exponha uma interface HID (bInterfaceClass 03), e o daemon reautoriza qualquer dispositivo HID authorized==0 na 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 EVIOCGRAB exclusivo é obtido no /dev/input/eventN desse dispositivo (apenas nós com um manipulador kbd em /proc/bus/input/devices, correspondendo a VID:PID). O nó do mouse permanece intacto. Este é o equivalente Linux do macOS CGEventTap; 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.
  • Caixa de aprovação — junto com a captura, um pedido de aprovação é colocado em /run/roamswitch/approvals.json e a GUI mostra um modal (Permitir / Negar). Permitir adiciona o dispositivo à lista de permissões e liberta a captura. Se ninguém aprovar em 3 minutos, o teclado deixa de ser libertado por predefinição (desde a 1.10.4; as versões anteriores falhavam em aberto após 3 minutos, e ativar usb_keyboard_timeout_release restaura esse comportamento).

Armazenamento em massa USB não autorizado

Quando um dispositivo de armazenamento em massa USB não registrado (sem interface HID) é inserido e o guarda está ativado, ele é mantido em espera com authorized=0 (seguro, pois não tem HID) e uma caixa de diálogo de aprovação é exibida, independentemente de a rede ser confiável ou não. Uma unidade que está montada ou em uso como swap (um disco externo em uso, um pen drive live) não é mantida em espera, porque isso equivaleria a uma remoção abrupta que pode danificá-la; ela é apenas registrada no log. Permitir reautoriza e inclui na lista de permissões; Negar mantém a espera. A versão para macOS foi alinhada a este modelo “reter em somente leitura + prompt de aprovação” na 1.7.4.

Identificação e direitos adquiridos

O HID é determinado a partir de bInterfaceClass == "03" e de /proc/bus/input/devices, na linha Handlers=. Um receptor combo (um VID:PID com uma seção de teclado e uma de mouse) é mesclado por VID:PID. O grandfathering (registrar os teclados existentes na lista de permissões) é feito pela GUI, não pelo daemon. Os teclados conectados naquele momento são registrados na lista de permissões quando você ativa o guarda no assistente de configuração inicial (e em uma migração única para uma instalação existente com o guarda ativado, configuração inicial concluída e nenhum teclado conectado na lista de permissões). Ativar o guarda depois, na aba USB, não registra nada. Um teclado que está conectado na inicialização do daemon, mas não registrado, é tratado como não registrado, pois pode ter sido trocado antes de uma reinicialização, e é capturado (grab) se houver outro teclado disponível para digitar.

§7

No roamswitch-core, o LinuxHealthChecker avalia 27 itens e produz uma pontuação de 0 a 100, uma nota S/A/B/C (S para 100, A para 85 a 99, B para 70 a 84, C abaixo disso) e conselhos por item. No painel GTK, na CLI (roamswitch status) e no MCP (get_security_report), o título, os detalhes e a recomendação de todos os 27 itens aparecem no idioma do usuário (10 idiomas).

Categorias de avaliação
CategoriaItens de exemplo
Disco / inicializaçãoCriptografia LUKS/dm-crypt, inicialização segura UEFI
Controle de acessoAuditoria de configuração LSM (AppArmor/SELinux), SSH/sudo
Endurecimento do kernelreforço de sysctl, verificação do estado dos namespaces de usuário sem privilégios (mitigação do Frag Gap; a edição Client não recomenda desativá-los), restrição de ptrace do Yama, controle de core dump, noexec em /tmp e /dev/shm
Atualizaçõesconfiguração automática de atualização de segurança
RedeMonitoramento de falsificação de ARP, estado de bloqueio ARP/NDP do gateway, auditoria de porta exposta, força de criptografia Wi-Fi
Programas maliciososClamAV (incluindo um teste real de detecção com EICAR) / fanotify (verifica também se o guarda está de fato em execução e se nenhum evento foi perdido por estouro da fila) / monitoramento de entropia / noexec / verificação da instalação dos programas auxiliares (honeytokens e coleta de evidências)
NavegadorConfigurações de navegação segura do Firefox / Chrome / Chromium
DNS/USBDNS de proteção contra ameaças, estado de confiança zero do barramento USB
Contentores e vulnerabilidadesExposição por montagem do socket do Docker, auditoria de contentores privilegiados (--privileged), nível de isolamento do runtime de contentores (runc / gVisor / Kata), exposição a CVE conhecidas do kernel em execução

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.

Mapeamento NIST CSF 2.0 / CIS Controls v8

Cada um dos 27 itens recebe um identificador de função/categoria do NIST CSF 2.0 (por exemplo, PR.DS-01, DE.CM-09) e, apenas nos itens em que há uma correspondência confiável, também o número de controle do CIS Controls v8 (por exemplo, 3.11, 4.1). Os itens com correspondência ambígua não são forçados e permanecem sem mapeamento. A saída do MCP (get_security_report) e a saída JSON da CLI (roamswitch status --json) incluem esse mapeamento (nistCsf / cisControl). Ele não aparece na GUI nem na saída normal da CLI da edição Client (a saída de roamswitch status --server da edição Server o mostra). Isso permite que operadores e auditores que precisam de cobertura de frameworks de conformidade associem mecanicamente as verificações do RoamSwitch aos frameworks existentes.

§8

roamswitch-mcp fala JSON-RPC sobre stdio e é iniciado por clientes de IA como Claude Desktop, Cursor e Antigravity. Cada ferramenta é só de leitura e quase todas se concluem localmente. A única ferramenta que pode ligar-se a um anfitrião externo é a run_npm_audit_signatures, desativada por predefinição. A avaliação de get_security_report e verify_security_findings não envia nada para a LAN, e o gateway só é lido da cache ARP e de outras informações locais. Um ping só pode sair noutros processos que voltam a resolver o MAC do gateway (monitorização ARP do daemon, restauro de emergência, emparelhamento do Sensor, get_guard_status e semelhantes).

Principais ferramentas expostas (lista completa: Configuração do MCP)
FerramentaObjetivo
get_security_reportAvaliação de 27 itens, pontuação, recomendações
verify_security_findingsReavalia os itens (checkId) do get_security_report com base no estado atual e devolve um veredicto por item, stillPresent (continua a aplicar-se), resolved (resolvido, ou não aplicável por conceção) ou inconclusive (não foi possível determinar), juntamente com um código de motivo independente do idioma. Serve para confirmar, depois de uma correção, que esta teve mesmo efeito. Um item que não foi possível medir, por exemplo por falta de privilégios, porque o helper privilegiado não está ligado ou porque o Docker não está acessível, nunca é comunicado como seguro e é devolvido como inconclusive. Se omitir checkIds, são verificados todos os itens (até 50 identificadores no Mac e 64 no Linux; no Linux também é aceite isServer). No Mac, host_firewall, network_stealth_mode, gateway_arp_lock, malware_scanning, dns_threat_guard e usb_zero_trust são decididos pelas definições da app RoamSwitch, pelo que refletem essas definições e não são uma nova medição do estado real do sistema operativo. É só de leitura, não se liga a anfitriões externos e não repara nada nem altera definições. No Linux também não envia nada para a LAN e só lê o gateway das caches locais. No Mac, para obter o endereço MAC do gateway, pode enviar um ping ICMP, normalmente um (alguns se o MAC não for resolvido), ao gateway da LAN.
audit_mcp_configsLê os ficheiros de configuração MCP dos clientes de IA (Claude Desktop, Claude Code, Cursor, Windsurf, VS Code, Cline, Gemini CLI, além de .mcp.json e ficheiros semelhantes nas pastas de projeto registadas) e assinala definições de risco. Deteta ferramentas aprovadas automaticamente sem confirmação em cada chamada (auto_approve), servidores http:// em texto simples num anfitrião que não é loopback, indicados quer no campo url quer como argumento de arranque, como --server http://… (remote_plain_http), comandos de arranque que são linhas únicas de shell ou intérprete (shell_wrapper), lançadores como npx / uvx / docker run que obtêm um pacote ou imagem sem versão fixa (unpinned_package_launcher), docker run com --privileged ou com o socket do Docker ou o sistema de ficheiros raiz montados (container_host_access), as variáveis de ambiente que carregam código adicional no arranque, como LD_PRELOAD ou --require em NODE_OPTIONS (nomes sem distinção de maiúsculas, valores nunca devolvidos; code_loading_env), entradas que apontam para o HexStrike AI, um backend de ferramentas de ataque sem autenticação (hexstrike), ficheiros de configuração graváveis pelo grupo ou por outros (world_writable_config), a opção enableAllProjectMcpServers do Claude Code (auto_trust_project_servers) e um ficheiro de configuração que reúne um servidor que lê textos escritos por outras pessoas (páginas web, e-mails, conversas, issues) e um servidor que pode executar comandos ou alterar ficheiros, a combinação de que a injeção de prompt indireta precisa (reconhecida por nomes de pacote conhecidos, sem ver o conteúdo das ferramentas do servidor; injection_exposure). Não tem argumentos, é só de leitura e não envia nada para a rede. Nunca devolve valores de ambiente ou de cabeçalhos, argumentos nem segredos, apenas nomes de servidores, o nome base de um comando, um anfitrião de URL e, se for um nome de registo simples, o nome do pacote ou da imagem. Os ficheiros que não foi possível analisar ficam listados em unreadableFiles e não foram auditados.
audit_agent_setupLê os ficheiros de instruções e as skills dados a agentes de IA (CLAUDE.md, commands, agents e skills em ~/.claude, além de ~/.codex, ~/.cursor/rules, ~/.gemini, ~/.agents, ~/.openclaw, e CLAUDE.md, AGENTS.md, .cursorrules e semelhantes nas pastas de projeto registadas) e o openclaw.json do OpenClaw, e assinala conteúdo e definições de risco. Nos ficheiros de instruções e de skills reporta frases escondidas em carateres de etiqueta Unicode invisíveis (hidden_tag_characters), carateres de largura zero e de controlo de direção (invisible_characters), redação que anula instruções anteriores ou esconde algo do utilizador (override_instruction), descarregar e executar como curl … | sh (remote_script_exec), descodificar e executar (encoded_payload_exec), referências a armazéns de chaves e credenciais (credential_access) junto de um envio ou de um serviço de receção (credential_exfil_shape, exfil_endpoint), configuração de mecanismos persistentes (persistence_instruction), código compilado dentro de uma skill (skill_precompiled_code) e ficheiros que outros podem escrever (agent_file_writable). O que estiver dentro de um comentário HTML é avaliado mais alto, porque uma página renderizada o esconde. Para o OpenClaw reporta autenticação desativada (openclaw_auth_disabled), uma escuta acessível a partir da rede (openclaw_wide_bind), um token com menos de 24 carateres (openclaw_weak_token), uma origem universal (openclaw_origin_wildcard), interruptores dangerously… ligados (openclaw_dangerous_flag), uma lista de proxies que confia em todos (openclaw_broad_trusted_proxy, openclaw_proxy_trust_loosened), uma política de mensagens diretas ou de grupo aberta a qualquer pessoa (openclaw_open_dm), mDNS em full (openclaw_mdns_full), resultados de autodiagnóstico ocultos (openclaw_audit_suppressed) e permissões demasiado largas (openclaw_loose_permissions). Cada regra é determinística: uma correspondência significa «uma pessoa deveria ler esta linha», não «malicioso». Não aceita argumentos, é só de leitura e não envia nada para a rede. Nunca devolve o conteúdo dos ficheiros nem segredos (um token é reportado apenas pelo comprimento), apenas um nome de regra, um número de linha, um anfitrião de URL e um caminho de chave de configuração. Os ficheiros que não foi possível ler ficam em unreadableFiles e não foram auditados.
get_exposed_portsportas expostas e seu status de bloqueio de firewall
get_guard_statusnível de proteção atual e estado do módulo de defesa
audit_url_safetyanálise heurística de segurança de URL (não busca o alvo)
audit_secretsdetecção de chaves/segredos de API em texto (não transmite o texto)
audit_security_logsagregação de logs locais de diário/auth
get_quarantine_status /get_canary_statusquarentena e estado canário
get_app_helppesquisa da base de conhecimento integrada
run_active_vuln_scanVerificação ativa de vulnerabilidades, não destrutiva e limitada a 127.0.0.1. Não contacta nenhum outro host, mas envia pedidos de rede, por isso está desativada por predefinição e requer ativação explícita nas definições.
run_package_cve_scanCompara os pacotes instalados (Mac: Homebrew / Linux: dpkg, dnf, zypper, pacman) com um mapa de CVE local. Não ocorre qualquer comunicação de rede. No Linux, se a lista de pacotes não puder ser lida ou o tempo se esgotar, não é comunicado «sem correspondências»; complete passa a false e incompleteReason indica o motivo (um resultado incompleto não é um resultado limpo). No Ubuntu (20.04, 22.04, 24.04, 26.04) usa os dados próprios do Ubuntu, compara por nome e versão do pacote de origem e verifica apenas as CVE com CVSS 7,0 ou superior para as quais o Ubuntu já publicou uma correção, pelo que «sem correspondências» não significa que não existam CVE conhecidas. O resultado acrescenta ecosystem e release, cada deteção traz binary (o nome do pacote binário instalado) e as correções disponíveis apenas com o Ubuntu Pro têm proOnly. As derivadas do Ubuntu (Linux Mint, Pop!_OS e outras) e as versões não suportadas devolvem complete = false com o motivo em incompleteReason, sem mudar para os dados do Debian.
run_package_cve_scan_languagesCompara 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.
get_vulnerability_scan_historyDevolve, para cada sonda registada pelo scan-vulns --confirm, o número de dias decorridos desde a última execução e confirmação efetivas (pass_age_days).
verify_fimRecalcula o hash de 23 arquivos críticos do sistema fixos, de todos os arquivos diretamente em 12 diretórios (autenticação, cron, systemd, configuração SSH etc.), dos binários do próprio RoamSwitch e do authorized_keys de cada usuário, e os compara com a linha de base para verificar adulterações e novos arquivos. Quando executada como root, o resultado inclui também um campo adicionado, recentIntrusionContext: para cada alteração recente, o processo que a fez (conta, executável, comando) e o início de sessão SSH a que pertence (origem, hora do início de sessão, método de autenticação), ou o motivo pelo qual não foi possível apurá-lo.
get_file_scan_guard_statusRetorna a configuração do File Scan Guard (ClamAV) e o estado do Vault de quarentena que ele usa (ferramenta da edição Server; não informa as configurações do guarda de malware da edição Client).
get_port_anomaly_incidentsDevolve 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_incidentsDevolve o estado de isolamento atual da guarda de execução eBPF e o historial de incidentes que lhe deu origem. Na Server Edition cada registo traz também o tempo desde a deteção até à resposta em milissegundos (detect_to_receive_ms, receive_to_action_ms, detect_to_action_ms), e o resultado inclui o estado do heartbeat do Falco (heartbeat).
get_ssh_guard_incidentsDevolve o estado e o histórico da guarda contra força bruta SSH: se bloqueia ou apenas deteta (por exemplo, porque outra ferramenta já protege o sshd), as origens bloqueadas neste momento com o tempo restante e até aos 200 registos mais recentes (uma origem foi bloqueada, foi detetada sem bloqueio, ou um início de sessão teve êxito depois de uma série de falhas da mesma origem). Os nomes de utilizador experimentados não são guardados no Linux (apenas quantos) e no Mac são devolvidos higienizados, até 5. O Mac devolve ainda os inícios de sessão SSH recentes (origem, utilizador, método, impressão digital da chave). Só de leitura: nenhuma ferramenta MCP levanta um bloqueio. No Linux a lista de bloqueios só pode ser lida por root, pelo que quem chama sem root recebe apenas o número. Linux: a Server Edition e a Client Edition enquanto o sshd estiver em execução. Mac: apenas Pro.
get_resource_guard_incidentsRetorna o histórico do guarda de esgotamento de recursos e anomalias de processos (vazamentos de memória, loops de falha), com um nível de confiança (este guarda pertence à edição Server; a edição Client não o executa, portanto o resultado ali é sempre vazio).
get_notification_historyDevolve 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_timelineReú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).

A verificação ativa de vulnerabilidades regista também, para cada sonda confirmada, quantos dias decorreram desde a última execução e confirmação efetivas (pass_age), distinguindo assim «esta verificação existe» de «esta verificação foi mesmo confirmada recentemente». Ainda não existe um executor agendado, pelo que este valor reflete apenas a frequência com que a análise foi realmente executada. O mesmo número de dias decorridos está disponível através da CLI (roamswitch vuln-status), da GUI e do MCP (get_vulnerability_scan_history), permitindo a um operador ou auditor perceber quando foi efetivamente feita a última verificação, em vez de presumir uma verificação recente apenas porque a funcionalidade existe.

O próprio servidor MCP não abre rede. A única ferramenta que se conecta diretamente ao soquete Unix do daemon (/run/roamswitch/roamswitch.sock) é a get_sensor_audit_results; todas as outras chamam diretamente a lógica do roamswitch-core ou apenas leem arquivos de estado gravados pelo daemon (o roamswitchkit funciona no sentido oposto: é o SDK que a CLI e outros usam para iniciar o roamswitch-mcp como processo filho). As exceções são duas ferramentas desativadas por padrão: a run_active_vuln_scan envia sondas não destrutivas para 127.0.0.1, o próprio host, e a run_npm_audit_signatures inicia o comando npm, que consulta o registro npm (o binário MCP em si não contém pilha HTTP / TLS). A avaliação de get_security_report e verify_security_findings não envia nada para a LAN, e o gateway é apenas consultado no cache ARP e em outras informações locais. Um ping só pode sair em outros processos que resolvem novamente o MAC do gateway (o monitoramento ARP do daemon, a restauração de emergência, o pareamento do Sensor, o get_guard_status e semelhantes).

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

Por padrão, o RoamSwitch para Linux faz um único tipo de solicitação de saída: o GET de um manifesto assinado pelo atualizador diário (§11). Tudo o resto — cada avaliação, detecção e auditoria — conclui-se localmente. As comunicações que só ocorrem quando um operador ou usuário as ativa ou aciona explicitamente estão listadas no 9.4. O RoamSwitch para Linux é freeware proprietário e o seu código-fonte não é publicado, pelo que as inspeções abaixo foram realizadas pela lafine; os resultados estão no 9.2 e os passos do apêndice C são reprodutíveis apenas com o pacote distribuído.

9.1 Método de auditoria

  1. Isolamento de crates — só é permitida uma pilha cliente HTTP/TLS em um único crate, roamswitch-updater, que compila o binário autônomo do atualizador temporizado. scripts/audit_no_network.sh (executado em CI, bloqueante) faz falhar a compilação se roamswitch-core, roamswitch-daemon, roamswitch-app, roamswitch-mcp, roamswitchkit (ou os crates de CLI e Server) incorporarem uma pilha cliente HTTP/TLS como reqwest / ureq / rustls / native-tls / openssl-sys, ou se algum arquivo .rs fora de roamswitch-updater e dos arquivos citados no 9.4 usar TcpStream / TcpListener / UdpSocket / to_socket_addrs / lookup_host. A única autorização adicional é tonic / hyper, permitidos apenas em roamswitch-core para gRPC sobre socket de domínio UNIX até ao sensor eBPF (Tetragon); o mesmo script proíbe combiná-los com TLS.
  2. Inspeção de árvore de dependência — o Cargo.lock completo foi pesquisado para SDKs de telemetria/análise/relatório de falhas (sentry, opentelemetry,…).
  3. roamswitch-updater review — seu alvo de solicitação único, a ausência de string de consulta e de cookies, e um User-Agent fixo como único cabeçalho que nomeia o software e sua verificação Ed25519 de tudo o que baixa.
  4. Subprocess enumeration — cada site de chamada Command::new(...) foi revisado.

9.2 Resultados

Resultados da auditoria
Item de auditoriaResultado
Pilha HTTP/TLSPresente apenas em roamswitch-updater (ureq + rustls). Não em core / daemon / app / mcp / kit
SDK de telemetria/análise/relatório de falhasNão presente em nenhum lugar
TcpStream / TcpListener / UdpSocketApenas em roamswitch-updater e nos quatro arquivos dos recursos opcionais listados no 9.4
APIs de soquete em usoAF_UNIX (IPC local); AF_NETLINK (canal de veredicto NFQUEUE, controle de nftables e ip neigh — sem emissão de pacotes); AF_INET somente em roamswitch-updater e nos recursos opcionais do 9.4 (TLS/HTTP são delegados aos subprocessos do sistema openssl/curl); AF_PACKET somente quando a reverificação ARP (§4) envia um único ARP who-has ao gateway (dentro da LAN). O túnel VPN (§4), quando ativado, é wg-quick (backend WireGuard) ou tailscaled (backend Tailscale) — um subprocesso, não o daemon — enviando tráfego ao endpoint / tailnet do próprio usuário
Atualizador diárioDuas requisições a cada ~24 h: GET https://lafine.net/updates/v1/manifest e sua assinatura manifest.sig (os arquivos de dados só são obtidos adicionalmente quando a versão deles é mais recente). Sem cadeia de consulta, sem cookies, User-Agent: RoamSwitch-Updater fixo (o único cabeçalho que nomeia o software), sem identificador por dispositivo. Todas as cargas úteis verificadas com 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çaVerificação de assinatura Ed25519 offline. Nenhuma comunicação com um servidor de ativação
Conclusão

Por padrão, o RoamSwitch para Linux inicia exatamente um tipo de comunicação de saída: a verificação diária, somente de recebimento, de um manifesto assinado, mais o download de dados de detecção assinados quando existe uma versão mais recente; nenhuma das duas carrega informação sobre o usuário ou a máquina. A menos que um operador ou usuário a ative ou acione explicitamente, nenhuma outra parte do produto entra em contato com um servidor externo (9.4). O ping enviado à LAN para a fixação da tabela de vizinhos e o monitoramento ARP (#2 em 9.3) vai apenas para endereços de loopback, privados, link-local e ULA, e nunca é enviado a um endereço público.

9.3 Lista completa de operações externas

Todas as operações do funcionamento padrão que podem fazer um pacote sair da máquina local, direta ou indiretamente (a tabela a seguir; as operações que só ocorrem quando um operador ou usuário as ativa ou aciona estão no 9.4):

#OperaçãoDestinoGatilhoO que é enviado
1Atualizador diário: GET /v1/manifest e sua assinatura; os arquivos de dados só quando a versão é mais recente (os mapas de CVE do kernel, dos pacotes do SO e das linguagens são verificados cerca de uma vez a cada 30 dias; um dispositivo Ubuntu sem o mapa de CVE do Ubuntu obtém-no na execução diária seguinte)lafine.net/updates/v1 (CDN)Temporizador do systemd: primeira execução 15 minutos após a inicialização e, depois, cerca de a cada 24 h, com um atraso aleatório de até 3 h. Desativado quando updates.enabled = false ou quando o roamswitch-update.timer está desabilitadoum GET HTTPS simples: sem string de consulta, sem cookies, User-Agent: RoamSwitch-Updater fixo, nada sobre o usuário ou a máquina. Recebe um manifesto + feed assinado
1afreshclam (atualizador próprio do ClamAV), invocado pelo atualizador diário quando o ClamAV é instaladoCDN do projeto ClamAVcomo parte do #1; também quando o usuário pressiona o botão “Atualizar assinaturas de vírus agora (freshclam)” na GUItráfego do próprio ClamAV (um projeto separado); RoamSwitch apenas o inicia
2ping -c 1 -w 1 -W 1 <IP do gateway> (o mesmo tipo de ping único para o roteador IPv6 e para os servidores DNS da LAN)o gateway padrão da LAN; na fixação de ARP/NDP, também o roteador padrão IPv6 e, entre os servidores DNS listados em resolv.conf (/run/systemd/resolve/resolv.conf no systemd-resolved), apenas os que têm endereço de loopback, privado, link-local ou ULA. Nenhum pacote é enviado a um resolvedor público (por exemplo, o Quad9 do DNS de proteção contra ameaças)para o gateway, somente quando o MAC dele é necessário (monitoramento ARP e semelhantes) e não está em nenhum de: cache ARP, tabela de vizinhos ou cópia em memória de 15 segundos (no máximo uma vez a cada 8 s por destino). Para o roteador IPv6 e os servidores DNS, quando o bloqueio ARP do gateway (gateway_arp_lock_enabled, ativado por padrão) refaz a fixação na tabela de vizinhos (ao mudar para uma rede não confiável e quando a configuração é alternada)uma única requisição de eco ICMP (com os dados padrão do ping). Sem resolução de DNS
3iniciando o navegador do sistema (xdg-open)https://roamswitch.com/linux (site do produto), https://lafine.net/ (site do operador, termos de uso, contato) e https://github.com/lafine1211/roamswitch-support (notas de versão e suporte)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
4resolvectl dns …- (nenhuma comunicação do próprio RoamSwitch)DNS de proteção contra ameaças em uma rede não confiável (ativado por padrão, pode ser desativado). <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 sentinelaapenas altera a configuração do resolvedor do SO; o próprio RoamSwitch não envia nenhuma consulta DNS. Depois disso, a resolução de nomes nessa NIC passa a ir para o DNS de proteção contra ameaças selecionado (Quad9 por padrão)
5atualizações do gerenciador de pacotesos repositórios apt / dnf / zypper / pacman da distroquando o usuário executa apt upgrade etc., ou clica no botão “Atualizar agora” da GUI (via pkexec, executando apt-get / dnf / zypper / pacman)Em sistemas apt, o RoamSwitch apenas lê a saída local de apt-cache policy e inicia o gerenciador de pacotes. Em sistemas dnf, ele executa dnf check-update (por exemplo, ao abrir a aba de atualizações), o que pode consultar os repositórios configurados (zypper: zypper list-updates, pacman: pacman -Qu); em todos os casos, sem relação com a lafine
6Tú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ídao 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ãotrá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)

Todas as demais funções do RoamSwitch — controle do nftables, fixação ARP/NDP da tabela de vizinhos (ip neigh, sem emissão de pacotes), guarda de links (NFQUEUE, somente AF_NETLINK), varredura de portas (análise da saída de ss), monitoramento ARP, avaliação de integridade, monitoramento de entropia, canários, fanotify, monitoramento de USB (captura evdev), quarentena, auditoria de logs, auditoria de URLs e segredos — se completam apenas com leituras de arquivos locais e análise da saída de subprocessos. O túnel VPN do §4 é a “comunicação de saída configurada explicitamente pelo usuário”; seu destino é o próprio servidor VPN do usuário e nenhum dado flui para a infraestrutura da lafine. O destino de cada recurso do 9.4 é indicado no 9.4.

9.4 Operações que só alcançam o exterior quando ativadas ou acionadas pelo usuário

A tabela do 9.3 mostra o que acontece por padrão. A seguir estão os pontos do código que abrem um soquete ou delegam a comunicação a um programa externo (curl / openssl / npm) e que só funcionam se um operador ou usuário os ativar ou acionar explicitamente. O scripts/audit_no_network.sh permite as APIs de soquete apenas em cinco arquivos: event_forward.rs, webhook_guard.rs, active_vuln_scan.rs, web_vuln_scan.rs e sensor_pairing.rs (o webhook_guard.rs apenas resolve o nome do destino do webhook com o resolvedor do sistema, e somente quando a opção forward_webhook_block_private_addresses, desativada por padrão, é ativada, para verificar se não é um endereço interno; ele nunca se conecta. Além disso, o tests/fleet_roundtrip.rs é exclusivo de testes e se conecta apenas a um servidor simulado em 127.0.0.1). A compilação falha se qualquer outro código-fonte usar TcpStream e semelhantes. Esses recursos também não adicionam nenhum crate HTTP/TLS: o TLS fica a cargo do openssl do sistema e o HTTP do curl do sistema, como subprocessos.

  • Encaminhamento de eventos (event_forward.rs) — desativado por padrão (forward_enabled = false), e os únicos destinos são os que o operador configura manualmente; não há destino embutido. Os destinos suportados são syslog (RFC 5424 ou CEF, sobre UDP / TCP / TLS), um arquivo JSON Lines e um webhook assinado com HMAC-SHA256. O syslog TLS passa pelo openssl s_client do sistema e o webhook pelo curl do sistema (segredos como a URL e a assinatura são passados em uma configuração pela entrada padrão, nunca na linha de comando); o daemon não tem nenhum crate TLS/HTTP. Somente estes campos são enviados: nome do host, ID do evento, carimbo de data e hora, origem, gravidade, regra, ação, PID, nome do processo e resumo (nem conteúdo de arquivos nem o log bruto de execuções). Eventos não entregues via TCP / TLS / webhook ficam em uma fila em disco 0600 (limite padrão de 20 MB / 72 horas) e são reenviados.
  • Fleet (camada Business, apenas lado do dispositivo) (roamswitch-core/src/fleet/) — o código do lado do dispositivo vem no binário, mas um dispositivo não registrado não envia nada nem resolve o nome de host do Fleet (o worker apenas lê um arquivo de estado local). O registro só ocorre quando um operador executa roamswitch fleet enroll. O serviço de gerenciamento não está implantado nem lançado, portanto não há atualmente nada a que se conectar. As garantias de projeto presentes no código são: (a) o dispositivo aceita apenas dados de política assinados pela organização, verificados com a chave pública da organização fixada no registro; uma política que falhe na verificação não é aplicada. O projeto prevê que a chave de assinatura da organização fique com o cliente, não com a lafine. (b) Não existe canal de comando remoto: no lado do dispositivo não há endpoint, campo nem caminho de código pelo qual se pudesse ordenar isolar, liberar, coletar ou executar. (c) Uma política não pode enfraquecer o piso local: desativar uma proteção ou atrapalhar a liberação / a restauração de emergência nem sequer pode ser expresso no esquema, e prevalece o valor mais rigoroso. Uma política também só pode mudar o destino do encaminhamento de eventos para hosts permitidos pelo root local (fleet_forwarding_allowed_hosts, vazio por padrão). (d) Um dispositivo envia apenas um resumo mínimo de estado — versão do app, família do SO, estado de isolamento (normal / isolado), versão da política aplicada, pontuação de saúde (0–100) e época da chave — além do ID do dispositivo e do ID da organização nos cabeçalhos de assinatura. Usa o curl do sistema (TLS) e somente para um dispositivo registrado.
  • Pareamento com Sensor (sensor_pairing.rs) — desativado por padrão (sensor_pairing_enabled). Conecta-se apenas, e diretamente, ao endereço do Sensor informado pelo operador; não há destino embutido. Suporta TLS 1.3 (via o openssl do sistema, com fixação da impressão digital do certificado), mas um pareamento legado sem fixação recua para texto simples com um aviso. O Sensor é um produto separado e não é coberto pelas afirmações de Zero Telemetry deste documento.
  • Verificação ativa de vulnerabilidades (active_vuln_scan.rs / web_vuln_scan.rs) — desativada por padrão e exige ativação nas configurações. Nesta edição para estações de trabalho, o caminho se limita a sondagens a 127.0.0.1 das portas em escuta já observadas nesta máquina (fixado pelos testes próprios dos módulos).
  • O botão “Verificar atualizações” da GUI. Somente quando o usuário pressiona o botão, e apenas em distribuições baseadas em apt, a GUI usa o curl do sistema para fazer, em https://lafine.net/apt/dists/stable/main/<arch>/Packages, um único GET para descobrir a versão mais recente (tempo limite de 4 segundos; URL pública sem segredos, sem identificador nem cadeia de consulta). Em sistemas dnf, zypper e pacman o curl não é usado; o gerenciador de pacotes é consultado (#5 em 9.3).
  • Verificação de assinaturas do npm — roamswitch audit-npm-signatures <DIR> --confirm só funciona se a configuração npm_audit_signatures_enabled (false por padrão) estiver ativada e o usuário o executar com --confirm; então é o próprio npm que contata registry.npmjs.org.

§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.json e 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 identificador por dispositivo ou instalação, e um User-Agent: RoamSwitch-Updater fixo como único cabeçalho que nomeia o software. É 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.
  • Enquanto o DNS de proteção contra ameaças está em uso, as consultas DNS chegam a um resolvedor externo. O DNS de proteção contra ameaças (§4.5; ativado por padrão, apenas em redes não confiáveis) aponta o resolvedor DNS do SO para o Quad9 (9.9.9.9; também é possível escolher o 1.1.1.2 da Cloudflare). Nesse período, as consultas são enviadas a esse resolvedor pelo SO, e não pelo RoamSwitch, e o próprio RoamSwitch não envia nada. Para desativar, defina dns_enabled como false.
  • O histórico de notificações é apagado após 7 dias e tem comprimento limitado — o histórico das notificações enviadas fica armazenado localmente e, a cada gravação, as entradas com mais de 7 dias são excluídas. Um título é cortado em 200 caracteres e um corpo em 2000 (um pop-up de notificação da área de trabalho mostra no máximo 1000 caracteres do corpo). Esse histórico não sai da máquina, a menos que o encaminhamento de eventos (desativado por padrão) esteja ativado.
  • O registro de execuções também é somente local — o registro do registrador de execuções de processos (§5) fica, por padrão, apenas na máquina (/var/lib/roamswitch/exec_log, 0600). Como contém argumentos de linha de comando, ele é apagado automaticamente conforme o período de retenção (14 dias por padrão) e o tamanho (200 MB por padrão).

§11

Edições
EdiçãoParaPreçoLicença
Edição comunitáriaUso pessoal e uso comercial interno no Linux (sem limite de dispositivos)GrátisFreeware proprietário (EULA incluído)
Negócios (planejado)Gestão de frota, distribuição de políticas, apt interno assinado, SLA para organizaçõesPagoLicença comercial. Mesmo binário + portão de nível

Quando o nível Business passar a ser oferecido, a política prevista é conceder gratuitamente os recursos Business em suas próprias máquinas Linux a quem comprar o macOS RoamSwitch Pro Lifetime. Hoje o nível Business não é oferecido e não existe nenhum servidor de gestão.

Distribuição e cadeia de suprimentos

CanalDetalhe
APTOhttps://lafine.net/apt (stable main). Release assinado com uma chave RSA-4096
RPMhttps://lafine.net/rpm (Fedora/openSUSE separado). repomd.xml assinado por GPG
AURroamswitch-bin (em preparação. O AUR não está aceitando novas contas, por isso, por enquanto, a instalação é feita a partir de https://lafine.net/linux/dl/PKGBUILD com makepkg; o tarball de lafine.net/linux/dl é verificado por sha256 e por uma assinatura separada (validpgpkeys))
tarballEm https://lafine.net/linux/dl/, .tar.gz + .sha256 + .asc (assinatura separada pela chave de assinatura do repositório) para x86_64 / aarch64. O repositório do código-fonte é privado, portanto este, e não o GitHub Releases, é o local público de download
  • 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 autônomo executado por um timer do systemd (roamswitch-update.timer: 15 minutos após a inicialização e depois a cada 24 horas, com um atraso aleatório de até 3 horas). Por padrão, é o único componente que acessa uma URL de distribuição externa (exceto os recursos opcionais do 9.4). A cada execução:

  1. GET https://lafine.net/updates/v1/manifest e sua assinatura separada.
  2. 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.
  3. 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). Os restantes dados só são obtidos quando o manifesto indica uma versão mais recente (o mapa CVE da análise ativa e a lista de pacotes npm populares). Os mapas CVE do kernel, dos pacotes do sistema e dos ecossistemas de linguagens só são obtidos quando passaram cerca de 30 dias desde a última atualização. O mapa de CVE do Ubuntu só é obtido numa versão do Ubuntu suportada; um dispositivo que ainda não o tem obtém-no na execução diária seguinte, sem esperar os 30 dias. Tudo é instalado apenas depois de verificar a assinatura.
  4. Quando a versão do manifesto é mais recente, também são obtidas as regras de deteção da camada de aplicação web (§5). Verificam-se o SHA-256 e a assinatura Ed25519 e confirma-se que o ficheiro é legível como regras antes de o instalar de forma atómica. Um ficheiro ilegível nunca é instalado e as regras em vigor não são substituídas. Não se acrescenta nenhum destino novo: a obtenção vai incluída na do manifesto. Na edição Servidor segue cve_kernel_map_updates_enabled: se estiver desativado, não há qualquer contacto de rede.
  5. Se o ClamAV estiver instalado, invoque freshclam.
  6. 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 inválida, freshclam ausente) é registrada em updates.json, no campo lastError, e o processo ainda assim termina com exit 0, de modo que uma execução do temporizador nunca gera uma notificação de falha do systemd. Todo o mecanismo é desativado em updates.json com "enabled": false ou por systemctl disable --now roamswitch-update.timer; o produto então funciona apenas com os dados incluídos no pacote. O feed de ameaças também é distribuído dentro do pacote, de modo que uma instalação nova tem um feed funcional antes mesmo de o atualizador ser executado pela primeira vez, e o apt upgrade o atualiza mesmo com o atualizador desativado.

§12

  • SO: Ubuntu 22.04 / 24.04 e posteriores, Debian 12 e posteriores, Linux Mint / Pop!_OS / elementary / Zorin, família Fedora / RHEL, openSUSE, família Arch (o pacote do AUR está em preparação; instale a partir do PKGBUILD), Raspberry Pi OS 64 bits (Bookworm), Ubuntu para Raspberry Pi
  • Arquitetura: x86_64/aarch64
  • Obrigatório: systemd, nftables, iproute2 (ip neigh, o bloqueio ARP/NDP), conntrack e libnotify (notificações da área de trabalho); tudo, exceto o systemd, é instalado automaticamente pelas dependências do .deb / .rpm
  • Opcional: clamav (varredura de malware), nmcli (detecção de criptografia do Wi-Fi), iw (informações de rede sem fio como alternativa), bluez (guarda de Bluetooth), systemd-resolved (configuração do DNS de proteção contra ameaças), lsof (detecção de portas em escuta como alternativa), nmap (apoio às verificações ativas de vulnerabilidades), wireguard-tools (o backend WireGuard do túnel VPN + kill-switch, §4), tailscale (o respectivo backend Tailscale, §4), polkit / pkexec (executar uma atualização, uma desinstalação ou a reinicialização do daemon a partir 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

CaixaObjetivoRede
serde / serde_jsonserialização de configuração e IPCnenhum
Tóquioruntime assíncrono (features = ["full"]; por padrão, só são usados sockets Unix)nenhum (sem TCP por padrão; o pareamento opcional com Sensor é a exceção)
rastreamento / rastreamento-assinanteregistro (local / diário)nenhum
este erro / de qualquer maneiratratamento de errosnenhum
expressão regularanálise de string (diagnóstico, dimensionamento CSS)nenhum
urlAnálise de URL (heurística audit_url_safety)nenhum (não busca)
cronotratamento de data/horanenhum
ed25519-dalekverificação offline das assinaturas de licença e dos dados assinados, como o manifesto do atualizador e o feed de ameaças (também usada no pareamento opt-in do Sensor)nenhum
base64/sha2tokens de licença, hashes canáriosnenhum
libcchown / kill / chamadas de sistemanenhum
idnadecodificação de hosts IDN/punycode para detecção homográficanenhum
nfq (somente daemon)Ligações NFQUEUE para o link guard - Rust puro, AF_NETLINKnenhum (canal de veredicto, sem saída)
abertochama o xdg-open para abrir links como “Abrir notas de versão”, o EULA e as páginas de suporteo processo em si não se comunica
GTK3 / ksni (somente aplicativo)GUI e bandejanenhum
serde_yamlleitura de /etc/roamswitch/guard.yaml (core)nenhum
tomlanálise de Cargo.lock / poetry.lock (varredura de CVEs em pacotes de linguagens, core)nenhum
md-5hash das impressões digitais JA3 do Link Guard (não usado para autenticação nem integridade, core)nenhum
bs58verificação Base58 de chaves privadas de criptoativos (detecção de segredos, core)nenhum
randgeração da chave do dispositivo no pareamento opt-in do Sensor (core)nenhum
tonic / tonic-prost / prost / tower / hyper-utilgRPC com o sensor Tetragon; somente socket de domínio Unix, sem TLS (core)nenhum (somente socket Unix)
flate2descompactação gzip do mapa de CVEs de pacotes da família RHEL (somente no updater; Rust puro)nenhum (apenas descompactação)
inotifymonitoramento do acesso aos honeytokens (iscos de credenciais; roamswitch-honeytokens)nenhum
ayacarregador do XDP boot gate experimental (roamswitch-xdp-gate, referenciado pelo daemon)nenhum (somente bpf() / netlink)
urlencoding / gdk / gdk-pixbuf / glib / pangodecodificação de URL na GUI e crates periféricos do GTK3 (somente no app)nenhum
ureq + rustls (somente roamswitch-updater)os HTTPS GET do atualizador diário (manifesto, assinatura e quaisquer dados que mudaram)a única pilha HTTP/TLS na árvore; confinado ao binário do atualizador
Ponto-chave

Nenhum de roamswitch-core, roamswitch-daemon, roamswitch-app, roamswitch-mcp ou roamswitchkit contém um cliente HTTP capaz de alcançar a internet, uma pilha TLS ou um SDK de telemetria. No roamswitch-core, o par tonic / hyper serve ao gRPC com o Tetragon apenas por um soquete de domínio Unix, não tem TLS e nunca toca a rede. ureq + rustls existem apenas no roamswitch-updater (o 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 (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 é policy accept sem regras; balanced / lockdown são policy drop (ICMP / ICMPv6 são permitidos apenas em balanced)
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

table inet roamswitch {                 # Durante o Air-Gap, a mesma tabela é recriada: policy drop em input e output, somente lo é permitido (conexões estabelecidas também não passam)
    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 — independente da tabela de perfil
    chain output {
        type filter hook output priority 0; policy accept;
        udp dport 443 reject with icmpx type port-unreachable   # somente quando block_quic está ativado (desativado por padrão)
        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   # somente os primeiros 14 pacotes de cada conexão são enviados à fila de inspeção
    }
}

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_portscan {        # detecção e bloqueio de varredura de portas (ativado por padrão)
    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 {     # lista de bloqueio de tráfego de saída para IPs maliciosos (ativada por padrão)
    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 {     # contramedida para o desvio do firewall por portas publicadas do Docker (somente com o backend nftables nativo do Docker)
    chain forward { type filter hook forward priority -10; policy accept; … iifname { … } drop }
}

table inet roamswitch_isolate {          # bloqueio por processo após uma detecção eBPF (por cgroup / UID)
    chain process_block { type filter hook output priority -100; policy accept; … 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-daem)" -a -i
# → Nenhuma saída = zero conexões de internet TCP/UDP
sudo lsof -p "$(pgrep -x roamswitch-daem)" -a -U | grep roamswitch
# → Apenas /run/roamswitch/roamswitch.sock e events.sock (recebe eventos de detecção eBPF) (o guarda de link adiciona um soquete 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-(daem|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
# → Faz apenas HTTPS GET para lafine.net/updates/v1 (manifesto, assinatura e dados que mudaram) e encerra. Se o ClamAV estiver instalado, o freshclam também se conecta aos espelhos do ClamAV.

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-daem)" 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)

As diversas defesas do RoamSwitch (corte Air-Gap, autorreparo após a remoção forçada do firewall, detecção de ransomware, /tmp noexec, Yama LSM, detecção de homógrafos etc.) podem ser reproduzidas na sua própria máquina com o conjunto de testes Docker publicado no repositório oficial de suporte (roamswitch-support). O conjunto só manipula seu próprio namespace de rede e os arquivos dentro do contêiner, portanto a rede e os arquivos do host não são afetados. No entanto, ele é executado com --privileged; por isso, configurações compartilhadas por todo o kernel, como o ptrace_scope do Yama e o suid_dumpable, também são alteradas no host, e remover o contêiner não as desfaz. Execute-o em um ambiente descartável.

# 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 obtém automaticamente o pacote de distribuição oficial do repositório APT (lafine.net/apt) e executa e avalia as verificações dentro de um namespace de rede e de montagem isolado. O conjunto público consiste em pentest.sh (os sete cenários PENT-1 a 7) e selftest.sh (autotestes destrutivos do daemon para Air-Gap, autorreparo, canários, detecção de rajadas e o Vault de quarentena). Os IDs nas tabelas abaixo são números de série dos registros de verificação e não correspondem um a um às verificações dos scripts. O BadUSB (SP-6), os modos do Link Guard (família SP-7), a fixação de ARP e os kill-switches de VPN (SP-8 a 10), as verificações individuais de reforço do kernel (família SP-11) e PENT-8 e 9 não fazem parte do conjunto público; foram confirmados por outros meios (um host real, namespaces de rede separados, eventos injetados manualmente).

(1) Defesa de Rede Autônoma e Autocura de Air Gap

IDCenárioMétodo de verificaçãoCritério de aprovação
SP-3Ativação de Air-Gap → queda de saídaEnviar enable_air_gap IPCpolicy drop aplicado ao gancho de saída; todo o tráfego de saída bloqueado
SP-3bLimpeza forçada e autocorreção do firewallEliminar regras via nft delete table inet roamswitchAnti-Clobber Self-Healing restaura regras de drop em 5 segundos
SP-3cFechamento com falha na reinicialização do daemonEncerrar o daemon durante o Air-Gap (o selftest.sh usa kill, ou seja, SIGTERM) e reiniciá-loA política de queda de saída persiste perfeitamente durante a reinicialização
SP-3dRecuperação autorizada de Air-GapEnviar disable_air_gap IPC autorizadoPolítica de descarte suspensa, todos os arquivos de marcadores removidos, saída restaurada
SP-8Fixação preventiva de ARP/NDPHabilitar em ambiente de gateway netns isolado → enviar ARP gratuito falsificadoA entrada vizinha do gateway permanece PERMANENT; ARP falsificado ignorado
SP-9Interruptor de interrupção VPN (WireGuard)Servidor Live WireGuard em redes separadas, importe .conf → vpn_upO handshake é concluído por meio de inet roamswitch_vpn; zero vazamentos na morte do túnel
SP-10Kill-switch VPN (Tailscale)Stub tailscale CLI + redes separadas, tailscale0 SETCP/80 de texto simples fora do túnel e DNS local bloqueados; DERP e MagicDNS permitidos

(2) Endurecimento de Kernel e Montagem

IDCenárioMétodo de verificaçãoCritério de aprovação
SP-11aProteção sysctl de redeExecute apply_kernel_sysctl_hardening IPCrp_filter=1, tcp_syncookies=1, accept_redirects=0 atingem valores alvo
SP-11bProteção de memória e Yama LSMVerifique ptrace_scope e suid_dumpablekernel.yama.ptrace_scope=1 (bloqueia espionagem), fs.suid_dumpable=0 (sem core dump)
SP-11c/tmp e /dev/shm defesa noexecExecute apply_mount_hardening IPC/tmp e /dev/shm remontados noexec,nosuid,nodev; execução negada
SP-11dExecução legítima do usuário preservadaExecute 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

IDCenárioMétodo de verificaçãoCritério de aprovação
SP-1Detecção de violação canárioTruncar ou renomear arquivo canário chamariz (.xlsx) em ~/Documents/Violação detectada em segundos, acionando Air-Gap imediato
SP-2Detecção de explosão de criptografia em massa de ransomwareUm único processo gera rapidamente vários arquivos de alta entropiaProcesso de trabalho ofensivo congelado com SIGSTOP; Air-Gap acionado
SP-2bNenhum falso positivo em shredExecute shred -n 3 -u em arquivos de testeA correspondência da lista de permissões evita detecção ou congelamento de falsos positivos
SP-2cProcessos críticos nunca congeladosValidar a lógica do alvo congelado durante a detecção de burstDaemons críticos (dockerd, systemd, containerd) nunca são congelados
SP-4A string de teste EICAR é somente para notificaçãoGrave 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-4bAssinatura de malware real, confirme primeiroEscreva uma assinatura real fora das pastas monitoradasAumenta o diálogo de aprovação modal; colocado em quarentena somente após a confirmação do usuário
SP-4cProteção de permissão do Quarantine VaultInspecione as permissões do diretório do vault e dos arquivos de amostraDiretório do Vault definido como 0700, amostras em quarentena definidas como 0400
SP-4dRestauração de arquivo original seguro IPC privilegiadoEnviar restore_quarantine_file IPCArquivo 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

IDCenárioMétodo de verificaçãoCritério de aprovação
SP-7mode = block aplicação rigorosaConecte-se a hosts listados em feeds ou blocklistExtraCaiu na camada TLS (SNI analisado); hosts seguros não listados alcançam HTTP 200
SP-7bmode = warn retenção com falha fechadaConecte-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-7cmode = off desmontagem completaDesativar proteção de linkA tabela inet roamswitch_linkguard foi completamente desinstalada

(5) Mitigação BadUSB e controle de dispositivos

IDCenárioMétodo de verificaçãoCritério de aprovação
SP-6Proteção de teclado conectadoVerifique os teclados conectados na inicialização do daemonDispositivos existentes adquiridos; apenas os hot-plugs subsequentes foram capturados

(6) Itens de Autoverificação que simulam ataques (PENT-1 a 7 no conjunto Docker público, PENT-8 e 9 em verificações manuais separadas)

IDVetor de ataqueDetalhe da SimulaçãoCritérios de Defesa/Detecção
PENT-1Exfiltração C2Tentativa de saída HTTP/TCP para C2 sob Air-GapTodos os pacotes descartados no netfilter; vazamento zero
PENT-1bExclusão de Força FWEliminar regras com nft delete table inet roamswitchAnti-Clobber Self-Healing restaura regras em segundos
PENT-1cRestauração segura de air-gapProblema autenticado disable_air_gap IPCRegras de descarte removidas; marcadores excluídos; conectividade restaurada
PENT-2/tmp Porta dos fundosExecute o script backdoor descartado em /tmpNegado pela proteção de montagem noexec (Permission denied)
PENT-2b/dev/shm Memória CompartilhadaExecute a carga binária colocada em /dev/shmBloqueado por noexec defesa de montagem
PENT-2cPreservação do espaço do usuárioExecute o script no diretório de usuário legítimo (/home/tester/)Execução desimpedida; interrupção zero do fluxo de trabalho
PENT-3espionagem de memória ptracePTRACE_ATTACH para direcionar para extrair segredos/tokensNegado por Yama LSM kernel.yama.ptrace_scope=1
PENT-3bRaspagem secreta do Core DumpAcionar falha no processo para obter dump principal de memóriaImpedido por fs.suid_dumpable=0
PENT-4Adulteração de CanárioArquivo isca canário zerado em ~/Documents/Detecção em tempo real de anomalia de tamanho e alteração de hash
PENT-4bLimpeza em massa CanaryExclusão em massa de arquivos canary decoyDesaparecimento de arquivo detectado imediatamente, gerando incidente
PENT-5Falsificação de endereço IPTransmitir pacotes com IP de origem falsificadoEliminado por rp_filter=1 (filtro de caminho reverso)
PENT-5bSYN Flood DoSInundar conexões TCP semiabertasTabela de estados protegida por tcp_syncookies=1
PENT-5cSequestro de rota ICMPInjetar pacotes falsos de redirecionamento ICMPIgnorado devido a accept_redirects=0
PENT-6Phishing homógrafo cirílicoNavegue até gооgle.com (homógrafo cirílico)Identificado pelo LinkGuard, pontuação 40 (PERIGOSO/bloqueio)
PENT-6bIP bruto e tráfego de texto simplesConexão de saída direta para IP brutoSinalizado com precisão como fator de risco de segurança
PENT-7Vazamento de chave de API OpenAIExpor o token sk-proj-... no código ou nos logsDetectado através da análise de entropia de Shannon e mascarado
PENT-7bVazamento de token do GitHubExpor o token pessoal ghp_... no códigoCorrespondido por padrão de token e verificação de entropia
PENT-8Preempção de Kernel LPE (Frag Gap)unshare -U -r sem privilégios (tentativa CVE-2026-53362)Na medição da v1.1.0, rejeitado de imediato por user.max_user_namespaces = 0 com Operation not permitted. A edição Client atual não faz essa recusa por padrão (em guard.yaml, auto_enable_userns_restriction fica desativada por padrão; a edição Server a aplica sempre)
PENT-9Sensor de ameaças eBPF e congelamento de processosAlerta crítico/emergência injetado via soquete Falco eBPFProcesso congelado via SIGSTOP em milissegundos (estado: Ts); Air Gap implantado

Sobre os tipos de verificação: na tabela acima, PENT-2/2b (noexec), PENT-3/3b (Yama, core dumps) e PENT-5/5b/5c (rp_filter, SYN cookies, redirecionamentos ICMP) confirmam que um recurso padrão do kernel ou do mount configurado pelo RoamSwitch está em vigor. O PENT-8 (negar namespaces de usuário) é do mesmo tipo, mas é um registro da v1.1.0 e a edição Client atual não o aplica por padrão. Os itens que atacam a lógica própria de detecção e contenção do RoamSwitch são PENT-1/1b/1c (Air-Gap e autorreparo), PENT-4/4b (canários), PENT-6/6b (Link Guard), PENT-7/7b (detecção de segredos) e PENT-9 (congelamento e contenção após uma detecção eBPF). O PENT-2c confirma que o trabalho normal não é afetado.

(7) Resumo da Autoverificação (sem Falco [autônomo] e com Falco [integração eBPF])

Resumo dos resultados da Autoverificação completa que simula ataques, executada em um ambiente Docker isolado (Alvo: Ubuntu 24.04 com RoamSwitch Client Edition; Atacante: Nmap / curl / hping3). A tabela abaixo é uma medição do Client v1.1.0 feita em 2026-09-06. Em 2026-09-25, uma versão corrigida compilada a partir do código-fonte da série 1.10 (não é uma versão pública) passou em 11 de 11 itens do conjunto do cliente (consulte audit/RESULTS-PENTEST-2026-09-25.ja.md no repositório público).

Categoria de verificaçãoSem 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 (medição da v1.1.0; unshare bloqueado onde user.max_user_namespaces = 0 está definido. Desativado por padrão na Client Edition atual)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 CPUZero 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çãoImpactoMitigação
Em alguns kernels, FAN_MARK_MOUNT em tmpfs (/tmp) não entrega eventosgravaçõ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 / tmpfsa detecção de explosão de entropia no contêiner não pode ser validadavalidado 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 DockerAs operações de regras RoamSwitch podem interferir nas regras NAT do Dockerisolado em tabelas dedicadas (inet roamswitch e as demais tabelas roamswitch_*), mas operações globais, 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 issouma 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 hosta 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, ativada por padrão) cobre o caminho do resolvedor
Quando enabled é false em updates.json, a atualidade do feed do atualizador diário depende da cadência de lançamentoscom as atualizações desativadas, o feed é tão atual quanto o último apt upgradeas 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
Este documento reflete a implementação conforme está no RoamSwitch for Linux v1.11.1 (endurecimento do kernel contra LPE, proteção em tempo de execução eBPF, congelamento SIGSTOP de processos atacantes, otimização de baixo overhead do Falco, quarentena Vault de monitoramento em tempo real e restauração IPC privilegiada ao original, autorrecuperação de Air-Gap, detecção de tunelização DNS, captura forense de provas, honeytokens, acompanhamento do pass_age na verificação de vulnerabilidades, mapeamento NIST CSF/CIS Controls, reavaliação do diagnóstico, auditoria da configuração do MCP). Consulte o CHANGELOG do roamswitch-support para o histórico de alterações linha a linha. Para as informações mais recentes, consulte roamswitch.com/linux.