Files
Fail2banMqttActionBanishment/docs/deployment.md
T
2026-07-20 11:05:07 +02:00

7.9 KiB

Déploiement en production

Cette page détaille le déploiement d'un ensemble master + noeuds en production, au-delà du premier noeud isolé couvert par Installation.

Vue d'ensemble

Trois façons d'utiliser ce projet, du plus simple au plus complet — la même installation de base (sudo make install) sert de socle aux trois :

  • Solo : sudo make install, seul. Ce serveur détecte et bannit localement, sans rien centraliser ailleurs. Suffisant pour protéger un unique VPS. Le service de relais vers un master (fail2ban-emitter-master-client) tourne quand même, mais réessaie silencieusement en boucle tant qu'aucun master n'est configuré (aucune gêne, Restart=on-failure).
  • Solo + master : sudo make install puis sudo make install-master, sur le même serveur. Ce VPS protège ses propres services (comme en mode solo) et sert en plus de master, prêt à accueillir d'autres noeuds plus tard — utile pour démarrer seul aujourd'hui sans fermer la porte à un second serveur demain.
  • Master + noeuds (plusieurs VPS) : un serveur en mode solo+master, et un ou plusieurs autres serveurs, chacun installé en solo puis rattaché à ce master via l'auto-inscription par jeton (ci-dessous). Le master lui-même reste un noeud comme les autres (il détecte et bannit localement en plus de centraliser la décision) — passer de "solo+master tout seul" à "master + noeuds" ne demande donc aucune réinstallation du master, seulement d'y ajouter des noeuds au fur et à mesure.

Ordre recommandé pour passer à plusieurs noeuds :

  1. Déployer le master (une seule fois — solo+master si ce même serveur doit aussi se protéger lui-même, ce qui est le cas courant).
  2. Sur le master, émettre un jeton pour chaque nouveau noeud.
  3. Sur chaque nouveau noeud, s'auto-inscrire avec ce jeton.

Déployer le master

sudo make install-master

Nécessite un premier sudo make install déjà effectué sur cette même machine. Provisionne :

  • une CA privée dédiée (scripts/master-ca-init.sh), distincte du certificat Let's Encrypt qui sert l'identité TLS du broker — elle ne sert qu'à signer les certificats clients des noeuds (authentification mTLS) ;
  • le listener Mosquitto public (port 8883, TLS, MQTT_MASTER_DOMAIN dans .env) avec ACL par noeud ;
  • l'ingestion côté master (fail2ban-emitter-master-listen, tourne avec sa propre identité mTLS master-internal, générée automatiquement).

MQTT_MASTER_DOMAIN doit pointer vers un domaine public réel (DNS déjà en place) — le broker obtient son certificat Let's Encrypt à ce nom.

Auto-inscription d'un noeud (recommandé)

Le problème

Ajouter un noeud à la main demanderait de générer une clé + un certificat sur le master, puis de copier ces fichiers vers le nouveau noeud (scp), de les renommer correctement, et d'éditer son .env — plusieurs étapes manuelles, cross-machine, et la clé privée transiterait par le réseau (jamais une bonne pratique).

Le modèle

Le master émet un jeton à usage unique, à courte durée de vie (1h par défaut). Le nouveau noeud utilise ce jeton pour s'inscrire lui-même :

  • Le noeud génère sa propre clé privée localement et ne l'envoie jamais — seule une CSR (demande de signature) part vers le master.
  • Le jeton n'est jamais stocké en clair côté master : seul son hash SHA-256 est persisté (même logique qu'un hachage de mot de passe).
  • Le jeton est marqué utilisé de façon atomique dès sa validation, et rendu automatiquement si une étape suivante échoue (CSR incohérente, échec de signature) — un accroc transitoire ne force pas à réémettre un jeton entièrement nouveau.
  • Le CN de la CSR reçue doit correspondre au nom de noeud déclaré au moment de l'émission du jeton — un jeton volé ne permet pas de réclamer un autre nom.
  • Toute erreur renvoyée au client est volontairement générique ("jeton invalide ou expiré"), quelle que soit la cause réelle — le détail (jeton inconnu, expiré, déjà utilisé, CN incohérent, échec de signature) part uniquement dans les journaux du master, pour ne donner aucun indice à un tiers qui sonderait l'endpoint.
  • Le transport est le tableau de bord HTTPS du master (déjà exposé publiquement, nginx + Let's Encrypt) : aucun nouveau port ni service à ouvrir, et ça résout le problème d'oeuf-et-poule (un noeud ne peut pas utiliser mTLS MQTT pour obtenir... son premier certificat mTLS).

Étape par étape

Sur le master :

make join-token NODE=nom-du-noeud

Affiche le jeton en clair une seule fois, avec la commande complète à lancer sur le nouveau noeud (jeton perdu : simplement en émettre un nouveau, celui-ci reste valide indépendamment jusqu'à expiration).

Sur le nouveau noeud (déjà passé par sudo make install au préalable) :

sudo make join MASTER=https://<domaine-du-master> NODE=nom-du-noeud TOKEN=<jeton>
sudo make install

Le second make install applique la configuration reçue (MQTT_MASTER_NODE_NAME dans .env, certificat en place) et redémarre les services.

Ce qui se passe en coulisse

[nouveau noeud]                              [master]
  génère clé + CSR (locale, jamais transmise)
  POST /api/join/ {node_name, token, csr} ──►
                                              vérifie le jeton (hash, expiration, usage unique)
                                              vérifie le CN de la CSR == node_name
                                              signe la CSR via la CA privée
                                              ajoute l'entitlement ACL pour ce noeud
                                              redémarre mosquitto
                        {cert, ca_cert} ◄────
  écrit cert/ca_cert, met à jour .env

Dépannage

  • "Jeton invalide ou expiré" — le message est volontairement générique. Causes possibles : jeton déjà utilisé (relancer make join-token pour en émettre un nouveau), jeton expiré (durée par défaut 1h, ajustable via make join-token NODE=... TTL=<minutes>), ou faute de frappe dans le nom de noeud (doit être identique entre l'émission du jeton et make join).
  • Le nouveau noeud doit pouvoir joindre le master en HTTPS (443) — un pare-feu qui bloquerait la sortie empêcherait l'auto-inscription.

Bases de données

SQLite est le défaut, sans rien à configurer. Deux alternatives :

sudo make install-mariadb   # provisionne un serveur MariaDB local + base/utilisateur dédiés
sudo make install           # à relancer ensuite pour appliquer les migrations sur la nouvelle base

PostgreSQL est supporté côté configuration (DB_ENGINE=postgresql dans .env, driver emitter/requirements-postgresql.txt) mais sans sous-commande de provisionnement automatique pour l'instant — serveur et base à créer manuellement avant de basculer DB_ENGINE.

Portage d'une base vers une autre (dump indépendant du moteur, via manage.py dumpdata/loaddata) :

make dump-db   # écrit emitter/db-dump-<date>.json (exclu de git, contient des données réelles)
# ... bascule DB_ENGINE, migrate ...
emitter/.venv/bin/python emitter/manage.py loaddata <fichier>.json

Reverse proxy nginx + TLS public

sudo make install-nginx   # lit DASHBOARD_DOMAIN dans .env

Obtient un certificat Let's Encrypt et déploie un vhost nginx devant le tableau de bord (WebSocket inclus) — nécessaire pour un accès public ; sans ça, le tableau de bord n'est accessible qu'en local/VPN.

Alternative à systemd : supervisor

sudo make install-supervisor     # bascule les 4 services de l'émetteur vers supervisor
sudo make uninstall-supervisor   # retour arrière (réactive systemd)

Mise à jour

git pull
sudo make install

Réapplique dépendances/migrations et redémarre les services concernés, sur n'importe quel noeud (y compris le master).