hors suivi
Leçon

Anatomie d'un système IoT

Jour 1 · Comprendre et faire parler les objets · 25 min

Avant de brancher quoi que ce soit, il faut savoir où l'on met les mains. Un système IoT, quelle que soit sa taille, s'organise toujours en quatre couches. Un capteur de température dans un bureau ou un réseau de mille sondes industrielles : même découpage.

Perception capteurs, actionneurs température, humidité Réseau MQTT, CoAP, LPWAN transport des messages Traitement broker, base, règles stockage et alertes Application tableau de bord supervision, pilotage Le module M1 porte sur la couche réseau — celle qui fait circuler la donnée
Les quatre couches d'une architecture IoT, du capteur au tableau de bord.

Ce que fait chaque couche

CoucheRôleExemples
PerceptionMesurer le monde physique, ou agir dessusSonde de température, détecteur d'ouverture, relais de chauffage
RéseauAcheminer la donnée du capteur vers le systèmeMQTT, CoAP, LoRaWAN, Wi-Fi, 4G
TraitementRecevoir, stocker, déclencher des règlesBroker MQTT, base time-series, moteur d'alertes
ApplicationRendre la donnée utile à quelqu'unTableau de bord, alerte SMS, rapport mensuel
Ce module porte sur la couche réseau

C'est celle qui décide de tout le reste : mal choisie, elle vide les batteries, sature le serveur et laisse passer les pannes sans les signaler. Les autres couches viendront aux modules M2 à M5.

Le cas qui va nous servir de fil rouge

Thermeo supervise le chauffage de bâtiments tertiaires. Une centaine de capteurs de température et d'humidité, répartis sur plusieurs sites. Aujourd'hui, chaque capteur interroge un serveur HTTP toutes les trente secondes.

Trois problèmes en découlent, et ils sont typiques :

  • Les batteries se vident. Ouvrir une connexion HTTP coûte cher en énergie, et le capteur le fait 2 880 fois par jour.
  • Le serveur travaille pour rien. La température d'un bureau ne change pas toutes les trente secondes ; l'essentiel des requêtes ne rapporte aucune information nouvelle.
  • Une panne passe inaperçue. Si un capteur cesse d'émettre, personne ne le sait — le serveur attend simplement une requête qui n'arrive jamais.

Ces trois problèmes ont la même origine : le capteur demande au lieu de signaler. C'est ce que la leçon suivante va renverser.

✓ terminé
Leçon

Pourquoi MQTT plutôt que HTTP

Jour 1 · Comprendre et faire parler les objets · 25 min

HTTP fonctionne sur un principe simple : un client demande, un serveur répond. C'est parfait pour consulter une page web. Pour un capteur sur batterie, c'est un contresens.

Le renversement : publication et abonnement

MQTT inverse la logique. Le capteur ne demande rien : il publie quand il a quelque chose à dire. Les applications intéressées s'abonnent. Entre les deux, un broker fait le tri et la distribution.

Broker Mosquitto capteur 01 publie 21.4 capteur 07 publie 22.9 superviseur abonné thermeo/# alerte gel abonné .../temperature publish subscribe
Les capteurs publient, les applications s'abonnent. Aucun des deux ne connaît l'autre : c'est le broker qui les relie.

Le point important, et il est structurant : le capteur ne sait pas qui l'écoute, et l'application ne sait pas qui publie. On appelle cela le découplage. Ajouter un second tableau de bord ne demande aucune modification côté capteurs — on branche un abonné de plus.

Ce que cela change concrètement

HTTP (situation actuelle)MQTT (cible)
Qui déclencheLe capteur interroge en boucleLe capteur émet quand il a une valeur
ConnexionOuverte et fermée à chaque appelUne seule, maintenue et légère
En-tête d'un messagePlusieurs centaines d'octets2 octets au minimum
Ajouter un consommateurModifier le serveurS'abonner, rien d'autre
Capteur en panneSilence indistinct d'une absence de mesureDétecté automatiquement
L'ordre de grandeur à retenir

Un en-tête MQTT tient en 2 octets là où un en-tête HTTP en consomme plusieurs centaines. Sur un capteur qui émet toutes les dix minutes pendant cinq ans, cet écart décide de l'autonomie de la pile.

Et CoAP ?

Il existe un troisième larron, CoAP, qui transpose le modèle REST sur UDP pour des équipements encore plus contraints. Nous le manipulerons au chapitre 3. Retenez pour l'instant que MQTT maintient une connexion vers un broker, là où CoAP interroge une ressource sans intermédiaire.

Ce que MQTT ne fait pas

MQTT transporte des messages, rien de plus. Il ne les stocke pas durablement, ne les met pas en base et n'impose aucun format de contenu — la charge utile est une suite d'octets, à vous de décider si c'est du JSON, du texte ou du binaire.

✓ terminé
Leçon

Topics : nommer pour retrouver

Jour 1 · Comprendre et faire parler les objets · 20 min

Un topic est l'adresse d'un message. Le publieur y dépose, l'abonné y puise. C'est une simple chaîne de caractères découpée par des barres obliques, du plus général au plus précis :

thermeo/lyon/etage1/capt-01/temperature
└──┬───┘ └─┬─┘ └──┬──┘ └──┬──┘ └────┬────┘
  parc    site   étage  capteur   grandeur

Rien n'impose cette structure : le broker accepte n'importe quel nom. Mais une convention cohérente est ce qui rendra le système exploitable dans six mois, quand il y aura cent capteurs et trois personnes différentes pour les administrer.

Les deux jokers

JokerPortéeExempleCe qu'il capte
+Exactement un niveauthermeo/lyon/+/+/temperatureToutes les températures de Lyon, quel que soit l'étage et le capteur
#Tout ce qui suitthermeo/lyon/#Absolument tout ce qui concerne le site de Lyon
Le piège classique

thermeo/lyon/+/+/temperature ne captera jamais thermeo/paris/etage1/capt-12/temperature : le deuxième niveau est figé sur lyon. Le joker + remplace un niveau, il n'en saute aucun.

Autre règle : # ne peut apparaître qu'en dernière position. thermeo/#/temperature est invalide.

Bien concevoir sa convention

Trois principes suffisent :

  • Du général au précis. C'est ce qui rend les jokers utiles : on peut s'abonner à un site entier, puis affiner.
  • Pas d'accent, pas d'espace, pas de majuscule. Les topics circulent entre des systèmes hétérogènes ; restez en minuscules et tirets.
  • La grandeur en dernier. Si vous placez temperature avant le site, vous ne pourrez plus vous abonner à « tout ce qui concerne le capteur 01 ».
✓ terminé
Quiz

Quiz — MQTT et les topics

Jour 1 · Comprendre et faire parler les objets · 10 min

1. Un capteur publie sur thermeo/lyon/etage2/capt-07/temperature. Un abonné écoute thermeo/lyon/+/+/temperature. Reçoit-il le message ?

2. Quel abonnement capte à la fois les températures et les humidités de tout le parc ?

3. Dans le modèle publication-abonnement, que connaît le capteur ?

4. Pourquoi MQTT convient mieux qu'HTTP à un capteur sur batterie ?

5. Lequel de ces topics est invalide ?

✓ terminé
Travaux pratiques

TP 1 — Démarrer votre broker

Jour 1 · Comprendre et faire parler les objets · 45 min

Faire tourner un broker MQTT en local et vérifier qu'il accepte les connexions.

Ce dont vous avez besoin

Docker uniquement. Vérifiez avec docker --version — version 24 ou supérieure. Aucun matériel.

  1. Créez un dossier de travail tp-iot-mqtt et placez-vous dedans. Tout le module s'y déroulera.
  2. Créez mosquitto.conf. Sans ce fichier, le broker n'écoute que sur la boucle interne du conteneur et vous ne pourrez pas l'atteindre :
    listener 1883
    allow_anonymous true
    
    # Journalisation détaillée : indispensable pour comprendre ce qui se passe
    log_type all
    connection_messages true
  3. Démarrez le broker en montant votre configuration :
    docker run -it --name broker-thermeo \
      -p 1883:1883 \
      -v "$(pwd)/mosquitto.conf":/mosquitto/config/mosquitto.conf \
      eclipse-mosquitto:2
  4. Lisez les journaux : vous devez voir Opening ipv4 listen socket on port 1883. Laissez ce terminal ouvert — il sera votre console de supervision tout au long du module.
  5. Dans un second terminal, vérifiez que le port répond :
    docker exec broker-thermeo mosquitto_sub -h localhost -t 'test' -C 1 -W 3
  6. La commande attend trois secondes puis rend la main sans erreur : le broker écoute. Un Connection refused signifie que la configuration n'a pas été montée — reprenez l'étape b et vérifiez le chemin.
Questions de réflexion — à préparer par écrit
  • Pourquoi allow_anonymous true est-il acceptable ici, et pourquoi sera-t-il inacceptable en production ?
  • Le broker est le point de passage unique de tous les messages. Quelle conséquence sur la disponibilité du système ?
✓ terminé
Travaux pratiques

TP 2 — Publier, souscrire, filtrer

Jour 1 · Comprendre et faire parler les objets · 1 h 15

Mettre en œuvre la convention de nommage de Thermeo et la manipuler avec les jokers.

  1. Abonnez-vous à toutes les températures du site de Lyon :
    docker exec broker-thermeo mosquitto_sub -h localhost -v \
      -t 'thermeo/lyon/+/+/temperature'
  2. Dans un troisième terminal, publiez trois relevés :
    docker exec broker-thermeo mosquitto_pub -h localhost \
      -t 'thermeo/lyon/etage1/capt-01/temperature' -m '21.4'
    docker exec broker-thermeo mosquitto_pub -h localhost \
      -t 'thermeo/lyon/etage2/capt-07/temperature' -m '22.9'
    docker exec broker-thermeo mosquitto_pub -h localhost \
      -t 'thermeo/paris/etage1/capt-12/temperature' -m '19.8'
  3. L'abonné n'a reçu que deux messages sur trois. Identifiez lequel manque et pourquoi avant de continuer — la réponse tient au deuxième niveau du topic.
  4. Relancez avec le joker multi-niveaux :
    docker exec broker-thermeo mosquitto_sub -h localhost -v -t 'thermeo/#'
  5. Republiez les trois relevés : tout arrive cette fois. Ajoutez une publication d'humidité et vérifiez qu'elle est également captée.
  6. Écrivez capteur.py, qui simule un capteur émettant toutes les deux secondes :
    import time, random, json
    import paho.mqtt.client as mqtt
    
    SITE, ETAGE, CAPTEUR = "lyon", "etage1", "capt-01"
    TOPIC = f"thermeo/{SITE}/{ETAGE}/{CAPTEUR}/temperature"
    
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
    c.connect("localhost", 1883, 60)
    c.loop_start()
    
    try:
        while True:
            valeur = round(random.uniform(19.0, 24.0), 1)
            charge = json.dumps({"valeur": valeur, "unite": "C"})
            c.publish(TOPIC, charge)
            print("publie", charge)
            time.sleep(2)
    except KeyboardInterrupt:
        c.loop_stop(); c.disconnect()
  7. Lancez python capteur.py et vérifiez dans l'abonné thermeo/# que les relevés arrivent bien toutes les deux secondes.
Questions de réflexion — à préparer par écrit
  • Pourquoi publier une charge utile JSON plutôt que la valeur brute 21.4 ?
  • La convention place le site avant l'étage. Que faciliterait ou compliquerait le fait de placer la grandeur en premier ?
✓ terminé
Leçon

Qualité de service : que garantit-on ?

Jour 1 · Comprendre et faire parler les objets · 20 min

Un message peut se perdre. Le réseau coupe, le capteur redémarre, l'abonné plante. MQTT propose trois niveaux de garantie, appelés QoS, et le choix n'est pas anodin : chaque niveau supplémentaire coûte des échanges réseau, donc de l'énergie.

NiveauPromesseÉchangesConséquence
QoS 0Au plus une fois1Le message part et on n'en parle plus. Il peut se perdre sans que personne le sache.
QoS 1Au moins une fois2Le broker accuse réception. Si l'accusé se perd, le message est renvoyé — il peut donc arriver en double.
QoS 2Exactement une fois4Poignée de main en quatre temps. Aucune perte, aucun doublon, mais quatre fois plus de trafic.
L'erreur à ne pas commettre

Prendre QoS 2 « pour être tranquille ». Sur un capteur sur pile qui émet toutes les dix minutes, c'est quadrupler le trafic pour garantir l'unicité d'un relevé de température qui sera de toute façon remplacé dix minutes plus tard.

Comment choisir

  • QoS 0 — une mesure périodique, vite remplacée par la suivante. Perdre un relevé de température sur cent n'a aucune conséquence.
  • QoS 1 — une alerte, un événement. Mieux vaut la recevoir deux fois que pas du tout. Il faut alors gérer les doublons côté application.
  • QoS 2 — un ordre, une commande, une transaction. Arrêter deux fois une chaudière n'est pas neutre.
Publieur et abonné peuvent différer

Le niveau effectif est le minimum des deux. Un abonné en QoS 2 recevra en QoS 0 ce qu'un publieur a émis en QoS 0 : la garantie ne se crée pas après coup, elle se transporte.

La session persistante

Un abonné déconnecté ne reçoit rien — sauf s'il a demandé une session persistante. Le broker met alors ses messages en file d'attente et les lui livre à la reconnexion. Cela suppose deux choses : un identifiant client stable, et un QoS d'au moins 1. Vous le vérifierez dans le TP qui suit.

✓ terminé
Quiz

Quiz — Qualité de service

Jour 1 · Comprendre et faire parler les objets · 8 min

1. Un relevé de température toutes les deux secondes. Quel QoS ?

2. Une commande d'arrêt d'urgence d'une chaudière. Quel QoS ?

3. Le publieur émet en QoS 0, l'abonné a souscrit en QoS 2. Que se passe-t-il ?

4. Que faut-il pour qu'un abonné retrouve les messages émis pendant sa coupure ?

✓ terminé
Travaux pratiques

TP 3 — Éprouver les trois niveaux

Jour 1 · Comprendre et faire parler les objets · 1 h

Constater par vous-même ce que chaque niveau garantit, et ce qu'il coûte.

Avant de commencer

Notez par écrit ce que vous attendez de chaque niveau. Vous comparerez ensuite avec ce que vous observez : c'est là que la notion s'installe.

  1. Abonnez-vous en QoS 0, puis coupez l'abonné pendant que le capteur publie. Relancez-le : les messages émis pendant la coupure sont définitivement perdus.
    docker exec broker-thermeo mosquitto_sub -h localhost -v -t 'thermeo/#' -q 0
  2. Recommencez avec une session persistante et QoS 1. L'identifiant fixe et l'option -c demandent au broker de conserver la file :
    docker exec broker-thermeo mosquitto_sub -h localhost -v \
      -t 'thermeo/#' -q 1 -c -i 'superviseur-thermeo'
  3. Coupez l'abonné, laissez publier une dizaine de messages, relancez exactement la même commande. Les messages manqués sont livrés : c'est toute la différence.
  4. Passez le publieur en QoS 2 et observez les journaux du broker :
    c.publish(TOPIC, charge, qos=2)
  5. Comptez les échanges réseau visibles dans les journaux pour un seul message en QoS 2, puis en QoS 0. Consignez l'écart : c'est le prix de la garantie.
  6. Remplissez le tableau comparatif dans votre compte rendu : pour chaque niveau, le nombre d'échanges observés, ce qui est garanti, et un cas d'usage Thermeo qui le justifie.
Questions de réflexion — à préparer par écrit
  • Vos observations correspondent-elles à ce que vous aviez noté avant de commencer ?
  • Si un abonné en QoS 1 reçoit deux fois le même relevé, quelle conséquence côté application, et comment s'en prémunir ?
✓ terminé
Leçon

Connaître l'état du parc à tout instant

Jour 1 · Comprendre et faire parler les objets · 20 min

Reste le troisième problème de Thermeo : une panne de capteur passe inaperçue. MQTT y répond par deux mécanismes complémentaires, qu'on confond souvent.

Le message retenu

Un message publié avec le drapeau retain est conservé par le broker comme « dernière valeur connue » du topic. Tout nouvel abonné le reçoit immédiatement, sans attendre la prochaine publication.

C'est ce qui permet à un tableau de bord de s'afficher rempli dès son ouverture, au lieu de montrer des cases vides pendant dix minutes. Un seul message retenu par topic : chaque nouveau remplace le précédent. Pour l'effacer, on publie une charge utile vide.

Le testament

Le Last Will — le testament — est un message que le client confie au broker au moment de se connecter, avec cette consigne : « si je disparais sans prévenir, publie ceci à ma place ».

Le broker le déclenche lorsque le client cesse de donner signe de vie, à l'expiration du délai de keep-alive. Le capteur signale donc sa propre panne, sans que personne ait à le surveiller.

La distinction à ne pas manquer

Le message retenu est attaché à un topic : il conserve la dernière valeur, pour tout nouvel abonné.
La session persistante est attachée à un client : elle lui conserve les messages manqués pendant son absence.
Les deux survivent à une déconnexion, mais ils ne répondent pas à la même question. C'est le point sur lequel votre encadrant vous interrogera.

Les deux ensemble

Un capteur bien conçu publie en-ligne en retenu à sa connexion, et confie un testament hors-ligne, retenu lui aussi. Résultat : le topic de statut porte en permanence l'état réel du capteur, disponible instantanément pour qui s'y abonne. C'est exactement ce qui manquait à Thermeo, et vous allez le mettre en place maintenant.

✓ terminé
Travaux pratiques

TP 4 — Détecter un capteur muet

Jour 1 · Comprendre et faire parler les objets · 1 h

Mettre en place les deux mécanismes qui donnent l'état du parc en temps réel.

  1. Publiez une valeur en message retenu :
    docker exec broker-thermeo mosquitto_pub -h localhost -r \
      -t 'thermeo/lyon/etage1/capt-01/temperature' -m '21.4'
  2. Abonnez-vous ensuite à ce topic : vous recevez la valeur immédiatement, sans attendre de nouvelle publication.
  3. Vérifiez qu'un retenu écrase le précédent : publiez 22.0, réabonnez-vous, constatez que seule la dernière valeur revient. Puis effacez-le avec une charge vide :
    docker exec broker-thermeo mosquitto_pub -h localhost -r \
      -t 'thermeo/lyon/etage1/capt-01/temperature' -m ''
  4. Mettez en place le testament. Il se déclare avant la connexion :
    STATUT = f"thermeo/{SITE}/{ETAGE}/{CAPTEUR}/statut"
    
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
    c.will_set(STATUT, "hors-ligne", qos=1, retain=True)
    c.connect("localhost", 1883, 60)
    c.publish(STATUT, "en-ligne", qos=1, retain=True)
  5. Surveillez les statuts du parc dans un terminal :
    docker exec broker-thermeo mosquitto_sub -h localhost -v -t 'thermeo/+/+/+/statut'
  6. Lancez le capteur : en-ligne apparaît. Interrompez-le brutalement par Ctrl+C — le broker publie hors-ligne à sa place. C'est la détection de panne qui manquait à Thermeo.
  7. Testez la déconnexion propre : publiez hors-ligne avant disconnect() et vérifiez que le testament n'est alors pas déclenché.
Questions de réflexion — à préparer par écrit
  • Pourquoi le testament doit-il être déclaré avant la connexion, et pas après ?
  • Que verrait un tableau de bord ouvert après coup si le statut n'était pas retenu ?
  • Quel keep-alive retiendriez-vous pour un capteur sur pile qui émet toutes les dix minutes ?
✓ terminé
Point de contrôle

Point de contrôle — fin du jour 1

Jour 1 · Comprendre et faire parler les objets · 15 min

Vérifiez avant de continuer
Mon broker tourne et je sais lire ses journaux
Un capteur Python publie en continu, un abonné joker reçoit ses messages
J'ai observé une perte réelle en QoS 0 et une retransmission en QoS 1
Mon tableau comparatif des trois niveaux de QoS est rempli
La coupure brutale d'un capteur fait basculer son statut en hors-ligne
Je sais expliquer la différence entre message retenu et session persistante
Mes réponses aux questions de réflexion sont écrites
Ce que vous présentez au point de 16 h 30

Votre encadrant vous demandera de montrer, pas de raconter : il coupera lui-même un capteur pour voir le statut basculer. Préparez vos terminaux.

Utilisez le bouton Exporter mon avancement en haut de page : il copie le récapitulatif de votre progression, que vous lui transmettez.

✓ terminé
Leçon

Ce qu'un broker ouvert laisse passer

Jour 2 · Sécuriser les échanges · 20 min

La maquette du jour 1 fonctionne, mais elle est ouverte à tous vents. N'importe qui sur le réseau peut lire les relevés — et surtout en injecter de faux.

Le scénario qui doit vous inquiéter

Un attaquant publie -10 sur le topic de température d'un bâtiment. La régulation croit qu'il gèle et pousse le chauffage au maximum. Ou l'inverse : il publie 22 en plein hiver sur un capteur réel à 3 °C, et les canalisations gèlent.

Chez Thermeo, une consigne falsifiée n'est pas une fuite de données : c'est un dégât matériel.

Trois questions, trois réponses distinctes

On confond souvent ces trois notions. Elles répondent pourtant à des questions différentes, et chacune appelle son propre mécanisme.

QuestionNotionMécanisme MQTT
Qui es-tu ?AuthentificationMot de passe, puis certificat client
Qui peut lire ce qu'on s'échange ?ConfidentialitéTLS
Qu'as-tu le droit de faire ?AutorisationListes de contrôle d'accès (ACL)
Retenez surtout ceci

Le chiffrement n'autorise rien. Un capteur légitime, authentifié et en TLS, peut parfaitement écrire sur le topic d'un autre capteur si aucune ACL ne l'en empêche. C'est l'erreur de conception la plus fréquente, et votre encadrant vous interrogera dessus.

Ce que le responsable sécurité exige

Trois conditions avant tout déploiement, que vous allez remplir aujourd'hui :

  1. Plus aucun accès anonyme.
  2. Tout le trafic chiffré.
  3. Chaque capteur limité à ses propres topics — celui de Lyon ne doit pas pouvoir écrire sur ceux de Paris.
✓ terminé
Leçon

Certificats X.509 et chaîne de confiance

Jour 2 · Sécuriser les échanges · 25 min

Le mot de passe règle la question « qui es-tu ? », mais il circule en clair tant que la connexion ne l'est pas. Pour chiffrer, il faut des certificats — et pour des certificats, il faut une autorité.

Autorité de certification ca.crt · ca.key Certificat du broker CN = localhost prouve son identité Certificat du capteur CN = capt-01 sert d'identifiant signe signe
Une seule autorité signe les deux certificats. C'est ce qui permet au broker et au capteur de se reconnaître mutuellement.

Le principe

Une autorité de certification (CA) est simplement une paire de clés dont on décide qu'elle fait foi. Elle signe les certificats du broker et des capteurs. Chacun d'eux, possédant le certificat de l'autorité, peut alors vérifier que l'autre a bien été signé par elle.

Pour un réseau interne, vous êtes votre propre autorité : c'est ce qu'on appelle une CA privée. Aucun organisme extérieur n'intervient, et c'est parfaitement légitime tant que les certificats ne servent qu'entre vos machines.

FichierContenuQui le détient
ca.keyClé privée de l'autoritéPersonne d'autre que vous. Qui l'a peut signer n'importe quoi
ca.crtCertificat public de l'autoritéTout le monde — c'est lui qui permet de vérifier
broker.keyClé privée du brokerLe broker seul
broker.crtCertificat du broker, signé par la CAPublic
capt-01.key/.crtIdem pour un capteurLe capteur
Le champ CN n'est pas décoratif

Le Common Name du certificat du broker doit correspondre au nom par lequel les clients le joignent. Si le certificat dit localhost et que le client se connecte à 192.168.1.10, la vérification échoue — et c'est normal : c'est exactement le mécanisme qui protège d'un serveur usurpé.

Deux usages du certificat client

Un capteur peut présenter son propre certificat. Cela apporte deux choses d'un coup :

  • Il prouve son identité sans mot de passe, donc sans secret à stocker en clair dans le firmware.
  • Son CN peut servir directement d'identifiant — c'est l'option use_identity_as_username, qui relie le certificat aux ACL.
Sur la durée de validité

825 jours pour les certificats de service, 3650 pour l'autorité. L'écart n'est pas arbitraire : renouveler un certificat de service est une opération de routine, alors que changer l'autorité oblige à redéployer la confiance sur tout le parc.

✓ terminé
Quiz

Quiz — Authentification et chiffrement

Jour 2 · Sécuriser les échanges · 8 min

1. Un capteur est authentifié et communique en TLS. Peut-il publier sur le topic d'un autre capteur ?

2. Quel fichier ne doit jamais quitter la machine qui l'a généré ?

3. Le certificat du broker porte CN = localhost, mais le client se connecte à 10.0.0.5. Que se passe-t-il ?

4. À quoi sert use_identity_as_username ?

✓ terminé
Travaux pratiques

TP 1 — Fermer la porte

Jour 2 · Sécuriser les échanges · 40 min

Supprimer l'accès anonyme et imposer une authentification.

  1. Créez le fichier de mots de passe avec un premier compte. La commande demande le mot de passe deux fois :
    docker exec -it broker-thermeo \
      mosquitto_passwd -c /mosquitto/config/passwd capt-01
  2. Ajoutez deux comptes de plus. Attention : sans -c, sinon vous écrasez le fichier :
    docker exec -it broker-thermeo \
      mosquitto_passwd /mosquitto/config/passwd capt-07
    docker exec -it broker-thermeo \
      mosquitto_passwd /mosquitto/config/passwd superviseur
  3. Modifiez mosquitto.conf :
    listener 1883
    allow_anonymous false
    password_file /mosquitto/config/passwd
    
    log_type all
    connection_messages true
  4. Redémarrez avec docker restart broker-thermeo, puis tentez une publication sans identifiants. Elle échoue — c'est le résultat attendu. Notez le code d'erreur exact, il vous servira à diagnostiquer plus tard.
  5. Republiez avec les identifiants :
    docker exec broker-thermeo mosquitto_pub -h localhost \
      -u capt-01 -P '<votre-mot-de-passe>' \
      -t 'thermeo/lyon/etage1/capt-01/temperature' -m '21.4'
  6. Adaptez capteur.py, avant l'appel à connect :
    c.username_pw_set("capt-01", "<votre-mot-de-passe>")
Questions de réflexion — à préparer par écrit
  • Le mot de passe circule encore en clair sur le réseau. Qu'apporte malgré tout l'authentification à ce stade ?
  • Que se passe-t-il si vous oubliez -c à la création, et pourquoi est-ce dangereux sur un broker en service ?
✓ terminé
Travaux pratiques

TP 2 — Devenir sa propre autorité

Jour 2 · Sécuriser les échanges · 50 min

Produire la chaîne de confiance dont le broker et les capteurs auront besoin.

Conseil

Écrivez ces commandes dans un script generer-certs.sh au fur et à mesure. Vous le rejouerez plusieurs fois — et c'est un livrable attendu.

  1. Créez un dossier certs dans votre répertoire de travail.
  2. Générez la clé et le certificat de votre autorité, valable dix ans :
    openssl genrsa -out certs/ca.key 2048
    
    openssl req -new -x509 -days 3650 -key certs/ca.key \
      -out certs/ca.crt \
      -subj "/C=FR/O=Thermeo/CN=Thermeo Root CA"
  3. Générez la clé et la demande de signature du broker. Le CN doit être le nom par lequel les clients le joindront :
    openssl genrsa -out certs/broker.key 2048
    
    openssl req -new -key certs/broker.key -out certs/broker.csr \
      -subj "/C=FR/O=Thermeo/CN=localhost"
  4. Faites signer le certificat du broker par votre autorité :
    openssl x509 -req -in certs/broker.csr -CA certs/ca.crt \
      -CAkey certs/ca.key -CAcreateserial \
      -out certs/broker.crt -days 825 -sha256
  5. Faites de même pour le capteur — il servira au TP 4 :
    openssl genrsa -out certs/capt-01.key 2048
    
    openssl req -new -key certs/capt-01.key -out certs/capt-01.csr \
      -subj "/C=FR/O=Thermeo/CN=capt-01"
    
    openssl x509 -req -in certs/capt-01.csr -CA certs/ca.crt \
      -CAkey certs/ca.key -CAcreateserial \
      -out certs/capt-01.crt -days 825 -sha256
  6. Inspectez le résultat et relevez l'émetteur, le sujet et les dates de validité :
    openssl x509 -in certs/broker.crt -noout -text | head -20
Questions de réflexion — à préparer par écrit
  • ca.key devrait-il être conservé dans une vraie installation ?
  • Que faudra-t-il faire dans 825 jours, et comment l'anticiper sur cent capteurs ?
✓ terminé
Travaux pratiques

TP 3 — Chiffrer le trafic

Jour 2 · Sécuriser les échanges · 1 h

Passer le broker en TLS et vérifier de vos yeux que le chiffrement est effectif.

  1. Ouvrez un écouteur TLS sur le port 8883, en gardant provisoirement le 1883 pour comparer :
    listener 1883
    allow_anonymous false
    password_file /mosquitto/config/passwd
    
    listener 8883
    allow_anonymous false
    password_file /mosquitto/config/passwd
    cafile /mosquitto/config/certs/ca.crt
    certfile /mosquitto/config/certs/broker.crt
    keyfile /mosquitto/config/certs/broker.key
    tls_version tlsv1.2
    
    log_type all
  2. Recréez le conteneur en montant aussi les certificats :
    docker rm -f broker-thermeo
    
    docker run -it --name broker-thermeo \
      -p 1883:1883 -p 8883:8883 \
      -v "$(pwd)/mosquitto.conf":/mosquitto/config/mosquitto.conf \
      -v "$(pwd)/certs":/mosquitto/config/certs \
      -v "$(pwd)/passwd":/mosquitto/config/passwd \
      eclipse-mosquitto:2
  3. Si le broker refuse de démarrer, lisez le message : une erreur de permission sur les clés est le cas le plus fréquent. Consignez le message et sa résolution — c'est un livrable.
  4. Publiez en TLS en fournissant le certificat de l'autorité, pour que le client puisse vérifier le broker :
    mosquitto_pub -h localhost -p 8883 \
      --cafile certs/ca.crt \
      -u capt-01 -P '<mot-de-passe>' \
      -t 'thermeo/lyon/etage1/capt-01/temperature' -m '21.4'
  5. Retentez sans --cafile : la connexion échoue, le client refusant un certificat qu'il ne peut pas valider. C'est le comportement attendu.
  6. Adaptez capteur.py :
    import ssl
    
    c.username_pw_set("capt-01", "<mot-de-passe>")
    c.tls_set(ca_certs="certs/ca.crt", tls_version=ssl.PROTOCOL_TLSv1_2)
    c.connect("localhost", 8883, 60)
  7. Si vous avez Wireshark, capturez sur la boucle locale et comparez 1883 et 8883 : sur le premier, la valeur 21.4 se lit en clair ; sur le second, non. C'est la démonstration que le chiffrement fonctionne.
  8. Une fois la vérification faite, supprimez l'écouteur 1883 et redémarrez : plus aucun trafic en clair.
Questions de réflexion — à préparer par écrit
  • Pourquoi une erreur de certificat doit-elle faire échouer la connexion plutôt que produire un simple avertissement ?
✓ terminé
Leçon

Autoriser : les listes de contrôle d'accès

Jour 2 · Sécuriser les échanges · 15 min

Il reste la troisième exigence : qu'un capteur ne puisse écrire que sur ses propres topics. C'est le rôle des ACL, déclarées dans un fichier que Mosquitto relit à chaque connexion.

La syntaxe

Une ACL se lit comme une suite de blocs : un utilisateur, puis ses droits.

user superviseur
topic read thermeo/#

user capt-01
topic write thermeo/lyon/etage1/capt-01/#
topic read thermeo/lyon/etage1/capt-01/consigne

Trois verbes existent : read, write, et readwrite. Les jokers + et # fonctionnent comme dans les abonnements.

Le principe du moindre privilège

Chaque capteur n'écrit que sous sa propre branche, et ne lit que sa consigne. Le superviseur lit tout mais n'écrit rien. Si un capteur est compromis, le dégât reste circonscrit à sa branche — c'est exactement l'objectif.

Ce que cela implique

Notez la conséquence du dernier point : dans cette configuration, le superviseur ne peut pas publier de consigne. Si Thermeo veut plus tard piloter les chaudières depuis la supervision, il faudra un compte distinct, avec des droits d'écriture explicites et limités aux topics de consigne. C'est une décision d'architecture, pas un détail de configuration.

Un capteur volé

Que faire si un capteur disparaît du site ? Son certificat reste valide. Il faut pouvoir le révoquer — soit en retirant son bloc du fichier ACL, soit par une liste de révocation. Vérifiez que votre maquette permet la première option : c'est la question que posera votre encadrant.

✓ terminé
Quiz

Quiz — Autorisation

Jour 2 · Sécuriser les échanges · 6 min

1. Une ACL déclare user capt-01 puis topic write thermeo/lyon/etage1/capt-01/#. capt-01 peut-il lire les relevés de capt-07 ?

2. Un capteur est volé sur site. Quelle est la première action ?

3. Dans la configuration proposée, le superviseur peut-il publier une consigne ?

✓ terminé
Travaux pratiques

TP 4 — Identité par certificat et cloisonnement

Jour 2 · Sécuriser les échanges · 1 h

Donner à chaque capteur une identité propre et le restreindre à son périmètre.

  1. Exigez un certificat client et utilisez son CN comme identité :
    require_certificate true
    use_identity_as_username true
  2. Le capteur présente désormais son certificat, sans mot de passe :
    c.tls_set(ca_certs="certs/ca.crt",
              certfile="certs/capt-01.crt",
              keyfile="certs/capt-01.key",
              tls_version=ssl.PROTOCOL_TLSv1_2)
  3. Vérifiez dans les journaux que le client est identifié comme capt-01 : c'est le CN du certificat qui fait foi.
  4. Créez le fichier acl :
    # Le superviseur lit l'ensemble du parc
    user superviseur
    topic read thermeo/#
    
    # Chaque capteur n'écrit que sur sa propre branche
    user capt-01
    topic write thermeo/lyon/etage1/capt-01/#
    topic read thermeo/lyon/etage1/capt-01/consigne
    
    user capt-07
    topic write thermeo/lyon/etage2/capt-07/#
    topic read thermeo/lyon/etage2/capt-07/consigne
  5. Déclarez-le et redémarrez :
    acl_file /mosquitto/config/acl
  6. Depuis capt-01, tentez de publier sur le topic de capt-07. La publication est rejetée — c'est précisément l'exigence du responsable sécurité.
  7. Vérifiez le pendant : le superviseur lit tout le parc, mais sa tentative de publier une consigne est refusée.
Questions de réflexion — à préparer par écrit
  • Trois mécanismes se superposent maintenant : mot de passe, certificat, ACL. Que protège chacun, exactement ?
  • Quelle discipline use_identity_as_username impose-t-il à la génération des certificats sur un parc de cent capteurs ?
✓ terminé
Point de contrôle

Point de contrôle — fin du jour 2

Jour 2 · Sécuriser les échanges · 15 min

Vérifiez avant de continuer
Une publication sans identifiants est refusée par mon broker
J'ai généré une autorité, un certificat broker et un certificat capteur
Je sais lire le sujet et l'émetteur d'un certificat avec openssl
Mon capteur publie en TLS sur 8883 et le port 1883 est fermé
Un capteur ne peut pas écrire sur la branche d'un autre — vérifié dans les journaux
Je sais expliquer ce que protège le TLS et ce qu'il ne protège pas
Mon journal des incidents est à jour
Mes clés privées ne sont pas versionnées dans Git
Le point éliminatoire

Des clés privées présentes dans le dépôt Git font échouer la validation du module, sans discussion. Sur un parcours qui traite de sécurité, il ne peut y avoir aucune tolérance là-dessus.

Vérifiez avec git status et ajoutez un .gitignore avant de livrer.

✓ terminé
Leçon

CoAP : l'autre voie

Jour 3 · Élargir et assembler · 20 min

MQTT n'est pas la seule réponse. CoAP transpose le modèle REST sur UDP, pour des équipements encore plus contraints que ceux que nous avons manipulés.

Deux philosophies

MQTTCoAP
ModèlePublication / abonnementRequête / réponse, comme HTTP
TransportTCP — connexion maintenueUDP — pas de connexion
IntermédiaireUn broker, obligatoireAucun : on interroge l'objet directement
Qui déclencheL'objet, quand il a une valeurLe client, quand il veut savoir
En-tête2 octets4 octets
SécuritéTLSDTLS
Ce qui les départage vraiment

Ce n'est pas la taille de l'en-tête. C'est la connexion : MQTT en maintient une en permanence, ce qui consomme, mais permet à l'objet de signaler un événement immédiatement. CoAP ne maintient rien, mais l'objet doit alors être joignable au moment où on l'interroge — impossible s'il dort pour économiser sa pile.

L'observation

CoAP propose un mécanisme d'observe : le client s'inscrit sur une ressource et le serveur le notifie des changements. C'est un rapprochement du modèle MQTT, mais sans broker — donc sans point de collecte central, ce qui complique la supervision de cent capteurs.

Comment choisir chez Thermeo

Un capteur alimenté secteur qui doit remonter des alertes : MQTT. Un capteur sur pile qui se réveille toutes les heures et qu'on interroge à la demande : CoAP mérite l'examen. Les deux peuvent cohabiter derrière une passerelle.

✓ terminé
Leçon

Les réseaux LPWAN

Jour 3 · Élargir et assembler · 25 min

Reste la question radio. Pour les sept bâtiments de Thermeo sans connexion exploitable, il faut un réseau longue portée et basse consommation — c'est ce que désigne LPWAN.

Les trois candidats

LoRaWANSigfoxNB-IoT
Portée urbaine2 à 5 km3 à 10 km1 à 5 km
Débit0,3 à 50 kbit/s100 bit/s20 à 250 kbit/s
Messages par jourlimité par le cycle d'utilisation140 en émissionpas de quota fixe
Taille utile51 à 242 octets12 octetsjusqu'à 1 600 octets
Infrastructurevos propres passerellesopérateurréseau mobile
Coût annuel par objetfaible, mais passerelles à financerquelques eurosabonnement mobile
Le calcul qui tranche

Thermeo veut un relevé toutes les quinze minutes, soit 96 messages par jour. Regardez la ligne « messages par jour » : une des trois technologies est éliminée par ce seul chiffre. Laquelle, et pourquoi ? C'est le premier travail du TP.

Trois critères qu'on oublie souvent

  • Le cycle d'utilisation. Sur les bandes libres, la réglementation limite le temps d'émission — typiquement 1 % du temps. Cela contraint la conception applicative bien plus que le débit théorique.
  • Qui exploite l'infrastructure. LoRaWAN vous rend indépendant d'un opérateur, mais vous devenez responsable des passerelles, de leur alimentation et de leur maintenance.
  • La pérennité. Un opérateur peut cesser son service — c'est déjà arrivé. Sur un choix qui engage dix ans, ce risque pèse autant que les caractéristiques techniques.
✓ terminé
Quiz

Quiz — CoAP et LPWAN

Jour 3 · Élargir et assembler · 8 min

1. Quelle différence structurelle entre MQTT et CoAP ?

2. Thermeo veut 96 messages par jour et 12 octets de charge utile. Quelle technologie est éliminée ?

3. Un capteur sur pile qui dort et se réveille toutes les heures. MQTT ou CoAP ?

4. Qu'est-ce que le cycle d'utilisation contraint ?

✓ terminé
Travaux pratiques

TP 1 — Mettre CoAP en œuvre

Jour 3 · Élargir et assembler · 50 min

Faire fonctionner un échange CoAP et mesurer ce qui le distingue de MQTT.

Prérequis

Installez la bibliothèque : pip install aiocoap

  1. Écrivez le serveur serveur_coap.py :
    import asyncio, random
    import aiocoap.resource as resource
    import aiocoap
    
    class Temperature(resource.Resource):
        async def render_get(self, request):
            valeur = round(random.uniform(19.0, 24.0), 1)
            return aiocoap.Message(payload=str(valeur).encode())
    
    async def main():
        racine = resource.Site()
        racine.add_resource(['temperature'], Temperature())
        await aiocoap.Context.create_server_context(racine, bind=('127.0.0.1', 5683))
        await asyncio.get_running_loop().create_future()
    
    asyncio.run(main())
  2. Écrivez le client client_coap.py :
    import asyncio
    from aiocoap import Context, Message, GET
    
    async def main():
        ctx = await Context.create_client_context()
        req = Message(code=GET, uri='coap://127.0.0.1/temperature')
        rep = await ctx.request(req).response
        print('reponse :', rep.payload.decode())
    
    asyncio.run(main())
  3. Lancez le serveur puis le client. Notez la différence de posture avec MQTT : ici c'est le client qui demande, le capteur ne pousse rien de lui-même.
  4. Ajoutez une seconde ressource /humidite et vérifiez que le même serveur les expose toutes les deux.
  5. Comparez la taille d'un échange CoAP et d'un échange MQTT, et consignez ce que cela change pour un capteur sur pile.
Questions de réflexion — à préparer par écrit
  • CoAP fonctionne sur UDP, qui ne garantit pas la livraison. Comment le protocole compense-t-il, et à quel coût ?
  • CoAP n'a pas de broker. Avantage ou inconvénient pour superviser cent capteurs ?
✓ terminé
Travaux pratiques

TP 2 — Un ESP32 sans ESP32

Jour 3 · Élargir et assembler · 1 h

Exécuter du vrai firmware embarqué, sans aucune carte physique.

Broker public

Ce TP publie sur test.mosquitto.org, un broker public, parce que le simulateur tourne dans le cloud et n'atteint pas votre poste. N'y publiez jamais de donnée réelle : c'est un réflexe à installer maintenant.

  1. Ouvrez wokwi.com et créez un projet ESP32. Le simulateur exécute réellement le firmware et dispose d'une pile réseau.
  2. Ajoutez un capteur DHT22 par le bouton +. Reliez sa broche de données à la GPIO 15, son alimentation au 3V3 et sa masse au GND.
  3. Saisissez le firmware dans sketch.ino. Le SSID Wokwi-GUEST est le réseau du simulateur, sans mot de passe :
    #include <WiFi.h>
    #include <PubSubClient.h>
    #include "DHTesp.h"
    
    const char* SSID = "Wokwi-GUEST";
    const char* BROKER = "test.mosquitto.org";
    const char* TOPIC = "thermeo/demo/etage1/esp32-01/temperature";
    
    WiFiClient net;
    PubSubClient mqtt(net);
    DHTesp dht;
    
    void setup() {
      Serial.begin(115200);
      dht.setup(15, DHTesp::DHT22);
      WiFi.begin(SSID, "");
      while (WiFi.status() != WL_CONNECTED) { delay(250); Serial.print("."); }
      Serial.println(" wifi ok");
      mqtt.setServer(BROKER, 1883);
    }
    
    void loop() {
      if (!mqtt.connected()) mqtt.connect("esp32-thermeo-01");
      TempAndHumidity d = dht.getTempAndHumidity();
      char buf[16];
      dtostrf(d.temperature, 4, 1, buf);
      mqtt.publish(TOPIC, buf);
      Serial.printf("publie %s\n", buf);
      delay(5000);
    }
  4. Déclarez les bibliothèques dans libraries.txt :
    PubSubClient
    DHT sensor library for ESPx
  5. Démarrez la simulation. Le moniteur série affiche la connexion puis les publications. Cliquez sur le DHT22 pour faire varier la température : la valeur publiée suit.
  6. Depuis votre poste, recevez ce que publie l'ESP32 simulé :
    mosquitto_sub -h test.mosquitto.org -v \
      -t 'thermeo/demo/etage1/esp32-01/#'
  7. Vous recevez les relevés d'un microcontrôleur qui n'existe pas physiquement. Mesurez le délai entre l'affichage sur le moniteur série et la réception.
  8. Ajoutez un testament au firmware, comme au jour 1, en utilisant la signature complète de mqtt.connect. Coupez la simulation et vérifiez la bascule du statut.
Questions de réflexion — à préparer par écrit
  • Le simulateur exécute le firmware mais pas le matériel. Qu'est-ce qu'il ne permettra jamais de tester ?
  • Le firmware se reconnecte à chaque boucle si nécessaire. Quel problème sur un capteur alimenté par pile ?
✓ terminé
Travaux pratiques

TP 3 — Instruire le choix radio

Jour 3 · Élargir et assembler · 1 h 15

Recommander une technologie LPWAN pour les sept bâtiments isolés, sur critères.

Ce qui est évalué ici

Ce n'est pas un exercice technique mais un travail d'ingénieur : une valeur sans source ne sera pas retenue. C'est le point sur lequel votre encadrant vous interrogera.

  1. Documentez-vous sur LoRaWAN, Sigfox et NB-IoT. Sources attendues : LoRa Alliance, documentation The Things Network, spécifications 3GPP.
  2. Construisez un tableau comparatif portant sur : portée urbaine et rurale, débit utile, taille et nombre de messages autorisés par jour, consommation et autonomie estimée, coût annuel par objet, nécessité de passerelles, couverture en France.
  3. Renseignez chaque case avec sa source.
  4. Appliquez les contraintes de Thermeo : un relevé toutes les quinze minutes, 12 octets de charge utile, cinq ans d'autonomie visée, sept bâtiments dont trois en zone rurale, 15 € par objet et par an.
  5. Calculez le nombre de messages quotidiens qu'impose ce rythme et confrontez-le aux quotas. Une technologie est éliminée par ce seul calcul : identifiez-la et démontrez-le.
  6. Rédigez une recommandation d'une page : la technologie retenue, les deux raisons principales, et ce qui vous ferait changer d'avis si une contrainte évoluait.
Questions de réflexion — à préparer par écrit
  • LoRaWAN impose d'exploiter ses propres passerelles. Dans quel cas est-ce un avantage plutôt qu'une charge ?
  • Un opérateur peut cesser son service. Comment ce risque pèse-t-il sur un choix engageant dix ans ?
✓ terminé
Travaux pratiques

TP 4 — Assembler le tout

Jour 3 · Élargir et assembler · 1 h 30

Réunir les acquis des trois jours en une maquette qui tient debout seule.

  1. Repartez du broker sécurisé du jour 2 : TLS, certificats clients, ACL actives.
  2. Générez des certificats pour trois capteurs supplémentaires avec votre script generer-certs.sh, et étendez le fichier ACL en conséquence.
  3. Généralisez capteur.py pour qu'il prenne son identité en paramètre :
    import sys
    SITE, ETAGE, CAPTEUR = sys.argv[1], sys.argv[2], sys.argv[3]
    # le reste du code reprend ces trois variables
    # lancement : python capteur.py lyon etage1 capt-01
  4. Lancez quatre capteurs simultanément, sur deux sites et deux étages différents.
  5. Écrivez superviseur.py, qui s'abonne à tout le parc, tient à jour le dernier relevé de chacun et signale tout capteur muet depuis plus de dix secondes :
    # Ossature attendue — a completer
    # 1. connexion TLS avec le certificat du superviseur
    # 2. abonnement a thermeo/# en QoS 1, session persistante
    # 3. dictionnaire {topic: (valeur, horodatage)}
    # 4. boucle d'affichage toutes les 5 s : etat du parc
    # 5. alerte si horodatage plus vieux que 10 s
  6. Coupez brutalement un capteur : le superviseur doit le signaler, à la fois par son horodatage et par le testament reçu sur le topic de statut.
  7. Vérifiez enfin que l'ACL tient : tentez depuis un capteur de publier sur la branche d'un autre, et confirmez le refus dans les journaux.
Questions de réflexion — à préparer par écrit
  • Votre maquette tourne sur un poste. Qu'est-ce qui casserait en premier avec cent capteurs sur un vrai réseau ?
  • Le superviseur détecte un capteur muet par horodatage et par testament. Pourquoi conserver les deux ?
  • Quelle brique manque encore pour exploiter ces données au-delà du temps réel ?
✓ terminé
Point de contrôle

Point de contrôle — fin du module M1

Jour 3 · Élargir et assembler · 20 min

Vérifiez avant de continuer
Un client CoAP interroge deux ressources distinctes sur mon serveur
Mon ESP32 simulé publie et je reçois ses relevés depuis mon poste
Mon tableau comparatif LPWAN est sourcé et le calcul de quota est posé
Ma recommandation technologique est écrite et argumentée
Quatre capteurs publient en TLS et mon superviseur détecte un capteur muet
Les ACL bloquent bien les écritures hors périmètre
Mon dépôt permet de rejouer toute la maquette de zéro en suivant le README
Ma note de synthèse du module est rédigée
La revue de fin de module

Elle a lieu mercredi à 15 h 00 et dure deux heures. Votre encadrant passera en revue vos livrables, vous fera expliquer les notions et validera — ou non — le module.

Ce qui n'est pas attendu : la perfection du code Python. Un script qui fonctionne et que vous savez expliquer vaut mieux qu'un script élégant recopié.

✓ terminé

Mon avancement

Signature d'émargement

Demander de l'aide à votre encadrant

Étape concernée :

Avant d'envoyer

Cherchez seul pendant 30 minutes et notez ce que vous avez tenté. Ce que vous avez essayé fait partie de ce que vous présentez — c'est ce qui distingue l'autonomie de l'abandon.