Suricata : Integration dans VyOS via conteneur Podman

Comment un conteneur Suricata s'intègre à un routeur VyOS : la déclaration dans la configuration, le marquage du trafic, la persistance, et le déploiement automatisé. L'architecture NFQUEUE et l'image durcie ont leur article ; la politique de règles a le sien.

Architecture

Le routeur porte le conteneur nativement, à partir d'une image durcie plutôt que d'une image générique. La configuration et les règles vivent dans le répertoire persistant du routeur, et le trafic est dérivé vers l'analyseur par le sous-système de filtrage du noyau.

flowchart LR
    A["Internet"] -->|"interface WAN"| B["Routeur"]
    B -->|"marquage"| C["Files NFQUEUE"]
    C --> D["Suricata en ligne<br/>dans son conteneur"]
    D -->|"verdict accepte ou rejete"| B
    B -->|"interface LAN"| E["Reseau local"]

Déclarer le conteneur

Le conteneur n'est pas lancé à la main : il est déclaré dans la configuration du routeur, ce qui le rend persistant et le place sous la supervision du système.

set container name suricata allow-host-networks
set container name suricata arguments '-q 0 -q 1 -q 2 -q 3 --runmode workers'
set container name suricata capability 'net-admin'
set container name suricata capability 'sys-nice'
set container name suricata image 'docker.io/jbsky/suricata-hardened:TAG'
set container name suricata memory '2048'
set container name suricata restart 'on-failure'
set container name suricata volume config destination '/etc/suricata'
set container name suricata volume config source '/config/containers/suricata/etc'
set container name suricata volume logs destination '/var/log/suricata'
set container name suricata volume logs source '/var/log/suricata'
set container name suricata volume rules destination '/var/lib/suricata/rules'
set container name suricata volume rules source '/config/containers/suricata/rules'
set container name suricata volume run destination '/var/run/suricata'
set container name suricata volume run source '/config/containers/suricata/run'

Trois points de cette déclaration ne vont pas de soi :

  • L'accès au réseau de l'hôte est obligatoire. Le mécanisme de files du noyau travaille dans l'espace de noms réseau de l'hôte ; un conteneur isolé ne verrait tout simplement pas les files à consommer.
  • Deux capabilities, pas plus. Elles sont déclarées ici parce que le conteneur en a besoin pour manipuler les files et ajuster sa priorité — le détail du choix est dans l'article sur l'image.
  • Le volume d'exécution expose le socket de contrôle. C'est lui qui permet de recharger les règles à chaud, sans redémarrer le conteneur.

Ce qui a changé depuis la première version

Le montage initial utilisait une image générique suivie en latest, six gigaoctets de mémoire, et aucune politique de redémarrage.

Le passage à l'image durcie a permis de descendre la mémoire à deux gigaoctets — elle est nettement plus légère — et surtout d'épingler un tag au lieu de suivre latest. La différence est moins la sécurité que la prévisibilité : une mise à jour d'image devient un choix explicite, pas l'effet de bord d'un téléchargement.

Organisation des fichiers

/config/
├── containers/
│   └── suricata/
│       ├── etc/    ← configuration
│       ├── rules/  ← regles de detection
│       └── run/    ← socket de controle
└── scripts/
    └── suricata-update.sh

Tout vit sous le répertoire persistant du routeur : c'est ce qui garantit que la configuration survit à une mise à jour du système.

Dériver le trafic vers l'analyseur

# Marquer le trafic entrant depuis le WAN
iptables -t mangle -A FORWARD -i eth1 -j MARK --set-mark 10

# Marquer le trafic sortant vers le WAN
iptables -t mangle -A FORWARD -o eth1 -j MARK --set-mark 10

# Deriver les paquets marques vers l analyseur
iptables -t mangle -A POSTROUTING -m mark --mark 10 -j NFQUEUE \
  --queue-balance 0:3 \
  --queue-cpu-fanout \
  --queue-bypass

La première version marquait tout le trafic routé, sans distinction d'interface. Ça fonctionnait, et ça faisait inspecter du trafic entre VLAN internes qui n'avait aucune raison de l'être. Le marquage est désormais restreint à l'interface externe dans les deux sens : seul ce qui traverse réellement la frontière est analysé.

Deux options méritent d'être comprises plutôt que recopiées :

  • La répartition sur plusieurs files distribue la charge sur autant de fils d'exécution, avec une affinité processeur.
  • Le contournement automatique laisse passer le trafic si l'analyseur n'est pas là. C'est le filet de sécurité qui rend acceptable de redémarrer le conteneur en pleine journée : sans lui, l'arrêt de l'analyseur couperait le réseau.

En surface la syntaxe est celle de l'ancien outil de filtrage ; le routeur la traduit en règles du sous-système moderne, qui est ce qui tourne réellement.

Analyser le trafic routé, pas le trafic local

Un paramètre de la configuration indique que l'analyseur doit traiter le trafic qui traverse le routeur, et non celui que le routeur émet ou reçoit pour son propre compte :

host-mode: router

C'est un cas où le défaut ne convient pas : sans ce réglage, l'analyseur regarde au mauvais endroit et ne voit presque rien passer.

Mise à jour des règles

Une tâche planifiée du routeur déclenche la mise à jour quotidienne :

set system task-scheduler task update-suricata-rules executable path '/config/scripts/suricata-update.sh'
set system task-scheduler task update-suricata-rules interval '1d'

Le mécanisme du script — conteneur éphémère, contournement des capabilities posées sur le binaire, et le piège de la version mal détectée — est détaillé dans la stratégie de détection.

Déploiement

La configuration, les règles et le script sont versionnés et déployés par un playbook plutôt que synchronisés à la main. Il cible un groupe d'inventaire et non un nom d'hôte, ce qui le rend réutilisable tel quel :

ansible-playbook suricata-deploy.yaml -i inventories/production --tags config
ansible-playbook suricata-deploy.yaml -i inventories/production --tags script
ansible-playbook suricata-deploy.yaml -i inventories/production --tags check

L'étape de vérification finale n'est pas décorative : elle interroge l'état réel du conteneur après coup, au lieu de supposer que le déploiement a fait ce qu'il annonçait.

Points à surveiller

Les journaux grossissent vite. Une rotation est indispensable, avec un plafond de taille en plus de la périodicité — un pic d'activité remplit un disque plus vite qu'un cycle quotidien ne le vide :

/var/log/suricata/*.log /var/log/suricata/*.json
{
    daily
    maxsize 100M
    rotate 10
    missingok
    nocompress
    copytruncate
}

Le débit reste le même. Mesuré, il n'y a pas de différence notable entre les modes d'acquisition sur ce matériel — le plafond observé tient au lien, pas à l'analyse. Le détail de la comparaison est dans l'article sur l'image.


Liens

Articles connexes