5. Capteurs¶
Un capteur écoute un port miroir (SPAN) ou un TAP, produit des logs Zeek et
les envoie au central. Deux formes : l'image Docker (sensor/) sur une
machine existante, ou l'appliance (appliance/), une ISO Debian 12
dédiée, durcie, qui embarque la même image.
Image d'analyse (sensor/)¶
- Zeek avec les parsers industriels ICSNPP de la CISA (EtherNet/IP-CIP, S7comm, Modbus, DNP3, BACnet, OPC UA, FINS, HART-IP, GE-SRTP, PROFINET). JA4+ (FoxIO) n'est pas embarqué : sa licence interdit l'usage monétisé sans licence OEM.
entrypoint.sh:read <pcap…>(analyse de captures, réseau coupé),live <interface>(écoute continue),cle(clé d'envoi, créée au premier appel),envoyer(un envoi),version(versions de Zeek et des paquets).- En écoute continue : rotation horaire des logs, purge après 48 h
(
AUSPEX_SENSOR_RETENTION_HOURS), envoi toutes les minutes (AUSPEX_ENVOI_SECONDES). - Seules capacités nécessaires :
NET_RAWetNET_ADMIN(capture et réglage de l'interface) ; aucun port ouvert.
Envoi des logs au central¶
Côté capteur : la clé d'hôte du central est relevée puis comparée à
l'empreinte épinglée (CENTRAL_EMPREINTE, donnée par auspex capteur
empreinte) avant tout envoi ; un faux central ne reçoit rien.
Côté central (deploy/reception/, reception.py) :
sshdsans privilèges (UID du service), système en lecture seule, clés Ed25519 uniquement, pas de mot de passe, pas de shell, pas de redirection (optionrestrict, etDisableForwarding,PermitTTY nodanssshd_config) ; échange de cléssntrup761x25519(hybride post-quantique) oucurve25519, chiffrement ChaCha20-Poly1305 ou AES-256-GCM ; 3 tentatives et une session par connexion au plus ;authorized_keysgénéré par Auspex depuissensors[].keydeauspex.yamlà chaque cycle : retirer un capteur révoque sa clé ;from="…"reprendsensors[].allow;- commande imposée
auspex-recevoir NOM: écriture seule, sans suppression, liens symboliques neutralisés (rrsync -wo -no-del -munge), dans le seul dossierlogs/NOM/; - purge des logs reçus après 30 jours (
AUSPEX_LOGS_RETENTION_DAYS).
auspex capteur liste : capteurs déclarés, autorisés, dernier dépôt, version
d'analyse et résultat de la dernière mise à jour.
Appliance (ISO)¶
Construite par appliance/build.sh (live-build, Debian 12) à partir de
l'image Docker testée en CI (tools/appliance-rootfs.sh en extrait la
racine, sans les outils de compilation). Produit
auspex-capteur-<version>.iso et auspex-capteur-<version>.squashfs
(image de mise à jour).
Séquence de démarrage¶
| Ordre | Unité | Rôle |
|---|---|---|
| 1 | auspex-silence |
avant tout réseau : netdev egress drop sur toutes les cartes physiques |
| 2 | auspex-donnees |
disque AUSPEX-DATA monté ; un disque entièrement vide (≥ 256 Mio, lu en entier) est formaté, jamais un autre |
| 3 | auspex-config |
import depuis le support AUSPEX-CONF (capteur.conf, clé cle/cle.pub copiée en 600), clé créée sinon ; configuration validée ; interfaces résolues |
| 4 | auspex-parefeu |
nftables : sorties de l'administration limitées au central, DNS, NTP, DHCP ; écoute muette |
| 5 | auspex-reseau |
fichiers systemd-networkd écrits avant que networkd démarre ; écoute sans adresse |
| 6 | auspex-racine |
image d'analyse choisie, vérifiée, montée en lecture seule (voir mises à jour) |
| 7 | auspex-capteur |
Zeek sur /run/auspex/racine |
| 8 | auspex-maj |
mises à jour signées |
| — | auspex-console |
écran d'état et menu de configuration (tty1) |
Durcissement¶
- Système en lecture seule (image live) ; seuls la clé, la configuration, les logs récents et les emplacements de mise à jour sont sur le disque de données.
- Interface d'écoute muette : pas d'adresse, pas d'ARP, pas d'IPv6
(
ipv6.disable=1), et un filtreegressqui jette toute trame sortante.ifupdownetdhclientsont purgés (live-boot les lançait sur toutes les interfaces) ; seul systemd-networkd, piloté parauspex-reseau, configure le réseau, et un fichier par défautUnmanaged=yescouvre toute interface inconnue. - Aucun compte : pas de serveur SSH,
gettymasqués (console et série), root verrouillé, shellnologin, pas de paquetlive-config(compte « user » connu) ; aucun dépôt APT. - Analyse confinée :
auspex-capteurtourne avecRootDirectory(racine en lecture seule), seules capacitésCAP_NET_RAWetCAP_NET_ADMIN,NoNewPrivileges,Protect*, familles d'adresses limitées, appels système natifs seulement. capteur.confjamais exécuté : lu ligne à ligne, chaque clé validée par une expression régulière (lib.sh,valid) ; une valeur ne peut pas injecter de commande.
capteur.conf¶
Voir référence.
Mises à jour signées de l'analyse (A/B)¶
L'analyse (Zeek, ICSNPP, scripts) évolue plus souvent que le système : elle se met à jour par le réseau. Le système (noyau, pare-feu, scripts de démarrage) se met à jour en démarrant une nouvelle ISO.
Publication (central, maj.py)¶
auspex capteur cle-maj: crée au besoin la clé Ed25519 de mises à jour (data/state/cles/maj-capteurs-ed25519.pem, 600, distincte de la clé des audits) et affiche la ligneMAJ_CLE=(SubjectPublicKeyInfo DER en base64, 60 caractères).auspex capteur publier auspex-capteur-<v>.squashfs: vérifie la version (chiffres et points, plus récente que la publication en cours sauf--force), la taille (≤ 2 Gio) et la signature de format squashfs (hsqs) ; copie l'image, calcule l'empreinte de la copie, écrit et signe le manifeste ; bascule atomiquecourant/← nouvelle publication, l'ancienne devientprecedent/.
Manifeste (data/mises-a-jour/courant/manifeste, texte, signé tel quel ;
signature brute de 64 octets dans manifeste.sig) :
format=auspex-maj-capteur/1
version=0.2.0
sha256=<64 hexadécimaux>
taille=<octets>
publie_le=2026-11-01T12:00:00Z
Distribution¶
La réception sert data/mises-a-jour/ (monté en lecture seule) : quand la
commande rsync demandée est une lecture (rsync --server --sender),
auspex-recevoir exécute rrsync -ro /mises-a-jour ; tout le reste reste
un dépôt de logs en écriture seule. Même clé SSH, même port, même
épinglage : aucun nouveau flux à ouvrir dans les pare-feux.
Installation sur le capteur (auspex-maj, maj.sh)¶
- Vérification : manifeste ≤ 4 Kio, signature de 64 octets, vérifiée par
openssl pkeyutl -verify -rawinavecMAJ_CLE; chaque ligne du manifeste validée par expression régulière (rien n'est exécuté), format, version, empreinte et taille contrôlés. - Pas de retour en arrière imposé : seule une version strictement
plus récente (
sort -V) que la version active est installée. - Téléchargement dans
maj/telechargement/; l'image active y est d'abord copiée comme base, rsync ne transfère que les différences ;--partialpermet la reprise ; place libre vérifiée (taille + 10 % + 50 Mio). - Deux emplacements
maj/aetmaj/b+ l'image d'usine (ISO) : l'installation se fait toujours dans l'emplacement inactif. - État (
maj/etat, lu clé par clé) :actif,precedent,essai,refusees(20 dernières versions écartées). - Essai : l'analyse doit tourner
MAJ_ESSAIsecondes (300 par défaut) sans aucun redémarrage (NRestarts= 0) et être active à la fin. Sinon : version écartée, retour à l'emplacement précédent. Un capteur qui redémarre pendant l'essai (essai=1au démarrage) abandonne aussi la version. - À chaque démarrage (
auspex-racine), l'emplacement actif est revérifié (signature et empreinte complète) et doit être plus récent que l'ISO ; sinon l'image d'usine est utilisée. Montagesquashfsro,nodev,nosuidsur/run/auspex/racine; seules les variablesPATH,ZEEK*,LANGde l'image sont reprises. - État visible du central :
auspex-etat.json(version d'analyse, emplacement, version du système, dernier événement) envoyé avec les logs, affiché parauspex capteur liste. - Sans
MAJ_CLEdanscapteur.conf, les mises à jour sont désactivées.
Limites¶
- La clé publique de confiance est lue dans
capteur.conf, sur le disque de données : qui a un accès physique au disque peut remplacer la clé et l'image (il pourrait de toute façon modifier la configuration). - La signature est celle de l'organisation (clé du central) : elle prouve que l'image a été publiée par votre central, pas qu'elle provient du projet. Vérifier l'empreinte publiée avec chaque version avant de publier.