DEV Community

Evandro Carvalho
Evandro Carvalho

Posted on Originally published at landingfymax.com.br

Como Montar um SIEM Doméstico no Linux com Detecção de Intrusos ARP e Fabricante OUI

Manter a visibilidade sobre o que está conectado na sua rede local (LAN) é o primeiro passo para qualquer postura séria de segurança. Na maioria das residências e pequenos escritórios, confiamos cegamente no roteador da operadora — que raramente oferece logs em tempo real, auditoria ou alertas de novos dispositivos desconhecidos.

Neste artigo prático, vamos construir um Sentinela Doméstico (Micro-SIEM) em Python que roda em qualquer Linux (Raspberry Pi, Mini PC ou servidor local), capaz de:

  1. Ler a tabela ARP do kernel em tempo real;
  2. Realizar varreduras ativas assíncronas via Ping Sweep em sub-redes /24 em menos de 2 segundos;
  3. Resolver o fabricante do hardware via prefixo IEEE OUI (Apple, Samsung, Espressif, Intel, etc.) sem depender de APIs externas;
  4. Disparar alertas de novos nós conectados para quarentena imediata.

1. Por que a Tabela ARP e não apenas Nmap?

Ferramentas como Nmap são excelentes para auditorias pontuais, mas executar scans pesados continuamente na rede pode:

  • Gerar ruído e tráfego desnecessário de broadcast;
  • Travar dispositivos IoT sensíveis (lâmpadas, tomadas Tuya/Sonoff);
  • Consumir CPU desnecessária no host de monitoramento.

No Linux, o próprio kernel já mantém um cache dinâmico de todos os dispositivos que trocaram pacotes recentemente no arquivo pseudo-sistema /proc/net/arp. Lendo esse arquivo diretamente, a verificação consome praticamente 0% de CPU.


2. O Script Sentinela em Python

Abaixo está a implementação base que combina a leitura da tabela ARP com a resolução de fabricantes offline:

import os
import re
import asyncio
import subprocess

# Mapeamento simplificado de prefixos OUI populares (3 primeiros octetos)
OUI_DATABASE = {
    "00:1A:2B": "Ayecom Technology",
    "DC:A6:32": "Raspberry Pi Trading",
    "B8:27:EB": "Raspberry Pi Foundation",
    "AC:D1:B8": "Apple, Inc.",
    "F4:F5:DB": "Apple, Inc.",
    "24:4C:E3": "Samsung Electronics",
    "EC:FA:BC": "Espressif (Tuya / Sonoff / IoT)",
    "30:AE:A4": "Espressif Systems",
    "00:E0:4C": "Realtek Semiconductor",
    "18:66:DA": "Intel Corporate",
}

def resolve_vendor(mac: str) -> str:
    prefix = mac.upper()[:8]
    return OUI_DATABASE.get(prefix, "Fabricante Desconhecido")

def read_kernel_arp_table():
    devices = []
    if not os.path.exists("/proc/net/arp"):
        return devices

    with open("/proc/net/arp", "r") as f:
        lines = f.readlines()[1:] # Pula o cabeçalho
        for line in lines:
            parts = line.split()
            if len(parts) >= 6:
                ip, hw_type, flags, mac, mask, device = parts[:6]
                # Flags 0x2 significa dispositivo resolvido/válido
                if flags == "0x2" and mac != "00:00:00:00:00:00":
                    devices.append({
                        "ip": ip,
                        "mac": mac.upper(),
                        "interface": device,
                        "vendor": resolve_vendor(mac)
                    })
    return devices

async def ping_sweep_subnet(subnet_base="192.168.1"):
    """Dispara pings assíncronos rápidos para forçar atualização da tabela ARP"""
    async def ping_ip(ip):
        proc = await asyncio.create_subprocess_exec(
            "ping", "-c", "1", "-W", "1", ip,
            stdout=asyncio.subprocess.DEVNULL,
            stderr=asyncio.subprocess.DEVNULL
        )
        await proc.wait()

    tasks = [ping_ip(f"{subnet_base}.{i}") for i in range(1, 255)]
    await asyncio.gather(*tasks)

async def main():
    print("[*] Atualizando cache ARP com varredura assíncrona...")
    await ping_sweep_subnet("192.168.1")

    print("\n[+] Dispositivos Ativos na LAN:")
    print(f"{'IP':<16} {'MAC':<18} {'INTERFACE':<10} {'FABRICANTE'}")
    print("-" * 65)
    for dev in read_kernel_arp_table():
        print(f"{dev['ip']:<16} {dev['mac']:<18} {dev['interface']:<10} {dev['vendor']}")

if __name__ == "__main__":
    asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

3. Criando um Serviço em Segundo Plano (Systemd)

Para que o monitoramento seja ininterrupto, salve o script em /opt/network_sentry.py e configure o serviço no Linux:

# /etc/systemd/system/network-sentry.service
[Unit]
Description=EVM Network Sentry SIEM Daemon
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /opt/network_sentry.py
Restart=always
RestartSec=180

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Ative o daemon para iniciar no boot:

sudo systemctl daemon-reload
sudo systemctl enable --now network-sentry.service
Enter fullscreen mode Exit fullscreen mode

4. O que Fazer com Dispositivos Desconhecidos?

Quando um MAC que não está na sua lista de nós autorizados é detectado, a melhor prática não é apenas emitir um alerta visual, mas aplicar medidas de contenção:

  1. Isolamento via VLAN: Separar dispositivos de automação (IoT) dos computadores pessoais de trabalho;
  2. Quarentena no Switch/Roteador: Bloquear o encaminhamento de pacotes do IP atribuído via regras de iptables ou regras de firewall no roteador gerenciável (MikroTik / OpenWrt / pfSense).

Conclusão e Recursos

Ter visibilidade em nível de camada 2 (Data Link) é a fundação para qualquer ambiente seguro. Com poucas linhas de Python assíncrono, você transforma qualquer servidor Linux comum em uma sentinela permanente contra invasões e nós não autorizados.

Publiquei a arquitetura completa com integração de eventos e banco de dados SQLite WAL no meu blog:

👉 Artigo Completo no Fymax Sentinel


Como você monitora a entrada de novos aparelhos na sua rede doméstica hoje? Usa Pi-hole, ARPWatch ou regras no roteador? Compartilhe nos comentários!

Top comments (0)