Suricata est un moteur de détection d’intrusion réseau : il examine les paquets qui entrent et sortent d’une machine, reconstitue les connexions, comprend les protocoles (HTTP, TLS, DNS, SSH…) et compare le tout à des dizaines de milliers de règles décrivant des attaques connues. En mode IPS, il peut aussi bloquer : un paquet qui correspond à une règle de blocage n’arrive jamais à destination.
Ce tutoriel installe Suricata en IPS sur un serveur Rocky Linux exposé à Internet, avec NFQUEUE. C’est le mode le plus utile sur un serveur seul, et aussi le plus délicat : Suricata se retrouve sur le chemin de chaque paquet. Je décris donc aussi, en détail, les pannes que j’ai rencontrées, dont une qui bloquait des connexions locales en silence pendant des semaines.

IDS ou IPS
| Mode | Fonctionnement | Risque |
|---|---|---|
| IDS (af-packet) | Suricata reçoit une copie des paquets et alerte | aucun pour le trafic : au pire, des alertes manquées |
| IPS (NFQUEUE) | le noyau confie chaque paquet à Suricata, qui l’accepte ou le jette | Suricata est sur le chemin : une erreur bloque le trafic |
Commencez en IDS si vous découvrez l’outil. Passez en IPS quand vous savez lire ses alertes et que vous avez éliminé les faux positifs les plus bruyants.
Étape 1 : installer
dnf install epel-release dnf install suricata suricata --build-info | grep -E 'NFQueue|AF_PACKET' # les deux doivent être « yes »
Deux modules noyau sont nécessaires pour NFQUEUE : nfnetlink_queue (la file elle-même) et xt_NFQUEUE (la cible utilisée par les règles de pare-feu). Le second est souvent oublié : la règle échoue alors avec un message peu clair sur un module manquant. Chargez-les et rendez-les permanents :
modprobe nfnetlink_queue modprobe xt_NFQUEUE printf 'nfnetlink_queue\nxt_NFQUEUE\n' > /etc/modules-load.d/nfqueue.conf
Étape 2 : suricata.yaml, l’essentiel
# /etc/suricata/suricata.yaml (extraits)
vars:
address-groups:
HOME_NET: "[203.0.113.25/32,2001:db8::25/128]"
EXTERNAL_NET: "!$HOME_NET"
nfq:
mode: accept
fail-open: yes
flow-timeouts:
tcp:
established: 7200
stream:
memcap: 256mb
midstream: true
reassembly:
memcap: 512mb
depth: 1mb
outputs:
- eve-log:
enabled: yes
filename: eve.json
types: [alert, drop, anomaly, stats]
- HOME_NET : les adresses que vous protégez. Beaucoup de règles en dépendent (« trafic venant de l’extérieur vers HOME_NET ») ; une valeur fausse donne à la fois des faux positifs et des attaques manquées ;
- nfq.mode: accept : Suricata rend son verdict (accepter ou jeter) et le paquet poursuit ensuite son chemin normal dans le pare-feu ;
- fail-open: yes : si la file déborde parce que Suricata ne suit pas, les paquets passent au lieu d’être jetés. Sur un serveur seul, mieux vaut laisser passer quelques paquets non inspectés que couper le service ;
- flow-timeouts.tcp.established: 7200 : le point qui m’a coûté le plus cher, détaillé plus bas. Par défaut, Suricata oublie une connexion TCP inactive au bout d’une heure ; une connexion qui reste ouverte plus longtemps sans trafic voit ses paquets suivants traités comme des paquets « hors connexion », et parfois jetés ;
- midstream: true : Suricata accepte de prendre en compte une connexion dont il n’a pas vu le début, par exemple après son propre redémarrage. Sans cela, toutes les connexions établies avant le démarrage sont considérées comme invalides ;
- memcap : les plafonds de mémoire pour le suivi des flux et la reconstitution des données. Trop bas, Suricata abandonne l’inspection des connexions sous charge ; surveillez les compteurs
memcapdans les statistiques ; - eve.json : le journal structuré, une ligne JSON par événement. C’est ce fichier qu’on envoie à un SIEM.
Étape 3 : les règles
suricata-update # télécharge le jeu ET Open (gratuit) suricata-update list-sources suricata-update enable-source et/open ls -l /var/lib/suricata/rules/suricata.rules
suricata-update assemble toutes les règles dans un seul fichier, en appliquant vos fichiers de modification. Deux d’entre eux sont essentiels en IPS :
# /etc/suricata/drop.conf : ces familles de règles bloquent au lieu d'alerter re:classtype:trojan-activity re:classtype:command-and-control re:classtype:domain-c2 re:classtype:exploit-kit re:classtype:coin-mining # /etc/suricata/disable.conf : protocoles industriels absents ici re:dnp3 re:modbus
Par défaut, toutes les règles alertent seulement. drop.conf transforme en blocages celles dont la catégorie ne laisse pas de doute : communication avec un serveur de commande, cheval de Troie, minage de cryptomonnaie. Évitez de tout passer en blocage : une règle générique de « trafic suspect » déclenchée par une application légitime coupe cette application sans prévenir. disable.conf retire les règles inutiles, ce qui économise mémoire et processeur.
Planifiez une mise à jour quotidienne par un timer systemd qui lance suricata-update, puis demande à Suricata de recharger ses règles sans redémarrer (suricatasc -c reload-rules).
Étape 4 : envoyer le trafic à Suricata
Le service lit la file 0 :
# /etc/systemd/system/suricata.service.d/ips.conf [Service] ExecStart= ExecStart=/usr/sbin/suricata -c /etc/suricata/suricata.yaml -q 0 --pidfile /var/run/suricata.pid
Puis les règles de pare-feu, avec firewalld en mode « direct » :
# 1. la boucle locale ne passe jamais par Suricata (priorité -1 : avant tout) firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT -1 -i lo -j ACCEPT firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT -1 -o lo -j ACCEPT # 2. tout le reste est confié à la file 0 ; --queue-bypass : si Suricata est arrêté, le trafic passe firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -j NFQUEUE --queue-num 0 --queue-bypass firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -j NFQUEUE --queue-num 0 --queue-bypass firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -j NFQUEUE --queue-num 0 --queue-bypass # mêmes règles en ipv6 firewall-cmd --reload
- –queue-bypass : sans lui, si Suricata est arrêté ou plante, plus aucun paquet ne passe, SSH compris. Avec lui, le noyau laisse passer le trafic quand personne n’écoute la file. C’est la ligne qui évite d’avoir à appeler la console de secours de l’hébergeur ;
- la boucle locale exclue : la leçon la plus coûteuse de ce tutoriel, détaillée ci-dessous ;
- l’ordre : les règles directes de priorité -1 passent avant celles de priorité 0.
La leçon de la boucle locale
Pendant six semaines, un service de mon serveur (un gestionnaire de tâches qui parle à Redis sur 127.0.0.1) s’est figé à intervalles irréguliers, toujours exactement 31 minutes et 40 secondes, avant de repartir. Les journaux de l’application ne montraient rien, ceux de Redis non plus. La cause : Suricata inspectait aussi le trafic de la boucle locale. Une longue connexion inactive dépassait le délai de suivi des flux ; au réveil, ses paquets n’appartenaient plus à aucune connexion connue de Suricata, qui les jetait ; il fallait attendre que TCP abandonne et se reconnecte.
Le diagnostic est venu d’eve.json : chaque paquet jeté y apparaît avec sa raison.
jq -c 'select(.event_type=="drop") | {timestamp, src_ip, dest_ip, dest_port, reason: .drop.reason}' /var/log/suricata/eve.json | tail
Trois corrections, toutes nécessaires : exclure la boucle locale (le trafic entre processus d’une même machine n’a rien à faire dans un IPS), allonger le délai TCP à deux heures, et activer midstream. Retenez le symptôme : une connexion longue qui se fige un temps fixe et reproductible, sans erreur nulle part, c’est presque toujours un équipement intermédiaire (pare-feu, IPS, traduction d’adresses) qui a oublié la connexion.
Le piège des cartes réseau
Les cartes et pilotes modernes regroupent plusieurs paquets en un seul « super-paquet » avant de les remettre au noyau (GRO, LRO). Suricata voit alors des paquets plus gros que la taille réelle du réseau, et sa reconstitution des connexions s’en trouve faussée. Désactivez ces optimisations sur l’interface surveillée, de façon permanente, par NetworkManager plutôt que par un simple ethtool -K qui serait annulé à la reconnexion suivante :
nmcli connection modify eth0 ethtool.feature-gro off ethtool.feature-lro off \
ethtool.feature-gso off ethtool.feature-tso off
nmcli connection up eth0
ethtool -k eth0 | grep -E 'generic-receive|large-receive'
Étape 5 : démarrer et vérifier
suricata -T -c /etc/suricata/suricata.yaml # test de configuration et des règles systemctl enable --now suricata tail -f /var/log/suricata/suricata.log # « engine started » curl -s http://testmynids.org/uid/index.html # doit déclencher l'alerte 2100498 jq -c 'select(.event_type=="alert") | .alert.signature' /var/log/suricata/eve.json | tail -3
Vérifiez aussi que le trafic normal passe toujours : connexion SSH neuve, site web, envoi de courrier, résolution DNS. Puis surveillez les statistiques pendant quelques jours :
jq -c 'select(.event_type=="stats") | .stats | {drop: .ips.blocked, memcap: .tcp.ssn_memcap_drop, kernel: .capture}' /var/log/suricata/eve.json | tail -1
Les mêmes compteurs sur un serveur en production
Suricata 8.0.8 en mode IPS sur mon serveur web, lu par le socket de contrôle le 2 novembre 2026 après 75 minutes de fonctionnement. suricatasc donne les compteurs en direct, sans attendre la prochaine ligne de statistiques dans eve.json :
# suricatasc -c iface-list
{"message": {"count": 1, "ifaces": ["NFQ#0"]}, "return": "OK"}
# suricatasc -c ruleset-stats
[{"id": 0, "rules_loaded": 53325, "rules_failed": 0, "rules_skipped": 0}]
# suricatasc -c dump-counters | jq '.message | {uptime, ips, alerts: .detect.alert}'
uptime 4544 s
ips.accepted 2 076 277
ips.blocked 771
drop_reason.stream_error 769
drop_reason.applayer_error 2
drop_reason.rules 0
alerts 23
- NFQ#0 : Suricata est branché sur la file netfilter 0, il voit donc les paquets au passage et peut les jeter. En mode IDS, cette liste montrerait une interface réseau.
- 53 325 règles chargées, 0 en échec : un rules_failed non nul signifie le plus souvent qu’une règle utilise un mot-clé que votre version ne connaît pas, après une mise à jour des règles ou du moteur.
- 771 paquets bloqués, 769 pour stream_error, 0 par les règles : c’est le tableau normal d’un serveur web. Les rejets par règle sont rares ; la plupart des paquets jetés n’ont aucun sens pour le flux TCP auquel ils prétendent appartenir (retransmissions après la fermeture, numéros de séquence faux, sommes de contrôle invalides). Si ce nombre grimpe d’un coup, regardez les événements
dropdans eve.json : la leçon de la boucle locale, plus haut, a commencé par exactement ce genre de pic. - 23 alertes en 75 minutes, aucune bloquante : des anomalies de protocole (trop de SYN pour une même poignée de main, protocoles applicatifs incohérents, horodatages invalides) journalisées pour le SIEM. Pourtant, 23 665 des règles chargées sont réglées pour bloquer, par classe (chevaux de Troie, serveurs de commande, kits d’exploitation, minage…) grâce à
drop.conf: pendant cette heure, aucune ne s’est déclenchée. Une règle de blocage qui ne se déclenche jamais, c’est l’état attendu, pas le signe d’une panne.
Faire taire un faux positif
Une règle qui se déclenche sur un trafic légitime se neutralise, du plus précis au plus large : seuil ou suppression pour une adresse dans threshold.config, puis désactivation de la règle par son numéro dans disable.conf. Ne modifiez jamais le fichier de règles directement : la prochaine mise à jour l’écraserait.
# /etc/suricata/threshold.config suppress gen_id 1, sig_id 2210045, track by_src, ip 203.0.113.40
Envoyer les alertes au SIEM
eve.json est lisible par Wazuh, Elastic, Graylog et la plupart des SIEM. Avec un agent Wazuh, quelques lignes suffisent (format json, chemin du fichier) et les alertes de Suricata apparaissent à côté des journaux système, ce qui permet de rapprocher une alerte réseau d’une connexion SSH ou d’un changement de fichier. Pensez à la rotation : eve.json grossit vite, et logrotate doit envoyer à Suricata le signal de rouvrir le fichier.
Mettre à jour Suricata
Après chaque mise à jour du paquet, relancez suricata -T avant le redémarrage : une option renommée ou une règle incompatible empêche le démarrage, et grâce à --queue-bypass le trafic passera alors sans inspection, sans que rien ne casse visiblement. C’est le revers de la sécurité de fonctionnement : surveillez l’état du service, pas seulement le trafic.
Ce billet fait partie d’une série de tutoriels d’administration système (tag « pas à pas »).
Série « Pas à pas », tutoriel 10/77. · ← Tutoriel précédent · Tutoriel suivant → (à paraître le 11/11) · English version