- IDS (af-packet): copia del traffico → Suricata vede tutto, non può bloccare niente →
[**]in fast.log - IPS (nfqueue): traffico originale trattenuto → il kernel aspetta il verdetto →
[Drop]in fast.log iptables -I INPUT -j NFQUEUE --queue-num 0è la singola regola che trasforma il sistemafail-open: no= fail-closed: se Suricata muore, tutto il traffico viene droppato- Il Docker bridge (
br-XXXX) bypassa NFQUEUE - la SYN-ACK di ritorno viene bloccata e Wazuh si disconnette - La persistenza al reboot richiede un systemd service dedicato (non
iptables-persistent, che rimuove UFW)
▶ $ history
Voglio vedere cosa succede quando Suricata non si limita a guardare il traffico, ma lo blocca davvero.
Ho già Suricata 7.0.3 installato in modalità IDS - riceve una copia di ogni pacchetto, analizza, logga. Ma il traffico passa lo stesso. Un attaccante che fa port scan lo vedo in fast.log, ma non posso farci niente.
Oggi cambia.
Il setup#
Mac (192.168.64.1) → gateway / management
Ubuntu (192.168.64.3) → defender: Suricata + Wazuh in Docker
Kali (192.168.64.200) → attaccanteUbuntu ha già Suricata 7.0.3 e Wazuh 4.14.5 in Docker (tre container: manager, indexer, dashboard). Wazuh legge l'eve.json di Suricata in tempo reale.
IDS vs NIDS: prima di toccare la configurazione#
Ho già Wazuh con il suo agent (wazuh-agent.service) che monitora l'host. Che senso ha aggiungere Suricata?
La distinzione è fondamentale per Security+ e per capire cosa stai costruendo:
┌─────────────────────────────────────────────────────────────┐
│ HIDS │
│ (Host-based Intrusion Detection) │
│ │
│ Wazuh agent gira DENTRO il sistema operativo │
│ Legge: auth.log, /etc/passwd, modifiche ai file │
│ Vede: "qualcuno ha aperto questo file alle 03:00" │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ NIDS │
│ (Network-based Intrusion Detection) │
│ │
│ Suricata gira DAVANTI al sistema operativo │
│ Legge: pacchetti di rete grezzi sull'interfaccia │
│ Vede: "qualcuno sta mandando 1000 SYN al secondo" │
│ │
└─────────────────────────────────────────────────────────────┘Non si sostituiscono. Si completano. Un port scan non lascia traccia in auth.log. Una modifica a /etc/passwd non è visibile nei pacchetti di rete. Sono layer diversi dello stesso problema.
IDS vs IPS: la distinzione che conta per l'esame#
IDS = Intrusion Detection System → rileva, non blocca
IPS = Intrusion Prevention System → rileva E bloccaNon sono prodotti diversi. È la stessa tecnologia - Suricata può fare entrambi - con una differenza di posizionamento.
L'analogia che funziona meglio: una telecamera di sorveglianza vs un tornello.
La telecamera (IDS) vede tutto. Registra tutto. Manda alert al centro di controllo. Ma il ladro attraversa l'ingresso lo stesso - la telecamera non può fermarlo fisicamente. L'analisi avviene dopo.
Il tornello (IPS) è nel percorso. Non puoi passare senza che lui decida. Se non hai il badge giusto, non passi. La decisione avviene prima che tu entri.

graph LR
subgraph IDS["IDS - Suricata af-packet"]
direction TB
P1[Pacchetto entra] --> K1[Kernel lo accetta]
K1 --> APP1[Applicazione lo riceve]
P1 -.->|copia| S1[Suricata analizza]
S1 --> L1[fast.log: alert ✅]
end
subgraph IPS["IPS - Suricata nfqueue"]
direction TB
P2[Pacchetto entra] --> NF[NFQUEUE: kernel lo trattiene]
NF --> S2[Suricata analizza]
S2 -->|ACCEPT| APP2[Applicazione lo riceve]
S2 -->|DROP| TRASH[Scartato ❌]
S2 --> L2[fast.log: Drop ✅]
end
style IDS fill:none,stroke:none
style IPS fill:none,stroke:none
La differenza visibile: in fast.log, IDS produce [**], IPS produce [Drop]. In nmap, IDS mostra porte open/closed, IPS mostra porte filtered.
Fase 1: baseline IDS#
Suricata gira già in modalità af-packet. Verifico:
sudo systemctl status suricata
grep "af-packet" /etc/suricata/suricata.yamlLancio nmap da Kali:
sudo nmap -sS 192.168.64.3Su Ubuntu:
sudo tail -f /var/log/suricata/fast.log05/28/2026 [**] [1:2010936:3] ET SCAN Nmap -sS window 1024 [**]
↑
[**] = alert, non drop. Il traffico è passato.nmap su Kali vede le porte normalmente. Questo è il baseline: Suricata vede, non agisce.
Il meccanismo NFQUEUE: il kernel aspetta#
La differenza tecnica tra af-packet e nfqueue è nel punto di intercettazione.
af-packet (IDS):
[rete] ──► [kernel] ──► [applicazione]
│
└── copia ──► [Suricata]
(fuori dal percorso)
nfqueue (IPS):
[rete] ──► [kernel] ──► [NFQUEUE] ──► [Suricata]
│ │
│ ACCEPT │ DROP
│ │
└──────────────┘
│
[applicazione]
oppure [scartato]Con NFQUEUE, il kernel trattiene ogni pacchetto in una coda numerata (--queue-num 0). Suricata legge dalla coda, analizza, e manda un verdetto: NF_ACCEPT o NF_DROP. Il kernel esegue.
Finché Suricata non risponde, il pacchetto non si muove.
È come la dogana. Non è una telecamera che fotografa quello che entra - è un funzionario che trattiene il pacco, lo apre, decide, e solo allora lo consegna o lo confisca.
La configurazione: primo errore#
Modifico /etc/suricata/suricata.yaml con vim per passare a nfqueue.
Commento la sezione af-packet e abilito nfq:
nfq:
mode: accept
repeat-mark: 1
repeat-mask: 1
fail-open: noUna nota su fail-open: no - ci torno dopo. Per ora: questa scelta ha conseguenze importanti.
Riavvio Suricata:
May 28 15:44:50 suricata: <Error> eth0: No such deviceL'interfaccia si chiama eth0 nel yaml di default. Su Ubuntu ARM su UTM si chiama enp0s1 - il naming legacy ethX è stato sostituito da naming predittivo basato su posizione del bus. Lo verifico:
ip link show
# 2: enp0s1: <BROADCAST,MULTICAST,UP,LOWER_UP>Apro il yaml con vim, cerco eth0, correggo. Niente sed su file di configurazione complessi, meglio vedere il contesto prima di modificare.
sudo systemctl restart suricata
sudo systemctl status suricataQuesta volta parte.
Zero regole#
sudo tail -20 /var/log/suricata/suricata.log
# [WARN] - No rules loaded!Suricata gira ma non ha regole. Senza regole drop, ogni pacchetto riceve ACCEPT di default - l'IPS è inline ma non blocca niente.
suricata-update scarica l'Emerging Threats Open ruleset:
sudo suricata-update
# Loaded 50241 rules50.241 regole. Adesso Suricata ha qualcosa da fare.
fast.log ed eve.json#
Suricata scrive in due file distinti, entrambi in /var/log/suricata/. Esistono per scopi diversi e si usano in momenti diversi.
/var/log/suricata/fast.log - formato testuale, leggibile a occhio, pensato per il monitoring diretto in terminale:
sudo tail -f /var/log/suricata/fast.log[**] [1:2010936:3] ET SCAN Nmap -sS window 1024 ← IDS: alert
[Drop] [1:9000002:1] IPS Aggressive Port Scan ← IPS: bloccoUtile quando sei davanti al terminale e vuoi vedere gli alert in tempo reale. Non è strutturato, non è parsabile automaticamente.
/var/log/suricata/eve.json - formato JSON strutturato, una riga per evento, progettato per essere letto da SIEM e strumenti di analisi:
sudo tail -f /var/log/suricata/eve.json | python3 -m json.tool{
"timestamp": "2026-05-29T07:29:02",
"event_type": "alert",
"src_ip": "192.168.64.200",
"dest_ip": "192.168.64.3",
"alert": {
"action": "blocked",
"signature": "IPS Aggressive Port Scan Drop",
"severity": 3
}
}EVE sta per Extensible Event Format, il formato JSON standard di Suricata per l'output strutturato. "Extensible" perché ogni tipo di evento (alert, flow, DNS, TLS, HTTP) ha i suoi campi aggiuntivi nello stesso formato base.
Wazuh non legge fast.log. Legge eve.json. Il Wazuh agent ha una voce localfile in ossec.conf che punta a quel file, lo legge riga per riga, e manda ogni evento JSON al manager in tempo reale. Il manager lo decodifica e applica le sue regole di correlazione.
flowchart LR
S[Suricata] --> F[fast.log\ntestuale]
S --> E[eve.json\nJSON strutturato]
E --> WA[Wazuh Agent]
WA --> WM[Wazuh Manager]
WM --> WD[Dashboard]
F --> OP[Operatore\nmonitoraggio diretto]
In IDS, eve.json mostra "action": "allowed". In IPS, mostra "action": "blocked". Questa è la differenza che appare nel Wazuh dashboard.
Il servizio systemd: due trappole#
Suricata in modalità nfqueue si avvia con -q 0 - il numero della coda netfilter. Il servizio systemd di default usa --pcap per af-packet.
Creo l'override:
sudo mkdir -p /etc/systemd/system/suricata.service.d/
sudo tee /etc/systemd/system/suricata.service.d/override.conf << 'EOF'
[Service]
Type=simple
ExecStart=
ExecStart=/usr/bin/suricata -c /etc/suricata/suricata.yaml -q 0 --pidfile /run/suricata.pid
EOFTrappola 1: ExecStart= vuoto prima del nuovo valore. In systemd, per sovrascrivere (non aggiungere) un ExecStart esistente, bisogna prima svuotarlo. Senza quella riga vuota, il secondo ExecStart si accumula al precedente.
Trappola 2: Type=simple invece di Type=notify. Suricata in nfqueue non manda il segnale sd_notify. Con Type=notify, systemd aspetta quel segnale per 90 secondi poi dichiara il servizio failed - anche se Suricata sta girando correttamente. Type=simple considera il processo attivo appena parte.
La regola iptables#
sudo iptables -I INPUT -j NFQUEUE --queue-num 0Questa singola riga trasforma Ubuntu da server passivo a IPS inline. Tutto il traffico INPUT passa attraverso Suricata prima di essere consegnato.
INPUT è la catena iptables che gestisce i pacchetti in arrivo all'host - quelli destinati a questo sistema. iptables ha tre catene principali: INPUT (arrivo), OUTPUT (partenza dall'host), FORWARD (transito verso altri host). Aggiungendo NFQUEUE a INPUT, ogni pacchetto che arriva a Ubuntu - da Kali, dal Mac, da internet - viene fermato e consegnato a Suricata prima che il kernel lo passi all'applicazione di destinazione.
sudo iptables -L INPUT -n --line-numbers
# 1 NFQUEUE * 0.0.0.0/0 0.0.0.0/0 NFQUEUE num 0La regola custom: drop invece di alert#
Le regole ET Open generano alert ma non droppano. Creo /var/lib/suricata/rules/local.rules:
drop tcp any any -> $HOME_NET any (
msg:"IPS Aggressive Port Scan Drop";
detection_filter:track by_src, count 20, seconds 1;
sid:9000002; rev:1; classtype:network-scan;
)La logica della regola:
drop → NF_DROP (non NF_ACCEPT)
tcp → solo TCP (non ICMP, non UDP)
any any → qualsiasi sorgente e porta
-> $HOME_NET any → verso la rete protetta ($HOME_NET: vedi sotto)$HOME_NET è una variabile Suricata definita in /etc/suricata/suricata.yaml, sezione vars → address-groups. Di default include la rete locale - nel lab 192.168.64.0/24. "Rete protetta" significa il segmento che stiamo difendendo, quello dove girano i nostri servizi. Usare $HOME_NET invece di un IP fisso rende la regola portabile: funziona senza modifiche su qualsiasi deployment dove il yaml è configurato correttamente.
detection_filter → attivazione condizionale:
track by_src → conta per IP sorgente (l'attaccante)
count 20 → dopo 20 pacchetti
seconds 1 → in un secondotrack by_src e non by_dst: voglio tracciare il comportamento dell'attaccante, non del bersaglio. Non mi interessa quale porta colpisce - mi interessa quanti SYN manda.
Una regola con detection_filter non si attiva sul primo pacchetto. Si attiva al pacchetto 21. I primi 20 passano. Questo è intenzionale: un accesso legittimo non supera quella soglia. Un nmap --min-rate 1000 la supera in meno di 20 millisecondi.
Nota: la prima versione aveva anche flags:S per matchare solo i SYN. In combinazione con detection_filter, non funzionava - Suricata contava solo i SYN puri, ma nmap manda anche ACK, RST, pacchetti ibridi. Rimosso flags:S, il blocco ha iniziato a funzionare.
Il primo blocco#
Da Kali:
sudo nmap --min-rate 1000 -p 1-1000 192.168.64.3Su Ubuntu, tail -f /var/log/suricata/fast.log:
[Drop] [**] [1:9000002:1] IPS Aggressive Port Scan Drop
{TCP} 192.168.64.200:62851 -> 192.168.64.3:843
[Drop] [**] [1:9000002:1] IPS Aggressive Port Scan Drop
{TCP} 192.168.64.200:62851 -> 192.168.64.3:907Su Kali, nmap:
PORT STATE SERVICE
22/tcp filtered ssh
80/tcp filtered http
...
999 filtered portsIDS → porta appare "open" o "closed"
(il SYN arriva, il sistema risponde con SYN-ACK o RST)
IPS → porta appare "filtered"
(il SYN non arriva mai, nmap non riceve risposta, aspetta il timeout)filtered è la firma di un firewall o IPS: nmap non riceve né risposta né RST perché il pacchetto viene scartato nel percorso, prima che il sistema operativo lo veda.
Wazuh smette di rispondere#
Con NFQUEUE attivo, Wazuh si disconnette. Il Wazuh agent non riesce più a raggiungere il manager Docker.
Per capire il problema, devo capire il percorso della connessione.
wazuh-agent (host)
│
│ connette a 127.0.0.1:1514
▼
docker-proxy (host, ascolta su 0.0.0.0:1514)
│
│ apre nuova connessione verso container
▼
172.18.0.2:1514 (container wazuh-manager)
│
│ SYN-ACK di risposta torna a 172.18.0.1 (host)
▼
INPUT chain su br-46d49de3057f → NFQUEUE → Suricatadocker-proxy è un processo che Docker lancia automaticamente per ogni porta esposta (ports: nel compose). Ascolta sull'host (0.0.0.0:1514) e, quando arriva una connessione, apre una nuova connessione separata verso il container. Non è un semplice NAT - sono due socket distinti. Il Wazuh agent parla con il docker-proxy, non direttamente con il container.
Il loopback (127.0.0.1) è accettato prima di NFQUEUE - quello funziona. Ma il docker-proxy poi apre una nuova connessione verso il container attraverso il bridge Docker (br-46d49de3057f). La SYN-ACK di risposta arriva all'host su quell'interfaccia, non sul loopback, e finisce nel NFQUEUE.
Suricata vede la SYN-ACK ma non ha mai visto il SYN originale (che è andato su OUTPUT, non INPUT). Senza traccia del flow, il timer scade.
Leggere /proc/net/tcp: little-endian e big-endian#
Per diagnosticarlo, leggo direttamente la tabella TCP del kernel nel container.
Prima devo sapere come cercare: 1514 in esadecimale è 05EA.
1514 ÷ 16 = 94 resto 10 (A)
94 ÷ 16 = 5 resto 14 (E)
5 ÷ 16 = 0 resto 5
→ 0x05EAdocker exec single-node-wazuh.manager-1 \
bash -c "grep -i '05EA' /proc/net/tcp /proc/net/tcp6 2>/dev/null"Output:
/proc/net/tcp: 0: 00000000:05EA 00000000:0000 0A ... → LISTEN
/proc/net/tcp: 8: 020012AC:05EA 010012AC:EE02 03 ... → SYN_RECV
/proc/net/tcp: 16: 020012AC:05EA 010012AC:C69A 03 ... → SYN_RECVCome si legge /proc/net/tcp:
Il formato è ip_hex:port_hex. Gli IP sono in little-endian - i byte sono in ordine inverso rispetto a come li leggiamo normalmente.
020012AC → leggo i byte da destra a sinistra:
AC = 172
12 = 18
00 = 0
02 = 2
→ 172.18.0.2 (IP del container)
010012AC →
AC = 172
12 = 18
00 = 0
01 = 1
→ 172.18.0.1 (Docker gateway = host)Le porte invece sono in big-endian (network byte order, come ci si aspetta):
05EA = 0×256 + 5×256 + 14×16 + 10 = 1514 - si legge normalmente da sinistra.
Questa differenza non è casuale. Gli IP vengono salvati così com'è in memoria su architetture x86/ARM (little-endian). Le porte seguono il network byte order perché sono usate direttamente nelle intestazioni di rete. Chi conosce Bitcoin riconosce la convenzione: anche nei raw transaction Bitcoin gli interi multi-byte sono in little-endian, mentre i campi che seguono standard di rete sono big-endian. Il kernel Linux usa la stessa logica.
Gli stati TCP:
0A (hex) = 10 (dec) = TCP_LISTEN → in ascolto, aspetta connessioni
01 (hex) = 1 (dec) = ESTABLISHED → connessione attiva
03 (hex) = 3 (dec) = TCP_SYN_RECV → SYN ricevuto, SYN-ACK mandato, aspetta ACKIl three-way handshake:
sequenceDiagram
participant C as docker-proxy (host)
participant S as container :1514
participant NF as NFQUEUE (INPUT chain)
C->>S: SYN → (OUTPUT chain, non NFQUEUE)
S->>NF: SYN-ACK → (INPUT su br-46d49de3057f)
Note over NF: Suricata: non conosco questo flow
NF-->>NF: timeout / stallo
Note over S: stato: SYN_RECV (stuck)
Note over C: nessun ACK → connessione non si apre
Due connessioni bloccate in SYN_RECV - il container ha risposto con SYN-ACK, il docker-proxy non lo ha mai ricevuto. Il three-way handshake non si chiude.
Il fix: accettare il bridge Docker prima di NFQUEUE#
sudo iptables -I INPUT 2 -i br-46d49de3057f -j ACCEPTIl SYN-ACK del container ora viene accettato prima di raggiungere NFQUEUE. Il three-way handshake si completa. Wazuh si riconnette.
Il bridge che cambia nome ad ogni restart#
br-46d49de3057f - il nome è l'UUID della rete Docker. Ogni docker compose down && up ricrea la rete con un UUID diverso. La regola iptables diventa stale, cioè obsoleta: punta a un'interfaccia che non esiste più. Il kernel la ignora silenziosamente, il bridge nuovo non viene accettato, Wazuh ricade in SYN_RECV.
Nel docker-compose.yml aggiungo una sezione networks: che fissa il nome del bridge Linux:
networks:
default:
driver: bridge
driver_opts:
com.docker.network.bridge.name: wazuh-br0
ipam:
config:
- subnet: 172.20.0.0/24default è un nome speciale in Docker Compose: tutti i servizi lo usano automaticamente senza modifiche alle singole service definition.
Dopo docker compose down && up:
ip link show wazuh-br0
# 19: wazuh-br0: <BROADCAST,MULTICAST,UP,LOWER_UP>Nome stabile. Regola iptables permanente.
La catena iptables completa#
Dopo tutti i fix, la catena INPUT ha questa struttura:
┌─────────────────────────────────────────────────────────────────┐
│ Chain INPUT (policy DROP) │
│ │
│ 1 ACCEPT -i lo → loopback │
│ agent → docker-proxy (127.0.0.1) │
│ │
│ 2 ACCEPT -i wazuh-br0 → Docker bridge │
│ docker-proxy → container (SYN-ACK di ritorno) │
│ │
│ 3 ACCEPT -s 192.168.64.1 :22 → SSH dal Mac │
│ management, bypass IPS │
│ │
│ 4 ACCEPT ESTABLISHED,RELATED → risposte a connessioni │
│ uscenti (apt, curl, update) │
│ │
│ 5 NFQUEUE --queue-num 0 → tutto il resto │
│ → Suricata decide │
│ │
│ 6+ UFW chains │
└─────────────────────────────────────────────────────────────────┘Perché la regola 4 (ESTABLISHED,RELATED)?
Suricata in NFQUEUE vede solo INPUT - i pacchetti in arrivo. Non vede i SYN uscenti (che vanno su OUTPUT, catena diversa). Quando una risposta SYN-ACK arriva da un server esterno (dopo un apt update, un curl, qualsiasi connessione uscente), Suricata non ha traccia del flow originale.
Senza ESTABLISHED,RELATED, tutte le connessioni TCP uscenti pendono nel vuoto - Ubuntu non riesce a scaricare aggiornamenti, fare curl, niente.
ESTABLISHED,RELATED risolve a livello di conntrack del kernel: i pacchetti che appartengono a una connessione già stabilita vengono accettati prima di NFQUEUE. Conntrack (connection tracking) è il modulo del kernel Linux che tiene traccia dello stato di ogni connessione TCP/UDP - sa che quel pacchetto in arrivo è la risposta a un SYN che abbiamo mandato noi, quindi va lasciato passare. Suricata continua a vedere i pacchetti NEW - dove si trovano i port scan, i SYN flood, le connessioni malevole.
Per il lab: curl dal Mac durante un SYN flood passa ancora per NFQUEUE (regola 3 accetta solo SSH dal Mac, non HTTP). Se la regola drop si attiva, anche il curl viene bloccato. Questo è intenzionale - è il test.
La persistenza: systemd service#
Le regole iptables non sopravvivono al reboot. iptables-persistent sembrerebbe la soluzione naturale, ma su Ubuntu rimuove UFW - entrambi gestiscono iptables e non convivono.
La soluzione: uno script idempotente + un systemd service.
# /usr/local/bin/ips-rules.sh
#!/bin/bash
IPT=/sbin/iptables
# Rimuovi regole esistenti (se il service viene riavviato, nessun duplicato)
$IPT -D INPUT -i lo -j ACCEPT 2>/dev/null || true
$IPT -D INPUT -i wazuh-br0 -j ACCEPT 2>/dev/null || true
$IPT -D INPUT -s 192.168.64.1 -p tcp --dport 22 -j ACCEPT 2>/dev/null || true
$IPT -D INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || true
$IPT -D INPUT -j NFQUEUE --queue-num 0 2>/dev/null || true
# Inserisci in ordine inverso: ognuno va in posizione 1
# L'ultimo inserito diventa il primo
$IPT -I INPUT 1 -j NFQUEUE --queue-num 0
$IPT -I INPUT 1 -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPT -I INPUT 1 -s 192.168.64.1 -p tcp --dport 22 -j ACCEPT
$IPT -I INPUT 1 -i wazuh-br0 -j ACCEPT
$IPT -I INPUT 1 -i lo -j ACCEPTL'inserimento inverso con -I INPUT 1 funziona così:
dopo insert NFQUEUE: [NFQUEUE, UFW...]
dopo insert ESTABLISHED: [ESTABLISHED, NFQUEUE, UFW...]
dopo insert SSH: [SSH, ESTABLISHED, NFQUEUE, UFW...]
dopo insert wazuh-br0: [wazuh-br0, SSH, ESTABLISHED, NFQUEUE, UFW...]
dopo insert lo: [lo, wazuh-br0, SSH, ESTABLISHED, NFQUEUE, UFW...]
↑
ordine finale corretto# /etc/systemd/system/ips-rules.service
[Unit]
Description=Suricata IPS iptables rules
After=network.target docker.service suricata.service
Requires=suricata.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ips-rules.sh
ExecStop=/sbin/iptables -D INPUT -j NFQUEUE --queue-num 0
RemainAfterExit=yes
[Install]
WantedBy=multi-user.targetRequires=suricata.service - il service parte solo se Suricata è attivo. Non ha senso inserire NFQUEUE se nessuno legge dalla coda.
ExecStop rimuove la regola NFQUEUE quando il service viene fermato. Se Suricata si spegne e si ferma il service, il traffico torna a fluire invece di restare bloccato in coda vuota.
active (exited) è lo stato corretto per un Type=oneshot con RemainAfterExit=yes - il processo ha terminato, le regole sono in piedi, il service è considerato running.
fail-open: no - il trade-off da conoscere per Security+#
fail-open: no (fail-closed)
Se Suricata si blocca:
→ NFQUEUE rimane attivo
→ la coda si riempie
→ il kernel droppa tutto
→ zero traffico, zero accesso SSH
→ massima sicurezza, zero disponibilità
fail-open: yes (fail-open)
Se Suricata si blocca:
→ il kernel bypassa NFQUEUE
→ tutto il traffico passa senza ispezione
→ sistema accessibile, IPS disabilitato
→ massima disponibilità, zero sicurezza FAIL-CLOSED FAIL-OPEN
(fail-open: no) (fail-open: yes)
Sicurezza ████████████ ░░░░░░░░░░░░
Disponibilità ░░░░░░░░░░░░ ████████████Non esiste la risposta giusta. Esiste il compromesso che si decide in modo consapevole in base al contesto. Un IPS su un segmento critico di rete bancaria sceglie fail-closed. Un IPS su un server di produzione accessibile solo in remoto potrebbe scegliere fail-open per evitare il lockout totale.
Per Security+: questo è il trade-off CIA - Confidentiality/Integrity vs Availability. Fail-closed privilegia le prime due, fail-open privilegia la terza.
Il risultato in Wazuh#
Con tutto in funzione:
# Da Kali
sudo nmap --min-rate 1000 -p 1-1000 192.168.64.3
# Su Ubuntu
tail -f /var/log/suricata/fast.log[Drop] [**] [1:9000002:1] IPS Aggressive Port Scan Drop
{TCP} 192.168.64.200:62851 -> 192.168.64.3:843
[Drop] [**] [1:9000002:1] IPS Aggressive Port Scan Drop
{TCP} 192.168.64.200:62851 -> 192.168.64.3:907
2.083 eventi. Tutti con action: blocked. La pipeline completa:
sequenceDiagram
actor Kali
participant NF as iptables NFQUEUE
participant Sur as Suricata
participant FL as fast.log
participant EVE as eve.json
participant WA as Wazuh Agent
participant WM as Wazuh Manager
participant WD as Dashboard
Kali->>NF: nmap --min-rate 1000
NF->>Sur: pacchetto in coda (trattenuto)
Sur->>Sur: detection_filter: 20+ SYN/sec
Sur-->>NF: NF_DROP
Sur->>FL: [Drop] IPS Aggressive Port Scan
Sur->>EVE: action:blocked (JSON)
EVE->>WA: logcollector legge il file
WA->>WM: evento inoltrato
WM->>WD: alert rule 86601
Note over WD: 2083 hits visibili

IDS vs IPS: il confronto finale#
┌──────────────┬─────────────────────────┬─────────────────────────┐
│ │ IDS │ IPS │
│ │ (af-packet mode) │ (nfqueue mode) │
├──────────────┼─────────────────────────┼─────────────────────────┤
│ Posizione │ fuori dal percorso │ inline (nel percorso) │
│ Traffico │ copia │ originale trattenuto │
│ Blocco │ NO │ SÌ │
│ Se si rompe │ traffico passa comunque │ fail-open/closed │
│ fast.log │ [**] │ [Drop] │
│ eve.json │ action: allowed │ action: blocked │
│ nmap output │ open / closed │ filtered │
│ Latenza │ zero aggiunta │ processing di Suricata │
└──────────────┴─────────────────────────┴─────────────────────────┘exit 0#
Il salto da IDS a IPS non è una questione di configurazione - è un cambio di filosofia.
In IDS, Suricata è un testimone. Vede tutto, registra tutto, non interferisce. La risposta all'attacco è umana, avviene dopo, spesso quando il danno è già fatto.
In IPS, Suricata è nel percorso. Il kernel trattiene ogni pacchetto e aspetta. La risposta è automatica e istantanea - il blocco avviene prima che il pacchetto raggiunga la porta del servizio.
Il prezzo è la complessità: l'interfaccia di rete giusta nel yaml. Il servizio systemd con il tipo corretto. La catena iptables nell'ordine giusto. Il Docker bridge accettato esplicitamente. Le connessioni stabilite esentate. La persistenza al reboot.
Ogni pezzo mancante rompe qualcosa in modo non ovvio.
Ma quando tutto è al posto giusto, [Drop] in fast.log è tra le cose più soddisfacenti che un lab possa produrre.
Comandi usati: suricata · iptables · nmap · hping3 · systemctl · docker Concetti correlati: IDS/IPS · NFQUEUE · Wazuh · iptables chains · TCP three-way handshake




