Monitor de sockets de rede em tempo real no terminal (TUI), construído em Rust sobre o comando ss do iproute2.
Pense num htop para conexões de rede: lista TCP/UDP, estados, filas, e qual processo é dono de cada socket — atualizando ao vivo.
┌ shell_mon · proto:all ordem:estado ──────────────────────────────────────┐
│ total 37 tcp 25 udp 12 estab 17 listen 8 time-wait 1 │
└─────────────────────────────────────────────────────────────────────────────┘
┌ sockets (37) ───────────────────────────────────────────────────────────────┐
│ PROTO ESTADO RECV-Q SEND-Q LOCAL REMOTO PROCESSO │
│ tcp ESTAB 0 0 192.168.0.10:22 192.168.0.5:51234 sshd │
│ tcp LISTEN 0 0 127.0.0.1:11434 0.0.0.0:* ollama │
│ udp UNCONN 0 0 0.0.0.0:48958 0.0.0.0:* │
└─────────────────────────────────────────────────────────────────────────────┘
q sair · p pausa · / filtro · t proto · s ordem · r refresh · ↑↓ navega
- 🔄 Auto-refresh em tempo real (padrão 200ms), pausável a qualquer momento
- 🔍 Filtro ao vivo por endereço, processo, estado ou PID
- 🎨 Cores por estado (ESTAB verde, LISTEN azul, TIME-WAIT magenta) e destaque de conexões novas por ~1,5s
- 🏷️ Nomes dos IPs (DNS reverso): mostra
→ 140.82.113.25 githubem vez de só o número, resolvido em background sem travar a TUI, com cache em disco (~/.cache/shellmon/rdns.tsv, TTL de 14 dias) para não re-resolver a cada abertura (--no-rdnsdesativa) - 🛡️ Visão defensiva: zonas de confiança (loopback / rede local / internet), contadores de serviços expostos e entradas da LAN, e ⚠ destaque de conexões abertas por descendentes de navegador
- 🔬 Inspeção de processo (tecla
i): abre um painel para o socket selecionado com a linha de comando, a árvore de processos, e taxas ao vivo de I/O de disco (leitura/escrita), CPU e memória — útil para ver o que um processo suspeito está fazendo e quanto escreve em disco - 🩺 Triagem (
--triage): relatório humanizado do estado atual (o que está exposto, quem entra da LAN, o que fala com a internet) - 📝 Log de eventos: registra em disco quando listeners/entradas da LAN aparecem e somem, para revisar depois
- 🔔 Notificações (
notify-send): alerta no desktop em eventos de alta severidade (novo serviço exposto, entrada da LAN, DNS suspeito) - 🧭 Auditoria de rede (
--triage): checa gateway/rotas/DNS e sinaliza DNS não-padrão empurrado pelo roteador (sequestro de DNS), lista os dispositivos da LAN e audita o firewall (avisa se a entrada não estiver em DROP) - 🔀 Ordenação alternável (estado, local, remoto, processo, filas) e filtro de protocolo (rede / tcp / udp / unix)
- 🧦 UNIX domain sockets (IPC local): visíveis pela tecla
t(ficam fora da visão padrão de rede para não inundar), com toda a lógica defensiva aplicada só aos sockets de rede - 👮 Detecção de root — avisa quando, sem
sudo, os processos de sockets de outros usuários ficam ocultos - 📜 Modo lista (
--list) para uso scriptável / one-shot
- Linux com o comando
ssdisponível (pacoteiproute2, presente na maioria das distros) - Rust / Cargo (edição 2021) para compilar
yay -S shellmon # ou: paru -S shellmonO empacotamento fica em
packaging/aur/.
git clone https://github.com/ecodelearn/shell_mon.git
cd shell_mon
cargo build --release
# binário em ./target/release/shellmonOpcional — instalar no PATH do usuário:
cargo install --path .
# ou
cp target/release/shellmon ~/.local/bin/# TUI interativa (recomendado rodar com sudo para ver os processos)
sudo shellmon
# Intervalo de refresh de 1 segundo
shellmon --interval 1
# Lista única, scriptável
shellmon --list
# Ajuda
shellmon --helpPor que
sudo? Oss -psó revela o processo dono de um socket pertencente a outro usuário quando executado como root. Sem privilégios você ainda vê todos os sockets, mas a coluna PROCESSO/PID fica vazia para os que não são seus.
| Tecla | Ação |
|---|---|
q / Esc / Ctrl-C |
sair |
p |
pausar / retomar auto-refresh |
r |
refresh manual |
/ |
filtrar (endereço, processo, estado, PID) |
t |
alternar protocolo (rede → tcp → udp → unix) |
a |
voltar para rede (tcp+udp) |
s |
alternar ordenação |
i / Enter |
abrir/fechar inspeção do processo (cmdline, I/O, CPU, mem) |
↑/↓ ou k/j |
navegar |
PgUp / PgDn / Home |
navegação rápida |
Como o shellmon atualiza sozinho (5x/s por padrão), dá para deixá-lo rodando
num canto da tela como um painel passivo — você trabalha em outra coisa e só dá
uma olhada quando quiser. O script scripts/shellmon-panel.sh abre a TUI numa
janela Alacritty dedicada, com a classe/app-id
shellmon (use isso na regra de janela do seu compositor).
Instalação dos utilitários no PATH do usuário:
cargo build --release
install -m 755 target/release/shellmon ~/.local/bin/shellmon
install -m 755 scripts/shellmon-panel.sh ~/.local/bin/shellmon-panel
install -m 644 scripts/shellmon.desktop ~/.local/share/applications/shellmon.desktopAbrir o painel:
shellmon-panel # janela Alacritty fixa rodando a TUI
SHELLMON_COLS=140 shellmon-panel --interval 0.5 # customizando- Configurações do Sistema → Gerenciamento de Janelas → Regras de Janela → Adicionar Nova
- Em Correspondência de janela, Classe de janela (aplicativo) →
Exatamente igual a→shellmon - Adicione as propriedades (cada uma como Forçar / Aplicar inicialmente):
- Flutuante (não no mosaico): Sim
- Manter acima das outras: Sim
- Em todas as áreas de trabalho: Sim
- Pular barra de tarefas e Pular alternador: Sim
- Posição e Tamanho: o canto/dimensão que preferir
- Aplique. Toda janela aberta via
shellmon-paneljá nasce fixa ali.
cp scripts/shellmon.desktop ~/.config/autostart/ # autostart do PlasmaEm outros compositores Wayland (Hyprland, Sway, river…) a ideia é a mesma: uma window rule casando o app-id
shellmonparafloat+pin/above.
Sem privilégios, a coluna PROCESSO/PID fica vazia para sockets de outros usuários. Para um painel que mostra tudo — inclusive iniciando junto com a sessão, sem pedir senha — use o instalador privilegiado:
cargo build --release
sudo scripts/install-elevation.shEle faz isso com segurança:
- copia o binário para
/usr/local/bin/shellmon(root:root 755), um local que seu usuário não pode sobrescrever; - cria
/etc/sudoers.d/shellmoncom uma regraNOPASSWDrestrita exatamente a esse caminho (validada comvisudo -c).
Por que
/usr/local/bine não~/.local/bin? UmNOPASSWDapontando para um binário que você mesmo pode editar equivaleria a root irrestrito (bastaria trocá-lo por um shell). Em local root-only, osudosó consegue rodar aquele programa, somente-leitura.
Depois, abra o painel elevado:
shellmon-panel --root
# ou instale a entrada de autostart com root:
cp scripts/shellmon-panel-root.desktop ~/.config/autostart/Para remover a elevação:
sudo rm -f /etc/sudoers.d/shellmon /usr/local/bin/shellmonO shell_mon ajuda a observar e entender atividade de rede suspeita. Ele
classifica cada par remoto em zonas de confiança e destaca o que costuma
indicar problema:
- Zonas:
loopback(cinza, só a máquina) ·rede local(ciano) ·internet(amarelo). A coluna REMOTO é colorida por zona. - Serviços expostos: contador no topo de quantos serviços estão escutando
acessíveis pela rede (não-loopback) — uma porta dos fundos apareceria aqui.
Inclui UDP ligado a
0.0.0.0/::(que fica em estadoUNCONN, nãoLISTEN); para esses há um debounce (~1,5s) que ignora sockets UDP efêmeros (consultas DNS/QUIC) e só conta serviços que persistem. - Entradas da LAN (
lan-in): conexões estabelecidas entrando de um peer da rede local para um serviço seu — fica vermelho quando há alguma. - ⚠ via navegador: conexões abertas por um processo descendente de um navegador (Firefox/Chrome/Chromium/Brave) são marcadas, para flagrar ataques que escalam a partir da navegação.
No modo TUI, o shell_mon registra em disco os eventos de alto sinal — sem
encher de ruído — para você revisar depois "o que aconteceu enquanto eu não
estava olhando". Fica ligado por padrão (--no-log desativa):
~/.local/share/shellmon/events.log # ou $SHELLMON_LOG / $XDG_DATA_HOME
São registrados: novos serviços escutando (LISTEN_NEW, HIGH se exposto),
listeners que somem (LISTEN_GONE) e conexões entrando da LAN (LAN_INBOUND,
HIGH) — cada um com data/hora local, processo/PID e ⚠ se vier de navegador:
2026-06-19 16:55:43 [INFO] SESSION_START monitorando — base: 8 listeners (2 expostos), 0 entradas da LAN
2026-06-19 16:55:45 [HIGH] LISTEN_NEW tcp 0.0.0.0:4444 (python3, pid 78743) [todas as interfaces]
2026-06-19 16:55:47 [INFO] LISTEN_GONE tcp 0.0.0.0:4444 (python3, pid 78743) [todas as interfaces]
Acompanhe ao vivo com tail -f ~/.local/share/shellmon/events.log.
Eventos de alta severidade (novo serviço exposto, entrada da LAN, DNS
suspeito) também disparam uma notificação via notify-send — para você ser
avisado na hora, mesmo com o painel fora de vista. Ligado por padrão se
notify-send existir; --no-notify desativa. Há deduplicação para não repetir
o mesmo alerta em sequência.
As notificações precisam de um barramento D-Bus de sessão
(DBUS_SESSION_BUS_ADDRESS). Sem ele — por exemplo, rodando via sudo, que
limpa o ambiente — elas são silenciosamente desativadas (em vez de cuspir o erro
dbus-launch --autolaunch). Para que o painel root (shellmon-panel --root) consiga notificar, o launcher repassa o barramento da sua sessão e o
install-elevation.sh adiciona SETENV à regra sudoers; reexecute-o após
atualizar.
Um roteador comprometido entrega configuração via DHCP (DNS, gateway, rotas) —
um invasor pode redirecionar todo o seu tráfego sem invadir a máquina,
apenas empurrando um servidor DNS malicioso. O shell_mon audita isso:
- classifica cada servidor DNS em uso como conhecido (Quad9, Cloudflare, Google…), local (o roteador faz DNS) ou ⚠ não reconhecido;
- um DNS público não reconhecido vira aviso
DNS_SUSPECT(log + notificação) já na inicialização; - a
--triagemostra gateway, rotas, DNS classificados e os dispositivos da LAN.
A --triage também audita o firewall de host (firewalld/ufw): mostra a zona
padrão e se a política de entrada é DROP (bloqueada) ou ACCEPT (exposta). Se
o firewall estiver inativo ou aceitando entrada, vira aviso FIREWALL no log e
notificação. Exemplo de saída saudável:
🧱 FIREWALL
backend: firewalld (ativo)
zona padrão: public
entrada: DROP → bloqueada por padrão ✅
Se um DNS suspeito for sinalizado, fixe o seu e impeça o roteador de empurrar outro:
sudo nmcli connection modify "Wired connection 1" ipv4.ignore-auto-dns yes \
ipv4.dns "9.9.9.9 149.112.112.112"
sudo nmcli connection up "Wired connection 1"Relatório único e humanizado do estado atual — ótimo para uma checagem rápida:
sudo shellmon --triage # com root: atribui processo/PID a tudo
shellmon --triage # sem root: funciona, mas oculta donos de serviçosMostra, em linguagem clara: serviços escutando expostos à rede, conexões entrando da LAN, conexões ativas com a internet (marcando as que vêm do navegador) e um resumo no rodapé.
Limite importante: um monitor rodando na própria máquina potencialmente comprometida tem alcance limitado — um invasor com root pode ocultar rastros. Use o
shell_monpara pegar o óbvio e ganhar visibilidade, mas combine com firewall de host, inspeção do roteador e observação a partir de outro dispositivo.
| Flag | Efeito |
|---|---|
-l, --list |
imprime a lista de sockets uma vez e sai (scriptável) |
--triage |
relatório defensivo (sockets + DNS/rede + firewall) e sai |
-i, --interval <SEGS> |
intervalo de refresh da TUI (padrão: 0.2) |
--no-log |
não registrar eventos em disco |
--no-notify |
não enviar notificações de desktop |
--no-rdns |
não resolver nomes (DNS reverso) dos IPs |
-h, --help |
ajuda |
| Variável | Usada por | Efeito |
|---|---|---|
SHELLMON_LOG |
binário | caminho do log de eventos (padrão: $XDG_DATA_HOME/shellmon/events.log ou ~/.local/share/...) |
SHELLMON_RDNS_CACHE |
binário | caminho do cache de DNS reverso (padrão: $XDG_CACHE_HOME/shellmon/rdns.tsv ou ~/.cache/...) |
DBUS_SESSION_BUS_ADDRESS |
binário | se ausente, as notificações são desativadas (evita o erro do dbus-launch) |
SHELLMON_BIN |
shellmon-panel |
binário a executar (padrão: shellmon; no modo --root: /usr/local/bin/shellmon) |
SHELLMON_COLS / SHELLMON_ROWS |
shellmon-panel |
tamanho da janela Alacritty (padrão: 118 × 30) |
SHELLMON_SRC |
install-elevation.sh |
binário a instalar em /usr/local/bin (padrão: target/release/shellmon) |
As teclas da TUI estão na tabela em Uso; o painel "sempre na tela", a elevação e o log de eventos têm seções próprias acima. O histórico de versões fica em CHANGELOG.md.
O shell_mon executa ss -tuanpH (TCP + UDP, todos os estados, numérico, com
processo, sem cabeçalho), parseia a saída de forma robusta — lidando com IPv6
([::1]:631), interfaces (192.168.0.1%enp2s0) e o formato
users:(("nome",pid=123,fd=5)) — e renderiza tudo com ratatui.
src/
├── socket.rs coleta e parsing do `ss` + resumo agregado (com contadores de ameaça)
├── analysis.rs zonas de confiança (IP) e linhagem de processos (/proc)
├── procinfo.rs cmdline, I/O de disco, CPU e memória por processo (/proc)
├── app.rs estado: filtros, ordenação, scroll, diffs, cache de navegador
├── events.rs log de eventos defensivos em disco (listeners, entradas da LAN)
├── notify.rs notificações de desktop (notify-send) com deduplicação
├── netcfg.rs auditoria de rede: gateway/rotas/DNS/vizinhos + DNS suspeito
├── rdns.rs DNS reverso (PTR) em background → nomes humanos pros IPs
├── triage.rs relatório defensivo `--triage`
├── ui.rs renderização da TUI (ratatui)
└── main.rs terminal, loop de eventos, args, detecção de root
cargo test # testes do parser
cargo run # roda em modo debugMIT