Compare commits

...
Author SHA1 Message Date
gandalf cfe382aed2 fix(waf): le WAF detectait sans jamais pouvoir bannir (ref #1017)
Signale comme « plus de bans malgre attaques massives ». sbxwaf detectait
parfaitement — scanners, sondes, crawlers de reconnaissance — et rien n'en
sortait. Deux defauts independants, tous deux SILENCIEUX.

1. LE JOURNAL DES MENACES ETAIT INECRIVABLE

   waf-threats.log appartenait a secubox:secubox en 0640 ; le service tourne en
   secubox-waf. Chaque ecriture echouait en permission denied. Trois semaines de
   detections perdues, et un tableau de bord fige au 21 juillet — ce qui se lit
   exactement comme « aucune attaque ».

   Le chown porte desormais sur le repertoire ET le fichier : creer le
   repertoire ne suffit pas quand le fichier existe deja, ce qui est le cas de
   toute board en service.

2. LE REPLI cscli ETAIT INEXECUTABLE

   Le pont CrowdSec passe par "cscli decisions add" — le code documente
   pourquoi : la LAPI /v1/alerts rend 500 sur cette version et les jetons
   expirent toutes les heures ; seul ce chemin cree le blocage nft applique par
   le bouncer.

   Mais cscli doit lire /etc/crowdsec/config.yaml, interdit a secubox-waf, et
   NoNewPrivileges=yes aurait de toute facon bloque l'elevation. Un droit sudo
   BORNE au seul verbe "decisions add" est livre avec le paquet, et
   NoNewPrivileges passe a false. Les deux vont ensemble : l'un sans l'autre ne
   sert a rien.

   Le WAF peut bannir, PAS lever un ban : "decisions delete" reste hors de sa
   portee. Un WAF compromis pourrait bannir a tort — nuisance corrigeable a la
   main — mais pas desarmer la board en silence.

CE QUI N'ETAIT PAS EN CAUSE, contrairement a mon premier diagnostic :

- l'adresse de la LAPI. 10.100.0.1:8080 repond bien (403, soit la LAPI rejetant
  une requete non authentifiee). J'allais la remplacer par 127.0.0.1:8080 avant
  de lire le commentaire qui l'ecarte explicitement — VIP DNAT qui rebouclerait
  sur le proxy. Adresse laissee telle quelle.
- la liste blanche de CrowdSec, qui ecarte 98,6 % des lignes nginx. C'est
  normal : set_real_ip_from fonctionne, et ce trafic est celui du navigateur de
  l'operateur revenant par la box.

Verifie sur gk2 : "Decision successfully added" sous l'identite du service ;
journal des menaces en croissance ; 12 detections en 10 minutes, zero erreur
d'ecriture.
2026-08-12 16:15:33 +02:00
4 changed files with 100 additions and 1 deletions
+26
View File
@@ -1,3 +1,29 @@
secubox-waf-ng (1.3.2-1~bookworm1) bookworm; urgency=high
* Le WAF detectait sans jamais pouvoir bannir (ref #1017). Deux defauts
independants, tous deux silencieux.
* Le journal des menaces appartenait a secubox:secubox en 0640 alors que le
service tourne en secubox-waf : chaque ecriture echouait en permission
denied. TROIS SEMAINES de detections perdues, et un tableau de bord fige au
21 juillet — ce qui se lit comme « aucune attaque ». Le repertoire ET le
fichier sont desormais donnes au service au demarrage : creer le repertoire
ne suffit pas quand le fichier existe deja, ce qui est le cas de toute board
en service.
* Le pont CrowdSec passe par `cscli decisions add`, mais cscli doit lire
/etc/crowdsec/config.yaml, interdit a secubox-waf — et NoNewPrivileges=yes
aurait de toute facon bloque l'elevation. Un droit sudo BORNE au seul verbe
`decisions add` est livre avec le paquet, et NoNewPrivileges passe a false.
Les deux vont ensemble : l'un sans l'autre ne sert a rien.
* Le WAF peut donc bannir, mais pas LEVER un ban : `decisions delete` reste
hors de sa portee. Un WAF compromis pourrait bannir a tort — nuisance
corrigeable — mais pas desarmer la board en silence.
* L'adresse de la LAPI n'etait PAS en cause, contrairement a ce que laissait
croire la liste des sockets en ecoute : 10.100.0.1:8080 repond bien (403,
soit la LAPI rejetant une requete non authentifiee). Elle est laissee telle
quelle, l'auteur ayant documente pourquoi ne pas viser la boucle locale.
-- Gerald Kerma <devel@cybermind.fr> Wed, 12 Aug 2026 16:20:00 +0200
secubox-waf-ng (1.3.1-1~bookworm1) bookworm; urgency=medium
* Une page peut REFUSER la banniere de sante, par
+7
View File
@@ -54,6 +54,13 @@ override_dh_auto_clean:
rm -f $(CURDIR)/sbxwaf
rm -rf _gocache _gopath
override_dh_auto_install:
dh_auto_install
# Le droit sudo borne qui permet au WAF de bannir (#1017). Sans lui,
# sbxwaf detecte sans jamais pouvoir appeler cscli, et rien ne le signale.
install -d debian/secubox-waf-ng/etc/sudoers.d
install -m 0440 sudoers/secubox-waf-ng debian/secubox-waf-ng/etc/sudoers.d/secubox-waf-ng
override_dh_installsystemd:
# #896 reconcile: ship the SINGLE hardened unit (secubox-waf-ng.service,
# :8085) — it replaces the crash-looping worker@ fan-out template, which is
@@ -0,0 +1,39 @@
# /etc/sudoers.d/secubox-waf-ng — le WAF doit pouvoir bannir (#1017).
#
# POURQUOI CE DROIT EXISTE
#
# sbxwaf detecte les scanners, sondes et crawlers de reconnaissance, puis
# demande un bannissement. Le pont passe par `cscli decisions add` : le code du
# WAF documente pourquoi ce chemin plutot que la LAPI — /v1/alerts rend 500 sur
# cette version, et les jetons expirent toutes les heures. C'est ce chemin qui
# cree reellement le blocage nft applique par le bouncer.
#
# Or `cscli` doit lire /etc/crowdsec/config.yaml, interdit au compte
# `secubox-waf` sous lequel tourne le service. Sans ce droit, le WAF detectait
# en continu sans jamais pouvoir bannir — et rien ne le signalait : le tableau
# de bord affichait simplement peu de bans, ce qui se lit comme « peu
# d'attaques ».
#
# CE QUE CE DROIT N'EST PAS
#
# Ce n'est pas un acces root au systeme, ni meme a CrowdSec. Un seul verbe est
# autorise : `decisions add`. Le WAF ne peut ni lever un ban (`decisions
# delete`), ni lire la configuration, ni toucher aux machines, aux bouncers, aux
# collections ou aux scenarios.
#
# La distinction compte : un WAF compromis pourrait alors bannir a tort — une
# nuisance, corrigeable a la main — mais pas LEVER les bans en place, ce qui
# aurait desarme la board en silence.
#
# NoNewPrivileges=false est requis dans l'unite pour que ce droit soit
# seulement utilisable. Les deux vont ensemble : l'un sans l'autre ne sert a
# rien.
Cmnd_Alias SBX_WAF_BAN = /usr/bin/cscli decisions add *
secubox-waf ALL=(root) NOPASSWD: SBX_WAF_BAN
# Les journaux de securite doivent nommer l'auteur reel de la decision, pas le
# compte de service : sans cela, une decision contestee ne se remonte pas.
Defaults!SBX_WAF_BAN !requiretty
Defaults!SBX_WAF_BAN log_output
@@ -55,6 +55,16 @@ Group=secubox-waf
ExecStartPre=+/bin/mkdir -p /var/log/secubox/cookie-audit
ExecStartPre=+/bin/chown -R secubox-waf:secubox-waf /var/log/secubox/cookie-audit
ExecStartPre=+/bin/chmod 0750 /var/log/secubox/cookie-audit
# LE JOURNAL DES MENACES DOIT APPARTENIR AU SERVICE QUI L'ECRIT (#1017).
# Il etait a secubox:secubox en 0640 alors que sbxwaf tourne en secubox-waf :
# chaque ecriture echouait en « permission denied », et TROIS SEMAINES de
# detections ont ete perdues sans que rien ne le signale — le tableau de bord
# affichait simplement une image figee, ce qui se lit comme « aucune attaque ».
# Le chown porte sur le repertoire ET le fichier : creer le repertoire ne suffit
# pas quand le fichier existe deja, ce qui est le cas de toute board en service.
ExecStartPre=+/bin/mkdir -p /var/log/secubox/waf
ExecStartPre=+/bin/chown -R secubox-waf:secubox-waf /var/log/secubox/waf
ExecStartPre=+/bin/chmod 0750 /var/log/secubox/waf
ExecStart=/usr/sbin/sbxwaf \
--listen 127.0.0.1:8085 \
--routes /etc/secubox/waf/haproxy-routes.json \
@@ -68,6 +78,10 @@ ExecStart=/usr/sbin/sbxwaf \
# --crowdsec-url is the real CrowdSec LAPI (LXC-bridge gateway 10.100.0.1:8080),
# NOT 127.0.0.1:8080 (the nft DNAT VIP that would self-loop into this proxy).
#
# Verifie le 2026-08-12 (#1017) : cette adresse repond bien (403, soit la LAPI
# qui rejette une requete non authentifiee). L'adresse n'a jamais ete en cause
# dans l'absence de bans — c'est l'execution de cscli qui l'etait.
Restart=on-failure
RestartSec=5
@@ -76,7 +90,20 @@ RestartSec=5
# Same posture as the worker@ template (secubox-mesh convention, §6 CSPN), minus
# RuntimeDirectory (see header). /run/secubox is READ-WRITE so the client connect
# to waker.sock works under ProtectSystem=strict.
NoNewPrivileges=yes
# NoNewPrivileges=false EST REQUIS (#1017), et c'est un choix, pas un oubli.
#
# Le pont CrowdSec passe par "cscli decisions add" — le code le documente :
# la LAPI /v1/alerts rend 500 sur cette version et les jetons expirent toutes
# les heures ; seul ce chemin cree reellement le blocage nft applique par le
# bouncer.
#
# Or cscli doit lire /etc/crowdsec/config.yaml, interdit a secubox-waf. Avec
# NoNewPrivileges=yes, meme un droit sudo reste inutilisable : le WAF detectait
# donc en continu sans jamais pouvoir bannir, et rien ne le signalait.
#
# L'elevation est BORNEE au seul "cscli decisions add" par le droit sudo livre
# avec ce paquet (/etc/sudoers.d/secubox-waf-ng) — pas un blanc-seing.
NoNewPrivileges=false
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes