[Unit] Description=Fail2banActionBanisher - émetteur Django (ASGI/Daphne) After=network.target redis-server.service Requires=redis-server.service [Service] Type=simple User=__EMITTER_USER__ Group=__EMITTER_GROUP__ WorkingDirectory=__EMITTER_DIR__ EnvironmentFile=__ENV_FILE__ Environment=DJANGO_SETTINGS_MODULE=config.settings.production Environment=PYTHONUNBUFFERED=1 # Loopback uniquement par défaut : éditer le bind ici pour un accès distant # (ex. IP du VPN de confiance), pas d'exposition publique directe prévue. ExecStart=__EMITTER_DIR__/.venv/bin/daphne -b 127.0.0.1 -p 8050 config.asgi:application Restart=on-failure RestartSec=5 # Pas de NoNewPrivileges=true ici (retiré 2026-07-18) : depuis l'endpoint # /api/join/ (banevents/views.py::join_node, auto-inscription des noeuds # par jeton), ce process a structurellement besoin d'escalader via sudo # (sudo scripts/sign-node-csr.sh ...). Même piège déjà rencontré sur # fail2ban-emitter-master-client.service : NoNewPrivileges bloque tout # mécanisme de gain de privilège au niveau execve, y compris le setuid de # sudo lui-même — sudo échoue alors avec "the 'no new privileges' flag is # set", quels que soient les droits sudoers. Repéré en conditions réelles # (premier vrai test de /api/join/), pas en lecture de code. ProtectSystem=strict PrivateTmp=true # /etc/mosquitto/master-ca et master-acl.conf : sign-node-csr.sh (appelé # via sudo par /api/join/) écrit un fichier .srl à côté de ca.crt # (-CAcreateserial d'openssl) ET ajoute l'entitlement ACL du nouveau # noeud — sudo ne crée pas de nouveau mount namespace, le sudo'd child # hérite du même ProtectSystem=strict que ce process, donc ces deux # chemins doivent être listés ici aussi, pas seulement __EMITTER_DIR__. # Repéré en conditions réelles (premier vrai test de /api/join/, échec # silencieux : openssl/l'ajout ACL échouaient sans qu'aucun message ne # remonte jusqu'au subprocess.run() de la vue Django). # # Préfixe "-" obligatoire sur les deux chemins mosquitto : CE service # tourne sur TOUS les noeuds (unité générique), pas seulement le master — # /etc/mosquitto/master-ca et master-acl.conf n'existent QUE sur le # master (déployés par install-master). ReadWritePaths= exige que le # chemin existe déjà au moment de construire le mount namespace ; sans # "-", le service refuse de démarrer PARTOUT ailleurs que sur le master # ("Failed to set up mount namespacing: ... No such file or directory", # status 226/NAMESPACE) — cassé en conditions réelles sur un vrai noeud # (linuxtarn-vps1) le jour même du déploiement. "-" en tête dit à systemd # d'ignorer silencieusement l'entrée si le chemin est absent. # /var/spool/exim4 et /var/log/exim4 : django-sendmail-backend invoque # /usr/sbin/sendmail directement (voir config/settings/base.py) pour # l'email d'inscription à la communauté (banevents/views.py:: # community_landing, mail_admins) — même piège que ci-dessus, repéré en # conditions réelles : "Cannot open main log file ... Permission denied" # + "Failed to create spool file ... Read-only file system". Préfixe "-" # : un noeud sans exim4 installé ne doit pas empêcher ce service de # démarrer. ReadWritePaths=__EMITTER_DIR__ -/etc/mosquitto/master-ca -/etc/mosquitto/master-acl.conf -/var/spool/exim4 -/var/log/exim4 [Install] WantedBy=multi-user.target