Configuration Forge IoT¶
Statut : configuration en vigueur.
Le subscriberforge-mvc-iotest instancié à partir de cet objet (host, port, identifiants, TLS).
Voir Subscriber MQTT et Architecture Forge IoT.
Objectif¶
Définir comment forge-mvc-iot lit sa configuration MQTT à partir de l'environnement, avant d'écrire le subscriber.
L'objectif est d'isoler les questions « où va-t-on lire host/port/topic ?
»
des questions « comment se connecter au broker ?
».
Variables d'environnement¶
| Variable | Défaut | Vide accepté ? | Type cible |
|---|---|---|---|
FORGE_IOT_MQTT_HOST |
localhost |
non : ValueError |
str |
FORGE_IOT_MQTT_PORT |
1883 |
oui (revient au défaut) | int (1..65535) |
FORGE_IOT_MQTT_TOPIC |
forge/+/+/telemetry |
non : ValueError |
str |
FORGE_IOT_MQTT_CLIENT_ID |
forge-iot |
oui (revient au défaut) | str |
FORGE_IOT_MQTT_USERNAME |
None |
oui (= None) |
str | None |
FORGE_IOT_MQTT_PASSWORD |
None |
oui (= None) |
str | None |
FORGE_IOT_API_TOKEN |
None |
oui (= None) |
str | None |
FORGE_IOT_MQTT_TLS_ENABLED |
false |
oui (= false) |
bool |
FORGE_IOT_MQTT_TLS_CA_FILE |
None |
oui (= None) |
str | None |
FORGE_IOT_API_TOKEN protège l'API HTTP : absent ou vide → API ouverte (parcours local) ; défini → Authorization: Bearer <token> exigé sur les routes /api/iot/*.
Comme le mot de passe MQTT, il est masqué dans repr(IotConfig).
Les valeurs par défaut sont locales et pédagogiques : un Mosquitto installé sur la même machine (localhost:1883) écoute sur tous les topics compatibles avec le contrat MQTT.
Le topic par défaut forge/+/+/telemetry utilise les wildcards MQTT + (single-level) pour s'abonner à toutes les paires site/device_id sous forge/.../telemetry.
C'est l'inverse du contrat de publication, où + est interdit.
TLS MQTT¶
Deux variables activent MQTT over TLS (port broker 8883 en usage réel, certificat CA) :
| Variable | Rôle |
|---|---|
FORGE_IOT_MQTT_TLS_ENABLED |
active TLS : true/1/yes/on (insensible à la casse) → activé ; false/0/no/off ou vide → désactivé ; toute autre valeur → ValueError. |
FORGE_IOT_MQTT_TLS_CA_FILE |
chemin du certificat CA qui valide le broker (ex. /etc/ssl/certs/mosquitto-ca.crt). Optionnel au niveau config, recommandé en usage réel. Vide → None. |
Règles :
- Pour un Mosquitto local pédagogique, TLS reste désactivé (
false, le défaut) : on continue d'utiliserlocalhost:1883en clair.
Voir Mosquitto local. - Pour un broker exposé sur le réseau, TLS doit être préparé :
FORGE_IOT_MQTT_TLS_ENABLED=true, unFORGE_IOT_MQTT_TLS_CA_FILEpointant vers le CA du broker, et le port8883. - Le chemin du fichier CA est masqué dans
repr(IotConfig)(comme le mot de passe et le token), pour ne pas exposer de chemin sensible dans les logs ; les clients le lisent via l'attribut.
TLS branché dans les clients (IOT-MQTT-TLS-CLIENTS-001)
FORGE_IOT_MQTT_TLS_ENABLED=true est désormais réellement consommé par forge iot:doctor --mqtt, forge iot:listen (via le MqttSubscriber) et forge iot:simulate : le client paho appelle client.tls_set(...) avant de se connecter (ca_certs = le fichier CA si fourni, sinon les certificats système).
Le port n'est pas forcé : pour un broker TLS, configure aussi FORGE_IOT_MQTT_PORT (généralement 8883).
Hors périmètre : pas de certificat client (mTLS), pas de tls_insecure_set.
# Broker exposé sur le réseau, MQTT over TLS
FORGE_IOT_MQTT_HOST=mqtt.example.net
FORGE_IOT_MQTT_PORT=8883
FORGE_IOT_MQTT_TLS_ENABLED=true
FORGE_IOT_MQTT_TLS_CA_FILE=/etc/ssl/certs/mosquitto-ca.crt
export FORGE_IOT_MQTT_HOST="mqtt.example.net"
export FORGE_IOT_MQTT_PORT="8883"
export FORGE_IOT_MQTT_TLS_ENABLED="true"
export FORGE_IOT_MQTT_TLS_CA_FILE="/etc/ssl/certs/mosquitto-ca.crt"
forge iot:doctor --mqtt
API Python¶
IotConfig¶
from dataclasses import dataclass
@dataclass(frozen=True)
class IotConfig:
mqtt_host: str
mqtt_port: int
mqtt_topic: str
mqtt_client_id: str
mqtt_username: str | None
mqtt_password: str | None
api_token: str | None = None
mqtt_tls_enabled: bool = False
mqtt_tls_ca_file: str | None = None
Les champs avec défaut (api_token, mqtt_tls_enabled, mqtt_tls_ca_file) garantissent la compatibilité ascendante : les instanciations historiques à 6 champs restent valides.
L'objet est immuable (frozen=True) : le subscriber qui le reçoit ne peut pas le modifier par effet de bord.
load_iot_config()¶
from collections.abc import Mapping
from forge_mvc_iot.config import IotConfig, load_iot_config
# Lecture depuis os.environ
cfg: IotConfig = load_iot_config()
# Lecture depuis un mapping injecté (utile pour les tests)
cfg = load_iot_config({"FORGE_IOT_MQTT_HOST": "broker.lan"})
env=None(défaut) → litos.environ.env=mapping→ lit le mapping fourni, sans toucher àos.environ.
C'est le point d'injection pour les tests ; aucune variable d'environnement réelle n'est nécessaire.
Erreurs levées¶
load_iot_config lève ValueError dans les cas suivants :
| Cas | Message indicatif |
|---|---|
FORGE_IOT_MQTT_HOST présent mais vide |
FORGE_IOT_MQTT_HOST ne peut pas être vide |
FORGE_IOT_MQTT_PORT non convertible en entier |
FORGE_IOT_MQTT_PORT doit être un entier (vu : '...') |
FORGE_IOT_MQTT_PORT hors plage 1..65535 |
FORGE_IOT_MQTT_PORT hors plage 1-65535 (vu : ...) |
FORGE_IOT_MQTT_TOPIC présent mais vide |
FORGE_IOT_MQTT_TOPIC ne peut pas être vide |
Les autres erreurs (broker indisponible, payload invalide, etc.) n'appartiennent pas à ce contrat : elles seront traitées par le subscriber.
Masquage du mot de passe¶
Le repr() d'un IotConfig ne révèle jamais le mot de passe en clair :
>>> cfg = load_iot_config({
... "FORGE_IOT_MQTT_USERNAME": "forge",
... "FORGE_IOT_MQTT_PASSWORD": "s3cr3t",
... })
>>> repr(cfg)
"IotConfig(mqtt_host='localhost', mqtt_port=1883, mqtt_topic='forge/+/+/telemetry', mqtt_client_id='forge-iot', mqtt_username='forge', mqtt_password='***')"
Si le mot de passe est None (pas d'authentification), le champ apparaît tel quel : il n'y a rien à protéger.
Cette protection couvre :
- les traces d'exception affichées au démarrage ;
- les logs qui sérialisent l'objet via
repr()ouf"{cfg!r}"; - les sorties
pprintet la plupart des outils de debug.
Elle ne couvre pas un accès direct à l'attribut (cfg.mqtt_password) ; c'est volontaire : le subscriber a besoin de la valeur en clair pour s'authentifier.
Exemples d'environnement¶
Développement local (Mosquitto sans auth)¶
# env/dev
FORGE_IOT_MQTT_HOST=localhost
FORGE_IOT_MQTT_PORT=1883
FORGE_IOT_MQTT_TOPIC=forge/+/+/telemetry
FORGE_IOT_MQTT_CLIENT_ID=forge-iot-dev
Pas d'identifiants nécessaires : Mosquitto local accepte les connexions anonymes par défaut.
Broker MQTT avec authentification¶
FORGE_IOT_MQTT_HOST=broker.lan
FORGE_IOT_MQTT_PORT=1883
FORGE_IOT_MQTT_TOPIC=forge/atelier/+/telemetry
FORGE_IOT_MQTT_CLIENT_ID=forge-iot-atelier
FORGE_IOT_MQTT_USERNAME=forge
FORGE_IOT_MQTT_PASSWORD=motdepassefort
Le topic est ici restreint au site atelier, utile pour qu'une instance Forge IoT n'écoute qu'un périmètre précis.
Broker MQTT cloud¶
FORGE_IOT_MQTT_HOST=mqtt.example.com
FORGE_IOT_MQTT_PORT=1883
FORGE_IOT_MQTT_TOPIC=forge/+/+/telemetry
FORGE_IOT_MQTT_CLIENT_ID=forge-iot-prod
FORGE_IOT_MQTT_USERNAME=forge-prod
FORGE_IOT_MQTT_PASSWORD=...
Aucun branchement spécial n'est nécessaire : le contrat est identique à un broker local. Pour un broker exposé, active le TLS MQTT (FORGE_IOT_MQTT_TLS_ENABLED, FORGE_IOT_MQTT_TLS_CA_FILE, port 8883).
Les certificats client (mTLS) restent hors périmètre.
Limites itération 1¶
Sont explicitement hors périmètre :
- pas de lecture automatique de
env/devou.env: c'est l'appelant qui charge ces fichiers en amont (par exemple viapython-dotenvoucli.config.load_env()) ; - TLS est branché dans les clients (
IOT-MQTT-TLS-CLIENTS-001) viaclient.tls_set; restent hors périmètre : le certificat client (mTLS), la génération de certificats,tls_insecure_setet la configuration serveur Mosquitto TLS complète ; - pas de ACL Mosquitto : gestion côté broker, hors Forge ;
- pas de gestion de secrets externes (Vault, AWS Secrets Manager) : la config se contente de lire un mapping
str → str; - pas de validation runtime du topic vs contrat MQTT (le pattern
forge/+/+/telemetryest accepté tel quel par Forge IoT, qui s'abonne « bêtement »
au topic fourni) ; - pas de connexion au broker : aucune dépendance
paho-mqtt.
Toute extension passera par un ticket IOT-CONFIG-NNN ultérieur, sans casser le contrat actuel.
Tickets suivants¶
Ce contrat de configuration débloque :
IOT-MQTT-SUBSCRIBER-001: le subscriberpaho-mqttreçoit unIotConfigen argument et n'a plus à connaître les variables d'environnement ;IOT-DOCTOR-001:forge iot:doctoraffichera la configuration effective (avec mot de passe masqué) avant de tester la connexion ;IOT-CONFIG-TLS-001(livré) : a étenduIotConfigavecmqtt_tls_enabled/mqtt_tls_ca_file, sans changer les défauts existants (TLS désactivé par défaut) ;IOT-MQTT-TLS-CLIENTS-001(livré) : branche réellement TLS dansforge iot:doctor --mqtt,forge iot:listenetforge iot:simulate(via le helperforge_mvc_iot.mqtt.tls.configure_tls→client.tls_set, appelé avantconnect).
Voir Architecture Forge IoT pour la liste complète des jalons.