Mini-Usine IoT Sécurisée
Simulation entièrement conteneurisée d'une mini-usine IoT industrielle sécurisée — 6 devices streament de la télémétrie en MQTT mutual-TLS vers un stack Grafana de monitoring temps réel.
Les déploiements IoT industriels vivent ou meurent sur trois choses : communication sécurisée device-to-broker, ingestion time-series fiable à grande échelle, et observabilité temps réel avec alerting. La plupart des tutoriels bâclent la sécurité ou ajoutent le monitoring comme après-coup.
Construire un stack IIoT de référence end-to-end et production-shaped où chaque message device-to-broker est chiffré et mutuellement authentifié (mTLS), les données hétérogènes de capteurs sont normalisées dans un seul store time-series, et les opérateurs ont un dashboard Grafana live auto-provisionné avec alerting par seuils.
Tech Stack
Décisions clés
- →Bridge Python personnalisé plutôt que Node-RED — un subscriber/writer de ~90 lignes a moins de parties mobiles, un container plus petit, et un contrôle programmatique complet sur le filtrage de champs et la construction line-protocol.
- →Design two-listener du broker — mTLS sur 8883 pour les devices, plaintext interne sur 1883 pour les services backend dans le réseau Docker, évitant la redondance TLS sur le fabric interne de confiance.
- →Sécurité d'abord, sécurité forcée — TLS 1.2 obligatoire avec vérification mutuelle des certificats ; une suite de 5 tests de sécurité automatisée prouve que les connexions plaintext et bad-cert sont refusées.
- →Provisioning infrastructure-as-code — la datasource et le dashboard Grafana sont provisionnés depuis YAML/JSON au démarrage, aucune configuration UI manuelle.
- →Durcissement des containers — le simulateur tourne en utilisateur non-root (uid 1000), les clés privées sont chmod 600, certs/ est gitignored.
Mutual TLS pour chaque device
Le port externe du broker (8883) requiert TLS 1.2 avec vérification mutuelle des certificats (require_certificate true). Une suite de 5 tests de sécurité automatisée valide que les connexions plaintext sont refusées, les connexions bad-cert sont refusées, le mTLS valide réussit, TLS 1.2 est forcé, et la vérification stricte des certificats est active.
listener 8883
certfile /certs/server.crt
keyfile /certs/server.key
cafile /certs/ca.crt
require_certificate true
tls_version tlsv1.2Bridge Python : MQTT vers InfluxDB
Un service Python de ~90 lignes souscrit à iot/plant/#, parse les payloads JSON, et écrit les points InfluxDB line-protocol via HTTP. Surface d'attaque plus petite que Node-RED, moins de containers, entièrement scriptable.
def on_message(client, userdata, msg):
payload = json.loads(msg.payload.decode())
device_type = msg.topic.split('/')[2]
fields = {k: v for k, v in payload.items() if isinstance(v, (int, float))}
line = f"{device_type},device_id={payload['device_id']} " + \
",".join(f"{k}={v}" for k, v in fields.items())
requests.post(f"{INFLUX_URL}/api/v2/write?bucket=iot-data&precision=s",
data=line, headers={"Authorization": f"Token {INFLUX_TOKEN}"})6 devices simulés avec injection d'anomalies
Chaque simulateur est un processus Python conteneurisé émettant une télémétrie réaliste : 2 machines de production (5% de panne aléatoire), capteur de température (2% de spike, alerte à 80°C), capteur de pression (3% de surpression, alerte à 5 bar), capteur de vibration (4% d'anomalie sur un axe aléatoire), et un compteur d'énergie. Schéma de topics MQTT : iot/plant/{device_type}/{device_id}.
Fonctionnalités démo
- →Démarrage en une seule commande : docker-compose up -d lance tout le stack de 8 services
- →Dashboard Grafana live sur localhost:3001 avec 8 panneaux auto-provisionnés
- →Injection d'anomalies temps réel visible comme blips sur le dashboard en quelques secondes
- →Suite de tests de sécurité TLS automatisée avec 100% de réussite
- →Horizontalement scalable — ajouter un device = dupliquer un bloc de service compose
- →Node-RED a été remplacé par un bridge Python de ~90 lignes — les outils de glue prêts à l'emploi ajoutent du poids opérationnel ; un service purpose-built fin est plus clair pour un pipeline fixe.
- →Les SAN des certificats doivent couvrir à la fois l'alias DNS Docker et localhost pour la validation depuis l'intérieur et l'extérieur du réseau.
- →depends_on attend uniquement le démarrage du container, pas la readiness du service — toujours coder les clients MQTT pour retry avec backoff.
- →Intégrer le least-privilege dans l'image Docker (utilisateur non-root, clés chmod 600), pas au runtime.