Network sniffer, bastioni di sicurezza ed UTM

Dialogo con Gemini.

Ho usato in modo esteso network sniffer opensource su Linux o Bastioni di sicurezza, in particolare UTM. Potresti fare il punto attuale?

Il panorama dell’analisi del traffico e delle appliance perimetrali open source è cambiato radicalmente negli ultimi anni. Due forze principali hanno guidato questa evoluzione: l’onnipresenza della cifratura (TLS 1.3, DoH, QUIC/HTTP3), che ha reso obsoleta la sola ispezione del payload in chiaro, e la migrazione verso paradigmi come il Zero Trust e l’architettura a microservizi/container.

1. Bastioni e UTM Open Source (NGFW)

Il concetto tradizionale di UTM monolitico “tuttofare” (routing + proxy Squid + ClamAV + firewall) ha lasciato il passo ai Next-Generation Firewall (NGFW) modulari, dove i proxy HTTP tradizionali sono quasi scomparsi a favore di motori DPI layer-7 e filtraggio basato su reputazione DNS/SNI.

  • OPNsense (HardenedBSD/FreeBSD): È diventato il punto di riferimento primario per innovazione open source. Architettura pulita con framework MVC e API REST native, aggiornamenti bi-settimanali puntuali e integrazione di motori moderni: Regulus
    • Zenarmor (ex Sensei): plugin di punta che trasforma OPNsense in un vero NGFW Layer 7 con classificazione applicativa e controlli granulari (senza necessitare di proxy intermedi).
    • Suricata integrato: sfruttato in modalità inline (Netmap) per IPS ad alte prestazioni. AIMultiple
  • pfSense (CE vs Plus): La versione open source pura (Community Edition) è stata progressivamente messa in secondo piano da Netgate a favore di pfSense Plus (closed-source/commerciale). Resta estremamente stabile ed esteso negli ambienti legacy, ma la community indipendente è migrata in gran parte verso OPNsense.
  • IPFire (Linux-based): Basato su kernel Linux puro e stack Netfilter/nftables. Molto focalizzato sulla sicurezza minimalista e sulla resilienza, impiega Suricata come motore IPS predefinito. Ideale quando si cerca un’impronta Linux nativa leggera anziché un sistema BSD.
  • Distribuzioni storiche (IPCop, Endian, Smoothwall, Untangle):
    • IPCop e Smoothwall GPL: sostanzialmente abbandonati da anni o fermi a vecchi rami storici.
    • Untangle (NG Firewall): acquisito da Arista Networks, ha chiuso i canali gratuiti/community diventando una soluzione proprietaria enterprise a pagamento. AIMultiple
    • Endian: mantiene una Community Edition, ma l’innovazione principale si è spostata sulla gamma commerciale IoT/industrial. AIMultiple

2. Sniffing e Network Security Monitoring (NSM) BestDefense

La cattura pacchetti non si limita più al solo dump sequenziale su disco via libpcap, ma si divide in tre livelli operativi:

  • Analisi Forense e Protocol Decoding: BestDefense
    • Wireshark & tcpdump: Restano lo standard de facto assoluto per l’analisi interattiva e la risoluzione di problemi puntuali. Wireshark include ormai decodificatori maturi per HTTP/3, QUIC, WireGuard e meccanismi avanzati per iniettare chiavi di sessione TLS (SSLKEYLOGFILE). ET CIO
  • Network Analysis a Livello Metadati & Threat Detection:
    • Zeek (ex Bro): Non memorizza interi payload ma trasforma il traffico grezzo in metadati strutturati ad altissima densità informativa (sessioni TLS, impronte JA3/JA4, transazioni DNS, SMB, SSH). È il pilastro per l’invio di telemetria di rete verso SIEM (Elasticsearch, OpenSearch, Wazuh). BestDefense+ 1
    • Suricata: Ha definitivamente superato Snort negli ambienti Linux moderni grazie alla sua architettura nativamente multi-thread, al supporto eBPF/XDP per il bypass dei pacchetti noti a velocità di linea e al parser Rust per i protocolli ad alto livello. LinuxTeck
    • Arkime (ex Moloch): Lo strumento open source standard per la cattura full-packet indicizzata su larga scala con interfaccia web per investigazioni retroattive. BestDefense
    • ntopng / nDPI: Ha rimpiazzato i vecchi ntop e bmon per la telemetria L7 in tempo reale, introducendo l’analisi del traffico cifrato tramite impronte comportamentali ed eBPF. BestDefense

3. Il cambio di paradigma su Linux: da libpcap a eBPF/XDP

Se il tuo background è incentrato su iptables, syslog e socket RAW tradizionali (libpcap), la vera rottura tecnologica nel kernel Linux odierno è rappresentata da:

  • eBPF (Extended Berkeley Packet Filter): Permette di eseguire codice sandboxato ad altissima velocità nello spazio kernel senza modificare i sorgenti del kernel o caricare moduli instabili. DEV Community
  • XDP (eXpress Data Path): Intercetta i pacchetti direttamente a livello di driver di rete della scheda (NIC) prima che vengano allocate le strutture sk_buff del kernel. Consente di applicare filtri packet-drop anti-DDoS e reindirizzamento a decine di milioni di pacchetti al secondo su hardware standard.
  • Observability e Runtime Security: Strumenti come Cilium, Tetragon e Falco correlano il pacchetto di rete non solo con IP/porta, ma istantaneamente con il PID del processo, il namespace, il descrittore del file e l’eventuale container che ha generato la chiamata di sistema.

Sintesi dello stack attuale

Scenario d’usoStrumenti di riferimento oggi
Gateway NGFW / Bastione perimetraleOPNsense (con Zenarmor + Suricata) o IPFire
Ispezione L7 / IDS-IPS ad alte prestazioniSuricata (in modalità multithread / AF_PACKET / eBPF)
Network Security Monitoring & Log di sessioneZeek + stack Elastic/OpenSearch
Cattura e ricerca Full-Packet (PCAP)Arkime (infrastruttura) / Wireshark (debug locale)
Ispezione a bassissimo overhead a livello hosteBPF / XDP (con strumenti come Tetragon, Cilium o script bpftrace)
Confronta l’efficacia e le prestazioni di OPNsense rispetto a un gateway Linux personalizzato basato su nftables e Suricata.

Il confronto tra un’appliance chiavi in mano come OPNsense (basata su FreeBSD/HardenedBSD e packet filter pf) e un gateway Linux bare-metal personalizzato (basato su nftables e Suricata) mette a nudo il compromesso storico tra comodità operativa/governance centralizzata e prestazioni grezze/densità di throughput.

1. Architettura del Packet Processing e Prestazioni

La differenza fondamentale risiede nel modo in cui i due kernel gestiscono lo stack di rete e l’I/O ad alto carico.

  • Linux (nftables + Netfilter):
    • Scalabilità multi-core: Il networking subsystem del kernel Linux scala in modo più lineare su CPU multi-core moderne grazie a meccanismi avanzati di Receive Side Scaling (RSS), RPS/RFS e all’allocazione efficiente dei descrittori di rete.
    • Efficienza delle regole: nftables compila i set di regole in un bytecode eseguito da una macchina virtuale interna al kernel. L’uso nativo di tabelle hash, intervalli (range) e mappe eterogenee (es. concatenazioni IP-porta in una sola lookup O(1)) riduce drasticamente i cicli CPU rispetto al processamento sequenziale.
    • Zero-Copy e bypass kernel: Linux supporta nativamente socket ad altissima velocità come AF_PACKET (in modalità PACKET_MMAP), AF_XDP e hook eBPF/XDP. Questo consente di scartare pacchetti malevoli o inviarli a Suricata prima ancora che il kernel allochi la struttura dati sk_buff.
  • FreeBSD/OPNsense (pf):
    • Architettura di pf: Il packet filter pf su BSD è storicamente elegante e robusto, ma soffre di un overhead di lock a livello di stato delle connessioni (state table locking) più marcato quando si sale a decine di gigabit o a centinaia di migliaia di nuove connessioni al secondo.
    • Driver di rete: Sebbene FreeBSD sia eccellente su schede di rete di classe server (come Intel ixgbe o ice), il supporto per NIC di fascia consumer (es. Realtek 2.5GbE) o hardware 10/25/100GbE di ultimissima generazione è spesso meno ottimizzato o in ritardo rispetto al kernel Linux.
  • Verdetto prestazioni grezze: A parità di hardware (in particolare su link 10Gbps+ o carichi con milioni di PPS), il gateway Linux offre una latenza inferiore, un throughput di routing/NAT significativamente più alto e un utilizzo della CPU più ridotto.

2. Integrazione del motore IPS (Suricata)

Il modo in cui Suricata intercetta e analizza il traffico determina l’impatto prestazionale dell’ispezione Deep Packet Inspection:

  • In OPNsense (via Netmap):
    • OPNsense integra Suricata sfruttando il framework Netmap per l’I/O veloce a livello utente in modalità inline IPS.
    • Punto debole: Netmap richiede un supporto dedicato e privo di bug da parte dei driver della scheda di rete. Se il driver della NIC non implementa Netmap alla perfezione, si possono verificare memory leak, kernel panic o instabilità nei cambi di stato delle interfacce.
  • Nel gateway Linux personalizzato (via AF_PACKET o NFQUEUE / eBPF):
    • Suricata su Linux brilla in modalità AF_PACKET inline con supporto multi-queue (FANOUT): ogni coda di ricezione hardware della NIC viene mappata su un thread dedicato di Suricata, azzerando i cambi di contesto.
    • È possibile abilitare il bypass via eBPF: una volta che Suricata ha classificato un flusso cifrato come lecito (es. uno stream video o una sincronizzazione file affidabile), delega la gestione del flusso direttamente a XDP/eBPF nel kernel, eliminando l’overhead di analisi sui pacchetti successivi.

3. Gestione, Manutenzione e Sicurezza Operativa

Le prestazioni non sono l’unico parametro; il costo operativo è l’elemento determinante nella scelta:

  • OPNsense:
    • Ciclo di vita: Aggiornamenti bi-settimanali del firmware, snapshot ZFS con possibilità di rollback immediato al bootloader (boot environments) in caso di aggiornamento fallito.
    • Governance integrata: Generazione automatica di certificati Let’s Encrypt, gestione utenti, configurazione visuale di tunnel WireGuard/IPsec, backup cifrati e integrazione out-of-the-box con Zenarmor per il controllo Layer 7.
    • Visibilità: Dashboard pronte all’uso, grafici di carico e reportistica integrata senza necessità di configurare collector esterni.
  • Linux custom (nftables + Suricata):
    • Complessità sistemistica: Richiede la scrittura e il versionamento manuale dei file di configurazione (/etc/nftables.conf, suricata.yaml), la gestione dei set dinamici (nftset o integrazione con ipset/fail2ban) e la manutenzione dei download dei ruleset di Suricata (es. tramite suricata-update in cron).
    • Stack di monitoraggio separato: Per avere visibilità comparabile a OPNsense, è necessario configurare pipeline esterne: ad esempio esportare i log eve.json di Suricata su uno stack centralizzato (Vector/Logstash verso Elasticsearch o Graylog) e visualizzare le metriche con Prometheus/Grafana.

Confronto Tecnico di Sintesi

CaratteristicaOPNsense (FreeBSD / pf)Linux Gateway (nftables + Suricata)
Throughput Routing & NATMolto buono fino a 2.5–10 Gbps; fatica a scalare oltre con hardware commodityEccellente; scala linearmente fino a saturazione del bus (10–40+ Gbps)
DPI / IPS (Suricata)Inline tramite Netmap (dipendente dalla qualità dei driver BSD)Inline nativo tramite AF_PACKET + eBPF bypass (massima stabilità ed efficienza)
Filtraggio pacchetti L3/L4Sintassi pf leggibile, gestione stati robusta ma con lock centralizzatinftables con lookup O(1), mappe composite e supporto a filtri XDP stateless
Layer 7 / NGFWFacile tramite plugin Zenarmor (DPI, categorie Web, controlli applicativi)Richiede Suricata con regole L7/Lua dedicate o proxy Layer 7 a monte
Configurazione & GestioneGUI Web completa, API REST, gestione certificati e VPN integrataCLI pura, script Bash/Ansible, gestione manuale di WireGuard e routing
Disaster RecoveryRollback istantaneo a livello di bootloader grazie a snapshot ZFSDipendente dalla strategia di automazione dell’infrastruttura (es. Ansible / Git)

Indicazioni per la scelta

  • Scegli OPNsense se: Hai bisogno di un bastione perimetrale completo, gestibile da un unico pannello web, con un motore NGFW Layer 7 pronto (Zenarmor) e tempi di messa in produzione minimi, su linee fino a 1–2.5 Gbps.
  • Scegli il gateway Linux personalizzato se: Operi su uplink multi-gigabit (10G/25G), hai a che fare con hardware x86 recente o schede di rete che beneficiano del supporto driver nativo di Linux, desideri sfruttare eBPF/XDP per mitigazione DoS a velocità di linea e preferisci gestire l’infrastruttura come codice (IaC/Ansible).

Mostrami una configurazione minimale ma performante di nftables con gestione di set dinamici e interfaccia con Suricata in AF_PACKET.

Architettura della soluzione

Nel deployment con Suricata inline IPS via AF_PACKET, Suricata agisce come bridge trasparente a livello 2 tra due interfacce fisiche (es. eth0 WAN ed eth1 LAN), oppure su una coppia di interfacce virtuali (veth/MACVLAN).

Quando Suricata intercetta il traffico in modalità bridge AF_PACKET, i pacchetti vengono acquisiti direttamente dal ring buffer della scheda, processati dai worker multithread e, se non scartati (drop), reiniettati sulla scheda opposta. Lo stack Netfilter/nftables interviene a livello host prima e dopo il processing di rete per:

  1. Gestire lo stateful filtering (L3/L4) con lookup O(1).
  2. Bloccare istantaneamente IP o subnet abusive tramite set dinamici ad alta efficienza con timeout automatico (evitando memory leak).
  3. Eseguire NAT/masquerading per la subnet interna.

1. Configurazione performante di /etc/nftables.conf

Questa configurazione sfrutta le mappe concatenate e i set con flag dynamic e timeout per l’auto-scadenza degli host malevoli.

Snippet di codice

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    # Set dinamico per IP bloccati con timeout automatico
    set dynamic_denylist {
        type ipv4_addr
        flags dynamic, timeout
        timeout 1h
        size 65535
    }

    # Set per porte TCP consentite sul bastione stesso
    set bastion_tcp_services {
        type inet_service
        elements = { 22 }
    }

    # Catena di ingresso verso l'host stesso
    chain input {
        type filter hook input priority filter; policy drop;

        # 1. Loopback
        iif "lo" accept

        # 2. Connessioni già tracciate (fast-path hardware-friendly)
        ct state established,related accept
        ct state invalid drop

        # 3. Blocco immediato se presente nella blacklist dinamica (lookup O(1))
        ip saddr @dynamic_denylist drop

        # 4. Accesso ai servizi di gestione del bastione
        iif "eth1" tcp dport @bastion_tcp_services ct state new accept

        # 5. ICMP essenziale (ping limitato per evitare flood)
        ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } limit rate 5/second accept
    }

    # Catena di transito (Routing / Forwarding)
    chain forward {
        type filter hook forward priority filter; policy drop;

        # Blocco preventivo da denylist anche in transito
        ip saddr @dynamic_denylist drop
        ip daddr @dynamic_denylist drop

        # Stateful forwarding
        ct state established,related accept
        ct state invalid drop

        # Consenti traffico in uscita dalla LAN (eth1 -> eth0)
        iif "eth1" oif "eth0" accept
    }
}

table ip nat {
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;

        # Masquerade su WAN per il traffico originato da LAN
        iif "eth1" oif "eth0" masquerade
    }
}

Operazioni sui set dinamici da riga di comando

Puoi aggiungere e verificare dinamicamente gli host (manualmente o tramite script integrati con i log eve.json di Suricata):

  • Aggiungere un IP con timeout specifico:Bashnft add element inet filter dynamic_denylist { 198.51.100.42 timeout 30m }
  • Verificare il set e il tempo residuo per ciascun elemento:Bashnft list set inet filter dynamic_denylist

2. Configurazione di Suricata (suricata.yaml) in modalità AF_PACKET Inline

Nel file /etc/suricata/suricata.yaml, la sezione af-packet deve essere configurata per agganciare le code hardware della scheda (cluster-type: cluster_flow) con ring buffer a memoria mappata (mmap per azzerare le copie in userspace).

YAML

af-packet:
  # Interfaccia Esterna (WAN)
  - interface: eth0
    threads: auto
    cluster-id: 99
    cluster-type: cluster_flow
    defrag: yes
    use-mmap: yes
    mmap-locked: yes
    tpacket-v3: yes
    ring-size: 2048
    block-size: 32768
    # Inoltro diretto verso l'interfaccia interna
    copy-mode: ips
    copy-iface: eth1

  # Interfaccia Interna (LAN)
  - interface: eth1
    threads: auto
    cluster-id: 98
    cluster-type: cluster_flow
    defrag: yes
    use-mmap: yes
    mmap-locked: yes
    tpacket-v3: yes
    ring-size: 2048
    block-size: 32768
    # Inoltro diretto verso l'interfaccia esterna
    copy-mode: ips
    copy-iface: eth0

3. Tuning hardware e offload delle schede di rete

Per far operare Suricata in modalità inline senza corruzione del checksum o disallineamento dei pacchetti, è obbligatorio disabilitare gli offload hardware LRO/GRO sulle interfacce coinvolte.

Esegui (o inserisci nello script di avvio di rete):

Bash

# Disabilita Large Receive Offload e Generic Receive Offload su entrambe le interfacce
ethtool -K eth0 gro off lro off tso off gso off
ethtool -K eth1 gro off lro off tso off gso off

# Aumenta i buffer di ricezione/trasmissione hardware della NIC al massimo supportato
ethtool -G eth0 rx 4096 tx 4096
ethtool -G eth1 rx 4096 tx 4096

4. Flusso operativo di mitigazione

  1. L1/L2: Il traffico arriva sulla NIC (eth0); i ring buffer AF_PACKET trasferiscono i chunk di memoria direttamente allo spazio di memoria condiviso di Suricata (use-mmap: yes).
  2. L7 (Suricata): I thread analizzano i protocolli applicativi e le firme IDS. Se scatta una regola drop, il pacchetto viene scartato e registrato su /var/log/suricata/eve.json.
  3. Bridge/Inoltro: I pacchetti ammessi vengono reiniettati sulla seconda interfaccia (eth1).
  4. L3/L4 (nftables): Gestisce il routing, il masquerading (NAT) e intercetta gli IP recidivi tramite il set dynamic_denylist, scartandoli prima che impegnino risorse di calcolo a livello utente.

Spiegami in dettaglio come configurare il bypass dei flussi cifrati in Suricata sfruttando eBPF e XDP su Linux.

Il bypass via eBPF/XDP (chiamato anche eBPF Flow Bypass) è una delle ottimizzazioni prestazionali più incisive per Suricata su Linux.

Quando un flusso di rete non richiede ulteriore ispezione L7 (ad esempio una sessione TLS/HTTPS validata dopo l’handshake, un tunnel WireGuard o un grosso trasferimento dati già classificato come sicuro), Suricata scrive i metadati del flusso (5-tuple) in una BPF hash map condivisa con il kernel. Da quel momento, il programma XDP agganciato al driver della scheda di rete intercetta i pacchetti successivi e li inoltra direttamente a velocità di linea, evitando completamente il passaggio verso lo userspace e l’allocazione delle strutture sk_buff.

Prerequisiti di Sistema

  • Kernel Linux: Versione 5.4 o superiore (consigliato 6.x con supporto eBPF completo).
  • Pacchetti e librerie: clang, llvm, libbpf-dev, libelf-dev, iproute2.
  • Suricata: Compilato con supporto eBPF abilitato (--enable-ebpf). Su distribuzioni moderne (Ubuntu/Debian via repository OISF o pacchetti ufficiali), il supporto è solitamente compilato di serie.

Verifica il supporto da terminale:

Bash

suricata --build-info | grep -i ebpf
# Output atteso: eBPF support: yes

1.Compilare il programma eBPF di Suricata:Kernel space filter.

Suricata fornisce i sorgenti C per i moduli eBPF e XDP all’interno del proprio albero sorgente o in /usr/share/suricata/ebpf/ (a seconda del pacchetto della distribuzione).

Se i binari .o non sono precompilati, spostati nella directory del codice sorgente eBPF e compilali con Clang/LLVM:

Bash

cd /usr/share/suricata/ebpf # o path ai sorgenti: suricata/ebpf/
clang -O2 -g -target bpf -Wall -c xdp_filter.c -o /etc/suricata/ebpf/xdp_filter.o
clang -O2 -g -target bpf -Wall -c lb.c -o /etc/suricata/ebpf/lb.o

Verifica del successo: Il file /etc/suricata/ebpf/xdp_filter.o deve esistere e l’output di file /etc/suricata/ebpf/xdp_filter.o deve confermare un file oggetto ELF 64-bit per architettura BPF.

2.Configurare lo stream bypass in suricata.yaml:suricata.yaml.

Modifica /etc/suricata/suricata.yaml per abilitare l’engine di stream bypass sia a livello generale che all’interno delle interfacce af-packet.

Nel blocco globale di configurazione dello stream:

YAML

stream:
  memcap: 1gb
  checksum-validation: yes
  inline: yes
  reassembly:
    memcap: 2gb
    depth: 1mb
    toserver-chunk-size: 2560
    toclient-chunk-size: 2560
  # Abilita il motore di bypass
  bypass: yes

Nel blocco af-packet, sostituisci la configurazione base con i parametri XDP e le mappe BPF:

YAML

af-packet:
  - interface: eth0
    threads: auto
    cluster-id: 99
    cluster-type: cluster_flow
    defrag: yes
    use-mmap: yes
    tpacket-v3: yes
    ring-size: 2048
    block-size: 32768
    copy-mode: ips
    copy-iface: eth1
    # Integrazione eBPF / XDP
    ebpf: yes
    ebpf-filter-file: /etc/suricata/ebpf/xdp_filter.o
    xdp-mode: driver   # Opzioni: driver (nativo) o generic (fallback CPU)
    bypass: yes

  - interface: eth1
    threads: auto
    cluster-id: 98
    cluster-type: cluster_flow
    defrag: yes
    use-mmap: yes
    tpacket-v3: yes
    ring-size: 2048
    block-size: 32768
    copy-mode: ips
    copy-iface: eth0
    # Integrazione eBPF / XDP
    ebpf: yes
    ebpf-filter-file: /etc/suricata/ebpf/xdp_filter.o
    xdp-mode: driver
    bypass: yes

Nota su xdp-mode: L’impostazione driver esegue il codice all’interno del driver della NIC (massime prestazioni). Se la tua scheda non lo supporta, passa temporaneamente a generic (funziona ovunque ma interviene poco dopo nello stack kernel).

Verifica del successo: Esegui un test di validazione sintattica:

Bash

suricata -T -c /etc/suricata/suricata.yaml

L’output deve terminare con Configuration provided was successfully parsed.

3.Istruire Suricata al bypass dei flussi cifrati:Signature rules.

Il bypass non avviene automaticamente per qualsiasi connessione; è necessario definire quali flussi contrassegnare con la direttiva bypass.

Crea o modifica un file di regole locali (es. /etc/suricata/rules/bypass.rules):

suricata

# 1. Bypass del traffico TLS una volta convalidato l'handshake
pass tls any any -> any any (msg:"Bypass established TLS sessions"; tls.subject:"*"; flow:established; bypass; sid:9900001; rev:1;)

# 2. Bypass per tunnel cifrati WireGuard noti (es. porta 51820)
pass udp any any -> any 51820 (msg:"Bypass WireGuard Tunnel"; flow:stateless; bypass; sid:9900002; rev:1;)

# 3. Bypass per trasferimenti HTTPS basati su SNI fidato (es. video streaming / backup)
pass tls any any -> any any (msg:"Bypass trusted SNI"; tls.sni; content:"download.videolan.org"; endswith; flow:established; bypass; sid:9900003; rev:1;)

Assicurati che /etc/suricata/rules/bypass.rules sia incluso nella direttiva rule-files: del tuo suricata.yaml.

Verifica del successo: Riavvia il servizio Suricata e controlla /var/log/suricata/suricata.log per accertarti che il ruleset sia caricato senza errori di parsing.

4.Verifica del funzionamento a runtime:bpftool e statistiche.

Per verificare che il programma XDP sia caricato sulle schede e che i pacchetti vengano effettivamente bypassati:

  1. Controlla il caricamento XDP sull’interfaccia:

Bash

ip link show dev eth0

Dovresti notare la dicitura xdp o xdpdrv con l’ID del programma agganciato.

  1. Ispeziona la BPF Map dei flussi:Usa l’utility del kernel bpftool per vedere le connessioni memorizzate nella tabella hash eBPF:

Bash

bpftool map show
bpftool map dump name flow_table_v4
  1. Verifica le metriche nei log di Suricata:Controlla il file /var/log/suricata/stats.log cercando i contatori di bypass:

Bash

grep "capture.kernel_packets" /var/log/suricata/stats.log
grep "flow_bypassed" /var/log/suricata/stats.log

Verifica del successo: Il contatore flow_bypassed.pkts e flow_bypassed.bytes in stats.log deve incrementarsi mentre si eseguono trasferimenti dati pesanti (es. curl https://... o speedtest).

Gestione del ciclo di vita dei flussi bypassati

La BPF map memorizza la 5-tuple e un timestamp. Suricata continua a monitorare periodicamente lo stato delle connessioni: quando rileva la chiusura della connessione (tramite pacchetti TCP FIN/RST che continuano a essere monitorati o per timeout di inattività impostato in suricata.yaml), Suricata emette una syscall per rimuovere la chiave dalla hash map eBPF, liberando memoria per le nuove sessioni.

Come posso esportare i contatori di bypass di Suricata ed eBPF verso Prometheus e Grafana?

Per esportare le metriche di bypass di Suricata ed eBPF verso Prometheus hai a disposizione due architetture:

  1. L’exporter nativo o dedicato di Suricata (eve.json / Socket): Suricata registra periodicamente gli eventi stats contenenti tutti i contatori del motore (flow_bypassed, memcap, drop) in formato JSON. Strumenti come l’exporter dedicato in Go (suricata-exporter) o un pipeline agent leggero come Vector o Telegraf leggono questo stream e lo espongono in formato Prometheus.
  2. Exporter di metriche kernel/XDP (ebpf_exporter di Cloudflare): Legge direttamente le BPF Maps in kernel space per contare i pacchetti intercettati e inoltrati a livello XDP, garantendo metriche indipendenti dal logging di Suricata.

La soluzione standard, a basso overhead e più flessibile per un gateway di produzione, consiste nell’usare il logger eve-log.stats di Suricata intercettato da suricata-exporter (o Vector), combinato con node_exporter per i contatori di rete della NIC.

1.Abilitare l’output delle statistiche in eve-log:suricata.yaml.

Assicurati che Suricata scriva gli snapshot di statistiche nel file strutturato eve.json.

Apri /etc/suricata/suricata.yaml e verifica la sezione outputs:

YAML

outputs:
  - eve-log:
      enabled: yes
      filetype: regular # oppure 'unix_stream' se colleghi un demone direttamente a socket
      filename: eve.json
      types:
        - alert
        - drop
        - stats:
            totals: yes       # accumula i totali
            threads: no       # evita payload enormi se non ti servono statistiche per-core
            deltas: no

Imposta l’intervallo di campionamento delle statistiche (es. ogni 10 secondi per allinearlo allo scrape di Prometheus):

YAML

stats:
  enabled: yes
  interval: 10

Ricarica la configurazione di Suricata:

Bash

suricatasc -c reload-rules
# oppure: systemctl restart suricata

Verifica del successo: Controlla che vengano generati record di tipo stats eseguendo:

Bash

tail -f /var/log/suricata/eve.json | jq 'select(.event_type=="stats") | .stats.flow_bypassed'

Dovresti visualizzare l’oggetto JSON con le chiavi pkts, bytes e closed.

2.Installare e configurare suricata-exporter:Servizio di scraping L7.

Il binario suricata-exporter esegue il tailing su eve.json (oppure legge dal socket UNIX) ed espone le metriche sulla porta :9917/metrics.

  1. Scarica o compila il binario:

Bash

# Esempio scaricando la release precompilata (adatta ad architetture x86_64 o arm64)
wget https://github.com/dalmar/suricata-exporter/releases/latest/download/suricata-exporter-linux-amd64 -O /usr/local/bin/suricata-exporter
chmod +x /usr/local/bin/suricata-exporter
  1. Crea l’unità systemd /etc/systemd/system/suricata-exporter.service:

Ini, TOML

[Unit]
Description=Suricata Prometheus Exporter
After=network.target suricata.service

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/suricata-exporter --eve-path=/var/log/suricata/eve.json --listen-address=:9917
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
  1. Abilita e avvia il servizio:

Bash

systemctl daemon-reload
systemctl enable --now suricata-exporter

Verifica del successo: Interroga l’endpoint metrics via curl:

Bash

curl -s http://localhost:9917/metrics | grep suricata_flow_bypassed

Dovresti vedere metriche come suricata_flow_bypassed_pkts_total e suricata_flow_bypassed_bytes_total.

3.Configurare il job di scraping su Prometheus:Target definition.

Aggiungi il target del bastione nel file di configurazione di Prometheus (/etc/prometheus/prometheus.yml):

YAML

scrape_configs:
  - job_name: 'suricata'
    scrape_interval: 10s
    static_configs:
      - targets: ['127.0.0.1:9917'] # O l'IP di gestione del bastione
        labels:
          host: 'gateway-core'
          role: 'ngfw-suricata'

Ricarica Prometheus:

Bash

curl -X POST http://localhost:9090/-/reload

Verifica del successo: Nell’interfaccia web di Prometheus (Status -> Targets), il target suricata deve risultare in stato UP.

4.Costruire le query e la dashboard in Grafana:Dashboard & PromQL.

Crea una dashboard in Grafana per visualizzare il rapporto tra traffico ispezionato L7 e traffico scaricato in eBPF/XDP.

Query PromQL chiave per i pannelli:

  • Rapporto di Throughput Bypassato vs Ispezionato (Bytes/sec):

promql

# Throughput bypassato (eBPF/XDP)
rate(suricata_flow_bypassed_bytes_total[1m]) * 8

promql

# Throughput ispezionato da Suricata a livello utente
rate(suricata_capture_kernel_bytes_total[1m]) * 8
  • Percentuale di pacchetti bypassati (Offload Ratio):

promql

(
  rate(suricata_flow_bypassed_pkts_total[1m])
  /
  (rate(suricata_capture_kernel_packets_total[1m]) + rate(suricata_flow_bypassed_pkts_total[1m]))
) * 100
  • Flussi bypassati attivi (Stima flussi aperti al secondo):

promql

rate(suricata_flow_bypassed_closed_total[1m])
  • Pacchetti scartati o persi (Drop rate dell’engine):

promql

rate(suricata_capture_kernel_drops_total[1m])

Verifica del successo: Genera traffico TLS pesante da un host client (es. curl -k [https://speed.hetzner.de/1GB.bin](https://speed.hetzner.de/1GB.bin) -o /dev/null). Il pannello in Grafana deve mostrare una netta predominanza di banda sul contatore bypass rispetto al volume ispezionato dai thread L7.

Opzione Avanzata: Conteggio diretto delle BPF Maps via Cloudflare ebpf_exporter

Se desideri misurare l’I/O direttamente dai contatori del kernel gestiti dal file xdp_filter.o senza dipendere dall’intervallo di flush di eve.json, puoi installare ebpf_exporter:

  1. Scrivi un file di configurazione BPF YAML (es. /etc/ebpf_exporter/suricata_xdp.yaml):YAMLprograms: - name: xdp_suricata_flow metrics: counters: - name: xdp_bypass_packets help: Pacchetti processati direttamente via XDP bypass table: flow_table_v4 kprobes: {}
  2. Questo consente di plottare la latenza e i pacchetti scartati/inoltrati con risoluzione al sub-secondo, ideale per verificare se schede 10G/25G stanno saturando i ring buffer di rete.

Mostrami come usare Vector per inviare metriche ed alert di Suricata contemporaneamente a Prometheus ed Elasticsearch.

Usare Vector (scritto in Rust, ad altissime prestazioni e bassissimo footprint di memoria) è una scelta ideale su un gateway/bastione rispetto a Logstash o Fluentd: con pochissimi megabyte di RAM può gestire decine di migliaia di eventi al secondo senza rubare cicli CPU al motore IPS.

In questa architettura, Vector:

  1. Legge /var/log/suricata/eve.json in streaming.
  2. Esegue il parsing JSON con il linguaggio nativo VRL (Vector Remap Language).
  3. Instrada gli eventi:
    • Gli eventi di tipo alert (e opzionalmente drop) vengono normalizzati e inviati via HTTP a Elasticsearch (o OpenSearch).
    • Gli eventi di tipo stats vengono trasformati in metriche aggregate ed esposti sull’endpoint /metrics per lo scrape di Prometheus.

Configurazione Completa (/etc/vector/vector.yaml)

YAML

# ==============================================================================
# SORGENTE DATI
# ==============================================================================
sources:
  suricata_eve:
    type: file
    include:
      - /var/log/suricata/eve.json
    read_from: end
    # Opzionale: evita di rileggere file giganti dopo rotazione log
    max_line_bytes: 1048576

# ==============================================================================
# TRASFORMAZIONI E ROUTING
# ==============================================================================
transforms:
  # 1. Parsing JSON iniziale
  parse_eve:
    type: remap
    inputs:
      - suricata_eve
    source: |
      . = parse_json!(string!(.message))

  # 2. Separazione dei flussi in base a event_type
  split_eve:
    type: route
    inputs:
      - parse_eve
    route:
      alerts: '.event_type == "alert" || .event_type == "drop"'
      stats: '.event_type == "stats"'

  # 3. Normalizzazione degli alert per Elasticsearch
  format_alerts:
    type: remap
    inputs:
      - split_eve.alerts
    source: |
      # Estrai il timestamp originale di Suricata
      .@timestamp = parse_timestamp!(.timestamp, format: "%Y-%m-%dT%H:%M:%S.%f%z")
      
      # Normalizza i campi principali per facilitare le query in Kibana
      .signature = .alert.signature
      .severity = .alert.severity
      .action = .alert.action
      .category = .alert.category
      
      # Rimuovi campi ridondanti o troppo verbosi se non necessari
      del(.message)

  # 4. Estrazione metriche da stats.log per Prometheus
  extract_metrics:
    type: remap
    inputs:
      - split_eve.stats
    source: |
      # Creazione delle metriche Prometheus usando la funzione metric()
      
      # Metrica 1: Pacchetti bypassati (eBPF/XDP)
      if exists(.stats.flow_bypassed.pkts) {
        . = {
          "name": "suricata_flow_bypassed_pkts_total",
          "kind": "absolute",
          "counter": { "value": to_float!(.stats.flow_bypassed.pkts) },
          "tags": { "host": get_hostname!() }
        }
      }

      # Metrica 2: Byte bypassati (eBPF/XDP)
      if exists(.stats.flow_bypassed.bytes) {
        . = {
          "name": "suricata_flow_bypassed_bytes_total",
          "kind": "absolute",
          "counter": { "value": to_float!(.stats.flow_bypassed.bytes) },
          "tags": { "host": get_hostname!() }
        }
      }

      # Metrica 3: Pacchetti totali processati dal kernel
      if exists(.stats.capture.kernel_packets) {
        . = {
          "name": "suricata_capture_kernel_packets_total",
          "kind": "absolute",
          "counter": { "value": to_float!(.stats.capture.kernel_packets) },
          "tags": { "host": get_hostname!() }
        }
      }

      # Metrica 4: Pacchetti scartati a livello capture (drop/loss)
      if exists(.stats.capture.kernel_drops) {
        . = {
          "name": "suricata_capture_kernel_drops_total",
          "kind": "absolute",
          "counter": { "value": to_float!(.stats.capture.kernel_drops) },
          "tags": { "host": get_hostname!() }
        }
      }

# ==============================================================================
# DESTINAZIONI (SINKS)
# ==============================================================================
sinks:
  # Invio Alert a Elasticsearch / OpenSearch
  elasticsearch_out:
    type: elasticsearch
    inputs:
      - format_alerts
    endpoints:
      - "http://10.0.0.50:9200" # Inserisci l'IP del cluster Elastic
    mode: bulk
    bulk:
      index: "suricata-alerts-%Y.%m.%d"
    # Configurazione autenticazione se abilitata
    auth:
      strategy: basic
      user: "elastic"
      password: "YourStrongPasswordHere"
    compression: gzip
    batch:
      max_events: 1000
      timeout_secs: 5

  # Endpoint scraping Prometheus per le metriche
  prometheus_out:
    type: prometheus_exporter
    inputs:
      - extract_metrics
    address: "0.0.0.0:9090" # Vector fa da server metrics Prometheus
    default_namespace: "suricata"

Procedura di Messa in Produzione

1.Installare Vector:Installazione.

Su distribuzioni Debian/Ubuntu o derivate, Vector è installabile con il repository ufficiale di Timber.io / Datadog:

Bash

curl -1sLf 'https://repositories.timber.io/public/vector/cfg/setup/bash.deb.sh' | sudo -E bash
sudo apt-get install vector

2.Configurare i permessi su eve.json:Permessi.

Vector gira per default sotto l’utente vector. Assicurati che l’utente abbia i permessi di lettura sul file e sulla directory di log:

Bash

usermod -aG suricata vector # aggiunge vector al gruppo suricata se presente
chmod 750 /var/log/suricata
chmod 640 /var/log/suricata/eve.json

3.Validare la configurazione e avviare il servizio:Validazione e Avvio.

Vector include un linter che testa il parsing VRL e la sintassi prima del restart:

Bash

vector validate /etc/vector/vector.yaml

Se la sintassi è valida:

Bash

systemctl restart vector
systemctl status vector

Verifica del successo:

  • Prometheus metrics: Interroga l’endpoint con curl http://localhost:9090/metrics | grep suricata_flow_bypassed.
  • Elasticsearch indexing: Controlla che l’indice giornaliero venga creato:

Bash

curl -u elastic:YourStrongPasswordHere http://10.0.0.50:9200/_cat/indices/suricata-alerts-*?v

Vantaggi di questa Architettura

  • Zero Memory Bloat: Vector alloca tipicamente < 60 MB di RSS anche durante picchi di decine di migliaia di eventi.
  • Resilienza con Backpressure: Se Elasticsearch è temporaneamente non raggiungibile o saturo di I/O, Vector gestisce buffer su disco (buffer.type: disk) evitando perdite di alert senza impattare la stabilità di Suricata.
  • Disaccoppiamento totale: Suricata si occupa unicamente di ispezionare pacchetti ed emettere il JSON in kernel/userspace; tutta la logica di smistamento, arricchimento e compatibilità verso i sistemi di telemetria è gestita all’esterno.