mirror of
https://github.com/deunix-educ/Fail2banMqttActionBanishment.git
synced 2026-08-24 03:11:58 +02:00
First commit
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# GABARIT réutilisable pour protéger une NOUVELLE appli contre le
|
||||
# brute-force applicatif — n'importe quelle appli sur ce VPS, pas
|
||||
# seulement ce projet (trouvé utile après un résidu de jail appartenant à
|
||||
# une autre appli Django sur ce même serveur partagé, cf. CONTEXT.md).
|
||||
# Ce fichier n'est PAS déployé tel quel (extension .template, ignorée par
|
||||
# la boucle de copie générique d'install.sh::deploy_fail2ban) : c'est un
|
||||
# modèle à copier et adapter. Exemple concret déjà en place :
|
||||
# emitter-admin-auth.conf + emitter/banevents/signals.py (Django +
|
||||
# django-axes).
|
||||
#
|
||||
# Mode d'emploi :
|
||||
# 1. Faire journaliser à l'appli une ligne EXACTEMENT au format attendu
|
||||
# par filter.d/app-auth.conf (générique, partagé par toutes les
|
||||
# jails app-auth-*, rien à écrire côté filtre) :
|
||||
# Verrouillage <NOM_APPLI> après échecs répétés depuis <IP> (...)
|
||||
# <NOM_APPLI> = un seul mot (\S+ côté filtre), ex. "émetteur",
|
||||
# "webmail". L'appli décide QUAND verrouiller (son propre seuil
|
||||
# applicatif, cf. AXES_FAILURE_LIMIT pour l'exemple django-axes) —
|
||||
# fail2ban ne fait que réagir à cette décision déjà prise (d'où
|
||||
# maxretry=1 ci-dessous : chaque ligne EST déjà la décision, pas la
|
||||
# peine de recompter un 2e seuil derrière).
|
||||
# IMPORTANT — timestamp en tête de ligne : marquer explicitement le
|
||||
# fuseau (ex. suffixe "Z" ISO 8601, UTC), voir le commentaire dans
|
||||
# filter.d/app-auth.conf pour le pourquoi (piège vécu avec Django,
|
||||
# TIME_ZONE='UTC' + time.tzset() : sans marqueur, fail2ban ignore
|
||||
# silencieusement les lignes fraîches juste après son propre
|
||||
# redémarrage si le fuseau système du VPS diffère d'UTC).
|
||||
# 2. Copier ce fichier vers jail.d/<nom-appli>-auth.conf, remplacer
|
||||
# __APP_NAME__ (nom de la jail, unique) et __APP_AUTH_LOGPATH__
|
||||
# (chemin réel du fichier de log).
|
||||
# 3. Si ce chemin dépend du VPS (répertoire de déploiement différent
|
||||
# d'un serveur à l'autre — le cas courant) : NE PAS le laisser dans
|
||||
# la boucle de copie générique d'install.sh::deploy_fail2ban (même
|
||||
# piège que coturn.conf/emitter-admin-auth.conf, voir CONTEXT.md).
|
||||
# Ajouter plutôt une fonction deploy_<nom>_jail dédiée (sed sur le
|
||||
# placeholder), sur le modèle de deploy_emitter_admin_auth_jail.
|
||||
# 4. S'assurer que le fichier de log existe déjà (touch) avant le
|
||||
# premier démarrage de fail2ban après déploiement — un logpath
|
||||
# inexistant fait planter fail2ban au chargement (incident coturn,
|
||||
# voir ROADMAP.md Phase 1.b).
|
||||
[__APP_NAME__-auth]
|
||||
enabled = true
|
||||
backend = auto
|
||||
filter = app-auth
|
||||
logpath = __APP_AUTH_LOGPATH__
|
||||
maxretry = 1
|
||||
findtime = 1h
|
||||
bantime = 24h
|
||||
@@ -0,0 +1,19 @@
|
||||
# logpath : chemin réel du log coturn sur CE VPS, substitué par install.sh
|
||||
# (deploy_fail2ban) depuis "log-file=" dans /etc/turnserver.conf s'il existe
|
||||
# — jamais une valeur codée en dur partagée entre noeuds. Un chemin diffère
|
||||
# réellement d'un VPS à l'autre selon la config coturn locale (vécu :
|
||||
# /var/log/coturn/turnserver.log sur l'un, /var/log/turnserver.log sur
|
||||
# l'autre, même paquet Debian). Si coturn n'est pas installé sur ce noeud,
|
||||
# install.sh désactive cette jail plutôt que de laisser un chemin invalide
|
||||
# faire planter fail2ban au démarrage ("Have not found any log file").
|
||||
[coturn]
|
||||
enabled = true
|
||||
backend = auto
|
||||
filter = coturn
|
||||
logpath = __COTURN_LOGPATH__
|
||||
port = 3478
|
||||
protocol = tcp
|
||||
banaction = f2b-iptables-allports
|
||||
maxretry = 5
|
||||
findtime = 5m
|
||||
bantime = 24h
|
||||
@@ -0,0 +1,18 @@
|
||||
# Désactivée (2026-07-17) : ne protège rien de réel. Le filtre référencé
|
||||
# (filter.d/django-auth.conf) n'existe pas dans ce dépôt — sur le VPS où
|
||||
# ce jail.d a été trouvé déployé, un fichier du même nom existe bien, mais
|
||||
# c'est un résidu appartenant à une autre appli Django hébergée sur ce VPS
|
||||
# partagé (pas ce projet), et logpath (/var/log/django/*.log) pointe vers
|
||||
# un fichier vide que rien n'alimente. Notre propre émetteur Django
|
||||
# (emitter/) n'a aucune config LOGGING et n'écrit jamais là — l'ancien
|
||||
# commentaire "bloqué sur Phase 2" n'a plus de sens, Phase 2 est terminée
|
||||
# depuis longtemps et ne visait de toute façon pas ce fichier. À réactiver
|
||||
# seulement avec un vrai filtre + logpath réels si un jour ce projet doit
|
||||
# protéger la connexion admin de l'émetteur lui-même.
|
||||
[django-auth]
|
||||
enabled = false
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/django/*.log
|
||||
maxretry = 3
|
||||
bantime = 1h
|
||||
@@ -0,0 +1,21 @@
|
||||
# logpath : chemin réel du log axes sur CE VPS (emitter/logs/admin-auth.log
|
||||
# dans le checkout), substitué par install.sh (deploy_fail2ban) — jamais
|
||||
# une valeur codée en dur, le checkout vit sous un $HOME différent par VPS
|
||||
# (même piège que coturn.conf/master-sync : voir CONTEXT.md).
|
||||
#
|
||||
# maxretry=1 : chaque ligne journalisée EST déjà la décision de
|
||||
# verrouillage d'axes (AXES_FAILURE_LIMIT, cf. config/settings/base.py) —
|
||||
# fail2ban n'a pas à recompter un second seuil, il bannit dès la première
|
||||
# occurrence.
|
||||
#
|
||||
# filter = app-auth (générique, pas emitter-admin-auth) : premier
|
||||
# consommateur du gabarit réutilisable par toute appli du VPS, voir
|
||||
# filter.d/app-auth.conf et jail.d/app-auth.conf.template.
|
||||
[emitter-admin-auth]
|
||||
enabled = true
|
||||
backend = auto
|
||||
filter = app-auth
|
||||
logpath = __EMITTER_ADMIN_AUTH_LOGPATH__
|
||||
maxretry = 1
|
||||
findtime = 1h
|
||||
bantime = 24h
|
||||
@@ -0,0 +1,15 @@
|
||||
# Jail dédiée à la commande ESCALATE (Phase 4) — jamais déclenchée par un
|
||||
# log, uniquement par injection manuelle (fail2ban-client set banip)
|
||||
# depuis master_client.py sur réception d'un ESCALATE (auto-publié par
|
||||
# master_listen.py::correlation_rule_escalate quand une IP est bannie
|
||||
# indépendamment sur plusieurs noeuds distincts, cf. ROADMAP.md).
|
||||
# banaction = f2b-iptables-allports, bantime = -1 (permanent) : contraire
|
||||
# à master-sync (24h), une IP qui frappe plusieurs noeuds indépendamment
|
||||
# n'a pas droit à un simple ban temporaire.
|
||||
[master-escalate]
|
||||
enabled = true
|
||||
filter = master-sync
|
||||
logpath = /var/log/fail2ban.log
|
||||
banaction = f2b-iptables-allports
|
||||
bantime = -1
|
||||
maxretry = 999999
|
||||
@@ -0,0 +1,13 @@
|
||||
# Jail dédiée à la commande RATE_LIMIT (Phase 4) — jamais déclenchée par
|
||||
# un log, uniquement par injection manuelle (fail2ban-client set banip)
|
||||
# depuis master_client.py sur réception de RATE_LIMIT (déclenchée à la
|
||||
# main via manage.py publish_command, cf. master-sync.conf pour le même
|
||||
# schéma). banaction = f2b-iptables-hashlimit : throttle (10 req/s, burst
|
||||
# 20) au lieu d'un blocage total — cas limite, faux positifs probables.
|
||||
[master-ratelimit]
|
||||
enabled = true
|
||||
filter = master-sync
|
||||
logpath = /var/log/fail2ban.log
|
||||
banaction = f2b-iptables-hashlimit
|
||||
bantime = 1h
|
||||
maxretry = 999999
|
||||
@@ -0,0 +1,12 @@
|
||||
# Jail dédiée à la propagation SYNC_BAN reçue du master (Phase 3/4) —
|
||||
# jamais déclenchée par un log, seulement par injection manuelle
|
||||
# (fail2ban-client set master-sync banip <ip>) depuis master_client.py.
|
||||
# banaction = f2b-iptables-allports : un SYNC_BAN bloque l'IP sur tous les
|
||||
# ports, cohérent avec la menace globale qu'il signale (vue ailleurs).
|
||||
[master-sync]
|
||||
enabled = true
|
||||
filter = master-sync
|
||||
logpath = /var/log/fail2ban.log
|
||||
banaction = f2b-iptables-allports
|
||||
bantime = 1d
|
||||
maxretry = 999999
|
||||
@@ -0,0 +1,13 @@
|
||||
[mosquitto]
|
||||
enabled = true
|
||||
backend = auto
|
||||
filter = mosquitto
|
||||
logpath = /var/log/mosquitto/mosquitto.log
|
||||
port = 8883,8884
|
||||
protocol = tcp
|
||||
banaction = f2b-iptables-multiport
|
||||
# maxretry élevé car les clients MQTT se reconnectent légitimement
|
||||
# Réduire à 5 si tu utilises l'approche B (plugin avec IP sur échec auth)
|
||||
maxretry = 15
|
||||
findtime = 1m
|
||||
bantime = 1h
|
||||
@@ -0,0 +1,76 @@
|
||||
[nginx-http-auth]
|
||||
enabled = true
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*error.log
|
||||
|
||||
[nginx-req-limit]
|
||||
enabled = true
|
||||
backend = auto
|
||||
filter = nginx-limit-req
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*error.log
|
||||
findtime = 30s
|
||||
maxretry = 5
|
||||
bantime = 10m
|
||||
|
||||
[nginx-botsearch]
|
||||
enabled = true
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*error.log
|
||||
|
||||
[nginx-noscript]
|
||||
enabled = true
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*access.log
|
||||
maxretry = 3
|
||||
findtime = 5m
|
||||
bantime = 15m
|
||||
|
||||
[nginx-unauthorized]
|
||||
enabled = true
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*access.log
|
||||
maxretry = 3
|
||||
findtime = 5m
|
||||
bantime = 15m
|
||||
|
||||
[nginx-badbots]
|
||||
enabled = true
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*access.log
|
||||
maxretry = 1
|
||||
findtime = 1h
|
||||
bantime = 24h
|
||||
|
||||
[nginx-nohome]
|
||||
enabled = true
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*access.log
|
||||
maxretry = 5
|
||||
findtime = 5m
|
||||
bantime = 15m
|
||||
|
||||
[nginx-noproxy]
|
||||
enabled = true
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*access.log
|
||||
maxretry = 3
|
||||
findtime = 5m
|
||||
bantime = 15m
|
||||
|
||||
# Activer si trop de 404/403 en dehors des autres filtres
|
||||
[nginx-4xx]
|
||||
enabled = false
|
||||
backend = auto
|
||||
port = http,https
|
||||
logpath = /var/log/nginx/*.log
|
||||
maxretry = 10
|
||||
findtime = 5m
|
||||
bantime = 15m
|
||||
@@ -0,0 +1,14 @@
|
||||
[openvpn]
|
||||
enabled = true
|
||||
backend = auto
|
||||
filter = openvpn
|
||||
# Adapter le chemin selon la config OpenVPN (log ou log-append)
|
||||
logpath = /var/log/openvpn/openvpn.log
|
||||
/var/log/openvpn.log
|
||||
port = 41194
|
||||
protocol = udp
|
||||
# Bannir tous les ports (pas seulement VPN) car la menace est globale
|
||||
banaction = f2b-iptables-allports
|
||||
maxretry = 3
|
||||
findtime = 5m
|
||||
bantime = 24h
|
||||
@@ -0,0 +1,25 @@
|
||||
# Récidive — escalade automatique des bans
|
||||
# Les jails recidive-* sont exclus de la détection croisée par le filtre
|
||||
# (ignoreregex = \[recidive.*\])
|
||||
|
||||
# Niveau 1 : 3 bans en 24h → 24h de ban
|
||||
[recidive]
|
||||
enabled = true
|
||||
filter = recidive-filter
|
||||
logpath = /var/log/fail2ban.log
|
||||
action = f2b-iptables-allports
|
||||
f2b-mqtt-action-banisher
|
||||
findtime = 1d
|
||||
bantime = 1d
|
||||
maxretry = 3
|
||||
|
||||
# Niveau 2 : 3 bans en 1 semaine → ban permanent
|
||||
[recidive-hard]
|
||||
enabled = true
|
||||
filter = recidive-filter
|
||||
logpath = /var/log/fail2ban.log
|
||||
action = f2b-iptables-allports
|
||||
f2b-mqtt-action-banisher
|
||||
findtime = 1w
|
||||
bantime = -1
|
||||
maxretry = 3
|
||||
@@ -0,0 +1,15 @@
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = 22
|
||||
maxretry = 3
|
||||
bantime = 24h
|
||||
|
||||
[pam-generic]
|
||||
enabled = true
|
||||
maxretry = 3
|
||||
# facility 10 = auth/security (PAM, su, sudo…)
|
||||
journalmatch = SYSLOG_FACILITY=10
|
||||
|
||||
[mysqld-auth]
|
||||
enabled = true
|
||||
journalmatch = _SYSTEMD_UNIT=mariadb.service
|
||||
Reference in New Issue
Block a user