NOPE LinkedIn

Catégories:
IA

Victor : anonymiseur de logs de sécurité souverain et auto-apprenant

Victor : anonymiseur de logs de sécurité souverain et auto-apprenant image

Rubrique: IA Tag: Anonymisation Tag: NER Tag: spaCy Tag: Logs Tag: Cybersécurité Tag: RGPD Tag: SLM Tag: Ollama Tag: Open Source Tag: AGPL

Avant d’envoyer des logs à un éditeur, de les injecter dans un LLM externe ou de les archiver conformément au RGPD, une question se pose inévitablement : ces fichiers contiennent-ils des informations qui exposent mon infrastructure ?

Les logs de sécurité sont denses en données sensibles — adresses IP internes, noms d’hôtes, identifiants de comptes de service, clés API, adresses MAC. Et contrairement aux bases de données ou aux formulaires, leur format n’est jamais tout à fait uniforme : chaque équipement, chaque version de daemon, chaque intégration produit ses propres variantes. Un Fortigate ne logue pas comme CrowdSec. Un OPNsense ne logue pas comme un agent Wireguard.

Le réflexe naturel est d’écrire des expressions régulières. Et ça fonctionne — jusqu’à un certain point. Les IPs RFC1918 ? Une regex couvre ça facilement. Mais fw01-dmz, svc_backup_agent, crowdsec-bouncer-api ? Ces entités n’ont pas de forme canonique. Leur structure dépend des conventions de nommage de l’organisation, et elles changent au fil des déploiements. Maintenir un catalogue de regex pour tout couvrir devient vite un travail à part entière — et on n’en voit jamais vraiment le bout.

Il manquait un outil capable de comprendre le contexte, de détecter des entités qu’on n’avait pas anticipées, et de s’améliorer au fil des logs traités. C’est pour ça que Victor existe.


Ce que fait Victor

Victor) détecte et remplace les entités sensibles par des tokens pseudonymisés cohérents sur toute une session :

Block IP 192.168.1.50 on fw01.company.local — CVE-2024-12345
→ Block IP {{IP_PRIVE}} on {{HOSTNAME}} — {{CVE_ID}}

La même entité reçoit toujours le même token. Si fw01.company.local apparaît dans dix fichiers de logs différents d’un même batch, il sera {{HOSTNAME}} partout — ce qui préserve la corrélation sans exposer l’information.


Architecture : deux couches complémentaires

Victor combine deux mécanismes de détection :

texte brut
  └─► CustomRulesProcessor    (regex : IPs RFC1918, CVE, MAC, hostnames…)
        └─► NERProcessor       (spaCy : modèle AnonyNER — entités cyber)
              └─► ReplacementGenerator  (tokens cohérents : HOST_001, IP_001…)
                    └─► texte anonymisé + mapping + rapport

Les règles regex couvrent les catégories les plus fréquentes dans les logs cyber : IPs RFC1918, identifiants CVE, FQDNs de zones privées (.local, .corp, .lan), hostnames numérotés (fw01, srv-003), adresses MAC, tokens hex 32–64 caractères.

AnonyNER est un modèle spaCy entraîné spécifiquement sur des entités cybersécurité. Là où un modèle NLP généraliste voit une suite de mots ordinaires, AnonyNER reconnaît crowdsec-bouncer-http, wireguard_peer_pat, ou svc_backup_agent pour ce qu’ils sont : des identifiants internes qui n’ont rien à faire dans un fichier partagé.

Les deux couches se complètent : les règles regex gèrent les patterns prévisibles avec certitude, AnonyNER prend en charge tout ce qui relève du contexte et de la sémantique.


Traitement par batch de fichiers

Victor traite des répertoires entiers de logs en une seule passe. Tous les fichiers d’un batch partagent la même session d’anonymisation — la cohérence des tokens est garantie entre firewall.log, ids.log et crowdsec.log.

from pathlib import Path
from victor import LogProcessor

processor = LogProcessor(
    inbox_dir  = Path("logs/inbox"),
    outbox_dir = Path("logs/outbox"),
)
report = processor.process_batch()

Les fichiers traités sont routés automatiquement :

Répertoire Condition
clean/ 0 gap résiduel — anonymisation complète
partial/ Gaps résiduels — revue recommandée
error/ Échec de traitement

Un batch_mapping.json enregistre le mapping global {valeur_originale: token} pour l’ensemble du batch — utile pour la traçabilité ou la désanonymisation ultérieure.


La boucle d’auto-apprentissage

C’est la partie la plus intéressante. Victor ne se contente pas d’anonymiser — il observe ses propres lacunes.

Un gap NER est une entité détectée par AnonyNER mais non prise en charge par le moteur d’anonymisation. C’est un signal : le modèle voit quelque chose que les règles ne couvrent pas.

anonymize_*()
  └─► ner_gaps (entités non anonymisées)
        └─► GapCollector.record()
              └─► fréquence N / M sessions
                    └─► candidates()
                          ├─► to_regex_rule()      → RuleWriter    → custom_rules.json
                          └─► to_spacy_examples()  → AnnotationWriter → train.spacy

Le GapCollector agrège ces entités manquantes à travers les sessions, les score par fréquence et propose deux types d’actions :

  • Piste courte : générer une règle regex et l’ajouter à custom_rules.json — effet immédiat
  • Piste longue : générer un exemple d’entraînement spaCy et l’ajouter au dataset — amélioration du modèle

La validation humaine est l’unique verrou avant toute écriture. Le GapCollector ne modifie rien de lui-même.


Validation automatique par SLM local

Pour les environnements qui génèrent beaucoup de gaps, Victor peut déléguer la validation à un SLM local via Ollama. Aucun GPU requis — qwen2.5:1.5b (~1 Go) tourne sur CPU.

from victor import GapValidator

validator = GapValidator()   # Ollama localhost:11434, qwen2.5:1.5b
results = validator.validate_candidates(collector)
# ACCEPT → règle ou exemple d'entraînement
# REJECT → blacklisté
# unsure → reste en attente de validation humaine

Si Ollama n’est pas disponible, le validateur retourne decision="unsure" sans exception — la validation humaine reste toujours possible indépendamment.

Le choix de qwen2.5:1.5b est délibéré : la tâche est une classification binaire (cet élément est-il une entité sensible de type X ?), pas un raisonnement complexe. Un modèle léger sur CPU est suffisant et évite toute dépendance GPU.


Un projet souverain

Victor est conçu pour fonctionner sans aucune dépendance externe au-delà de spaCy. Pas d’appel API, pas de modèle cloud, pas de télémétrie. Les logs restent sur votre infrastructure.

Le projet est publié sous AGPL-3.0 — toute version modifiée, y compris utilisée comme service réseau, doit redistribuer son code source sous la même licence. Une licence commerciale dérogatoire est disponible pour les organisations dont les politiques internes sont incompatibles avec cette obligation (à l’exception du secteur de la défense et de l’armement).

Le code source est disponible sur GitHub.


Résultats sur logs réels

Premier test sur un jeu de logs public issu du projet LogHub : un fichier Linux syslog de 211 Ko (Linux_2k.log, 2 000 lignes).

Fichier Taille Remplacements Tokens générés Statut Gaps résiduels
Linux_2k.log 211 Ko 3 287 1 676 partial 1

Le moteur a effectué 3 287 remplacements sur un seul fichier, produisant 1 676 tokens distincts. Un seul gap résiduel : AnonyNER classifie selinux_register_security: comme MAC_ADDRESS — faux positif sans impact réel.

Ce que révèle le mapping

L’examen du batch_mapping.json illustre les limites actuelles du modèle sur ce format :

Entrée capturée Token Verdict
sshd(pam_unix)[19937]: {{IFACE_001}} ❌ Process+PID → Interface
14:53:32 {{IP_001}} ❌ Timestamp → IP
12:13:20 {{FW_RULE_002}} ❌ Timestamp → Firewall Rule
]: {{MAC_001}} ❌ Ponctuation → MAC
uid=0 {{PORT_001}} ❌ UID root → Port
rhost=220-135-151-1.hinet-ip.hinet.net {{HOST_001}} ⚠️ Correct, préfixe rhost= inclus

AnonyNER v3 a été entraîné sur des logs réseau et firewall. Les Linux syslogs ont un format structuré différent — process(subsystem)[pid]: — que le modèle ne reconnaît pas encore.

Ce résultat est documenté, pas masqué. Il justifie précisément l’existence de la boucle d’auto-apprentissage et du pipeline d’entraînement.


Entraîner ou ré-entraîner AnonyNER

Victor embarque un répertoire training/ avec les scripts et données pour ré-entraîner ou enrichir le modèle AnonyNER directement à partir des gaps de production.

# 1. Compiler le corpus JSONL → format binaire spaCy
python training/scripts/prepare_spacy_dataset.py \
    --input training/data/anonyner_train.jsonl \
    --train-out training/data/train.spacy \
    --dev-out training/data/dev.spacy

# 2. Entraîner (CPU, pas de GPU requis)
python training/scripts/train_anonyner.py
# → training/models/anonyner_model/model-best

La boucle naturelle : gaps de production → exemples annotés → ré-entraînement.

from victor import GapCollector, AnnotationWriter
from pathlib import Path

collector = GapCollector(data_dir=Path("data"))
ann_writer = AnnotationWriter(data_dir=Path("data/dataset"))

for gap in collector.candidates(min_occurrences=3):
    examples = collector.to_spacy_examples(gap["text"], gap["label"])
    ann_writer.add_examples(examples, label=gap["label"],
                            source_key=f"gap::{gap['label']}")
    collector.accept(gap["text"], gap["label"])
# → data/dataset/annotations.json → training/data/ → ré-entraînement

Chaque batch de logs traités est une opportunité d’améliorer le modèle pour les suivants.


Limitations connues

Tokens statiques pour les custom rules — plusieurs IPs distinctes capturées par la même règle regex reçoivent le même token ({{IP_PRIVE}}). La numérotation séquentielle ({{IP_001}}, {{IP_002}}) ne s’applique qu’aux entités détectées par AnonyNER.

Modèle de fallback — sans AnonyNER, le modèle générique en_core_web_md ne détecte pas les entités cyber. Les règles regex fonctionnent dans les deux cas.

Spécialisation firewall/réseau — AnonyNER v3 a été entraîné principalement sur des logs OPNsense, CrowdSec et WireGuard. Les logs applicatifs Linux (syslog, journald) ou Windows (Event Log) nécessitent un enrichissement du corpus avant d’obtenir des résultats fiables.


Victor est un outil de terrain. Il part du constat que les logs de production sont hétérogènes, que les conventions de nommage varient d’une infra à l’autre, et qu’aucun catalogue de regex ne sera jamais exhaustif. La boucle gaps → règles → dataset → entraînement est là pour ça : chaque corpus traité affine la détection, et ce qui était un angle mort aujourd’hui devient une règle ou un exemple d’entraînement demain.