CrowdSec n'a rien décidé pendant l'aspiration de la forge du 2026-10-08 parce que l'agent ne lisait aucune ligne de Traefik (racine Docker de pi1 non montée) ; et même en lisant, aucun scénario installé n'aurait mordu. Cette PR monte la bonne racine, ajoute deux scénarios réservés à la forge (ban 24 h, portée Ip) et met le foyer en liste blanche — rejoués hors trafic, sabotage compris.
Le constat chiffré (journal Traefik du 2026-10-08, fenêtre 14:37 → 15:58, 81 min)
Coût par famille de pages, tout le trafic de la forge : /compare/ médiane 9,9 s ; /commit/ 4,2 s (p90 14 s) ; /blame/ 2,5 s (p90 10,7 s) ; /src/commit/ 2,5 s ; /commits/ 2,3 s ; /raw/ 0,9 s.
Pourquoi rien n'a mordu — mesuré, dans l'ordre
L'agent ne lisait rien. Sur pi1, /var/log/containers/traefik-…log → /var/log/pods/…/traefik/0.log → /mnt/arcodange/docker/containers/<id>/<id>-json.log. Le chart ne monte que /var/log et /var/lib/docker/containers (l'ancienne racine, plus écrite depuis le 2026-04-07). Dans le pod :
head sur le lien → No such file or directory ; les 54 journaux de /var/log/pods pointent tous sous /mnt/arcodange/docker ;
journal de l'agent au démarrage (2026-09-26 17:56) : Error setting up tail for /var/log/containers/traefik-67bdf6f97d-…log: unable to read … ;
cscli metrics : tables vides ; /metrics n'expose même pas cs_filesource_hits_total (zéro ligne lue) ;
LAPI : sur les 7 jours que garde la base (flush.max_age: 7d), 2 alertes, les deux bans manuels.
Même en lisant, aucun scénario n'aurait mordu. J'ai rejoué les 2 h 45 de journal réel (11 670 lignes, toutes adresses) dans un CrowdSec v1.7.0 local avec le hub copié de l'agent du cluster (mêmes collections, mêmes versions) : 7 alertes, toutes sur le routeur - (des scanners sur des hôtes inconnus), zéro sur la forge. Les raisons :
crowdsecurity/http-crawl-non_statics (v0.7) compte par adresse (groupby: source_ip + target_fqdn), capacité 40, fuite 0,5 s : il faut plus de 2 pages distinctes par seconde, soutenues, pour déborder. La plage 16.216.88.0/24 répartit sa charge sur 74 adresses (9 req/min au plus chacune) ; 74.7.227.0/24 tourne à 0,4 req/s.
crowdsecurity/http-logs range .TS parmi les fichiers statiques (pensé pour la vidéo MPEG-TS) : les pages TypeScript de mediabunny n'entrent même pas dans le seau — 957 des 4 389 requêtes de la plage (dont 699 .ts).
cscli explain joué dans l'agent du cluster sur 5 lignes réelles des deux plages : docker-logs → traefik-logs → dateparse-enrich, geoip-enrich, http-logs, whitelists : tout se parse ; les 2 lignes en .ts ne vont dans aucun seau (static_ressource: true), les 3 autres seulement dans http-crawl-non_statics.
L'adresse vue est la bonne. CrowdSec lit remote_addr dans la ligne CLF de Traefik, c'est-à-dire le client résolu par Traefik (entryPoints.web.forwardedHeaders.trustedIPs=10.42.0.0/16), pas Cloudflare ni le pod cloudflared : cscli explain → evt.Meta.source_ip : 16.216.88.176. Le videur, lui, voit aussi la vraie adresse alors que son forwardedHeadersTrustedIPs vaut ['10.0.10.23/32','10.0.20.0/24'] et son clientTrustedIPs['192.168.1.0/24','10.42.0.0/16'] : c'est donc bien Traefik qui la résout en amont.
Un ban de PLAGE n'est pas appliqué par ce videur (constaté par l'orchestrateur sur les bans manuels : ServeHTTP:Get ip:16.216.88.122 isBanned:false cache:miss, 120 requêtes servies en 2 min ; les 512 adresses ont été réimportées une par une). La cause, lue dans le code : en mode stream, le plugin crowdsec-bouncer-traefik-plugin (v1.3.3 ici, .vendored-version du volume de plugins) range chaque décision sous decision.Value tel quel et cherche l'adresse du client par égalité exacte — aucune correspondance de plage. C'est toujours le cas sur sa branche principale (relu le 2026-10-08 ; rien dans les notes de version jusqu'à v1.8.0-alpha). Vérifié sur une LAPI v1.7.0 locale : /v1/decisions?ip=203.0.113.7&banned=true (mode live) rend bien la décision Range 203.0.113.0/24, tandis que /v1/decisions/stream la livre comme la valeur "203.0.113.0/24". → Les scénarios de cette PR gardent la portée Ip. Passer le videur en live réglerait les plages au prix d'un appel LAPI par adresse nouvelle (hors de ce dépôt : rôle crowdsec de factory).
La règle
agent.extraVolumes / extraVolumeMounts : /mnt/arcodange/docker/containers monté en lecture seule au même chemin (hostPath type: Directory : si la racine bouge encore, l'agent refuse de démarrer au lieu de redevenir aveugle en silence).
arcodange/forge-aspiration (seau qui fuit) : GET/HEAD sur un routeur dont le nom contient gitea, statut ≠ 403 (déjà banni par le videur), chemin ^/<owner>/<repo>/(src/(commit|branch|tag)|commits|commit|blame|compare|raw|archive|media|graph|activity|lastcommit)(/|$) ; groupé par /24 (IPv4) ou /64 (IPv6) ; capacité 60, fuite 4 s (15 pages/min tolérées en continu), blackhole 1 min. Portée Ip : au débordement, une alerte — donc un ban — par adresse présente dans le seau (39-40 adresses par débordement sur la plage tournante).
arcodange/forge-comparaisons : compare/archive seulement, capacité 15, fuite 60 s — pour le robot qui enchaîne une comparaison de ~10 s toutes les 15 s, trop lent pour le premier seau.
Profil LAPI forge_aspiration_24h : ban 24 h pour arcodange/forge-*, placé devant les deux profils par défaut de l'image, recopiés à l'identique (4 h). ⚠ Ce profiles.yaml remplace celui de l'image.
Liste blanche arcodange/foyer-whitelist (s02-enrich) : l'IPv4 publique du foyer et son /64 IPv6 (relevés dans doc/ce-que-traefik-voit.md, dynamiques). Les plages privées restent couvertes par crowdsecurity/whitelists (10/8, 172.16/12, 192.168/16, 127/8) — runners de CI et ArgoCD passent par gitea.arcodange.lab (routeur gitea@file, 3 108 requêtes dans la fenêtre, toutes d'adresses privées).
http-crawl-non_statics n'est pas touché (fichier du hub) : le seau de la forge est sa version resserrée pour la forge seule.
doc/ce-que-traefik-voit.md §6 : les deux limites ci-dessus, pour que personne ne relise « le videur juge la vraie IP » comme « la forge est protégée ».
Les preuves — hors du trafic réel
Banc : conteneur crowdsecurity/crowdsec:v1.7.0 (la version des pods), hub, parseurs, scénarios et fichiers de données copiés de l'agent du cluster (kubectl exec … tar, lecture seule), règles extraites du rendu helm template du chart amont 0.20.1 avec ces values (relu après le dernier commit : identiques au fichier joué). Une LAPI locale (crowdsec -no-cs), puis le rejeu crowdsec -dsn file://… -type docker -label program:traefik -no-api (l'acquisition du cluster est type: docker, program: traefik) ; les lignes réelles de kubectl logs sont réemballées au format json-file de Docker. Le verdict est un script à trois sorties : 0 = a débordé, 1 = n'a pas débordé, 2 = instrument aveugle (rien conclu). A = forge-aspiration, B = forge-comparaisons. Toutes les commandes de rejeu sont sorties 0.
#
jeu
A
B
ce que ça montre
1
(a) intact — 200 lignes réelles de chaque plage
0
1
16.216.88.0/24 : débordements à 1 min 54 puis 3 min 23 → 61 adresses ; 74.7.227.0/24 : à 7 min 27. 62 décisions ban, portée Ip, 24 h
(b) lecteur : 30 pages coûteuses en 10 min (rafale de 10 en 1 min), dont 4 comparaisons — lignes réelles, adresse 203.0.113.7
1
1
une lecture humaine ne mord pas
5
(b2) lecteur intense : 120 pages en 10 min (rafales de 20/min)
1
1
marge
6
témoin de (b2) : les mêmes 120 lignes en 120 s
0
1
les lignes du lecteur entrent bien dans le seau : c'est le débit qui les épargne
7
(c) = (a) avec 192.168.1.103
1
1
liste blanche privée
8
témoin de (c) : whitelists.yaml retiré
0
1
c'est la liste blanche qui protège
9
(c2) = (a) avec l'IPv4 publique du foyer
1
1
liste blanche du foyer
10
témoin de (c2) : foyer-whitelist.yaml retiré
0
1
idem
11
(e) = (a) en IPv6 dans un même /64 (2001:db8::/32)
0
1
le regroupement /64 fonctionne (45 adresses)
12
(e2) = (a) dans le /64 du foyer
1
1
liste blanche IPv6
13
(d) les 328 comparaisons réelles du 3ᵉ robot (adresse 198.51.100.9)
1
0
B déborde à 1 min 43 ; A seul ne l'aurait pas vu
14
tout le journal (2 h 45), règles du cluster seules
1
1
7 alertes, routeur - seulement : rien sur la forge
15
tout le journal, avec cette PR
0
0
les 74 adresses de 16.216.88.0/24, 74.7.227.0/24, le 3ᵉ robot — et aucune autre adresse
16
montage, mode réel : chaîne de liens de pi1 reproduite, acquis.yaml du cluster, racine montée
0
1
400 lignes lues / 400 analysées
17
sabotage : racine absente (= le cluster aujourd'hui)
1
1
Error setting up tail for /var/log/containers/traefik-… — le message exact du cluster —, zéro ligne
18
rétabli : racine montée
0
1
400 lues
Dans 14-15 les débordements se répètent (59 pour A) parce qu'aucun videur n'applique les bans dans un rejeu ; en vrai, les adresses bannies reçoivent 403 et sortent du seau.
Au déploiement
L'application ArgoCD crowdsec (projet tools, chemin crowdsec, targetRevision: HEAD, automated: prune + selfHeal) est Synced / Healthy sur 259861f (synchro du 2026-10-08 06:44 Z) : la fusion se déploie seule.
Ce qui bouge (diff du rendu helm template, hors Secret) : trois ConfigMaps neuves (crowdsec-scenarios, crowdsec-parsers-s02-enrich, crowdsec-profiles), le DaemonSet de l'agent (montages) et le Deployment de la LAPI (montage du profil). Les deux redémarrent.
⚠ La LAPI redémarre (stratégie Recreate) — comme à chaque rotation Vault, et très probablement à chaque changement de crowdsec/ (deux helm template successifs tirent deux secrets LAPI différents : le gabarit retombe sur randAlphaNum quand lookup ne voit pas le cluster, ce qui est le cas d'un rendu ArgoCD). Le videur a updateMaxFailure: 0 : si sa mise à jour de flux (toutes les 60 s) tombe pendant le redémarrage, tout *.arcodange.fr répond 403 jusqu'à la mise à jour suivante. Fusionner à un moment calme.
⚠ Tous les scénarios HTTP se rallument après six mois d'aveuglement, pas seulement ceux de la forge. Sur les 2 h 45 rejouées, ils n'ont mordu que des scanners (routeur -) ; le trafic public de kadans a culminé à 9 chemins distincts en 20 s pour une adresse (seuil de http-crawl-non_statics : ~40).
Vérifier après coup (lecture seule) :
kubectl --context default -n tools logs ds/crowdsec-agent -c crowdsec-agent | grep -c 'Error setting up tail' # aujourd'hui 2, attendu 0
kubectl --context default -n tools exec ds/crowdsec-agent -c crowdsec-agent -- cscli metrics show acquisition # la ligne file:/var/log/containers/traefik-… avec des lignes lues
kubectl --context default -n tools exec ds/crowdsec-agent -c crowdsec-agent -- cscli scenarios list | grep arcodange
kubectl --context default -n tools exec deploy/crowdsec-lapi -- cscli alerts list -s arcodange/forge-aspiration
Risques et ce qui n'est pas prouvé
Le montage n'a pas été essayé dans le cluster (aucune écriture permise) : il est prouvé sur une reproduction locale de la chaîne de liens (16-18) et par les liens relevés dans le pod. Si /mnt/arcodange/docker/containers n'existait pas sur pi1, l'agent ne démarrerait pas (type: Directory) — visible dans ArgoCD, sans effet sur le videur.
Faux positifs possibles : plusieurs personnes derrière un même /24 (NAT d'entreprise, opérateur mobile) qui lisent l'historique en même temps se partagent un seau ; un humain qui enchaîne plus de ~15 pages coûteuses par minute pendant plusieurs minutes, ou plus de 15 comparaisons/archives d'affilée. Les adresses du foyer sont dynamiques : si elles changent, la liste blanche ne protège plus (les adresses privées, elles, restent couvertes).
Le filtre repose sur le nom du routeur (contains 'gitea') : renommer l'IngressRoute sans « gitea » rendrait les deux seaux muets.
Le ban est de 24 h ; un robot qui revient le lendemain reprend un débordement complet (~2 à 7 min). Les bans manuels du jour (168 h) ne sont pas touchés.
Les journaux rejoués sont ceux de kubectl logs, réemballés au format Docker — pas les fichiers bruts de pi1 (inaccessibles depuis l'agent, c'est tout le problème).
Hors de ce dépôt, à considérer : un robots.txt côté Gitea (custom/public/robots.txt) qui refuse blame/compare/commits aux robots polis ; le passage du videur en mode live pour que les bans de plage servent.
## En une phrase
CrowdSec n'a rien décidé pendant l'aspiration de la forge du 2026-10-08 parce que **l'agent ne lisait aucune ligne de Traefik** (racine Docker de pi1 non montée) ; et même en lisant, **aucun scénario installé n'aurait mordu**. Cette PR monte la bonne racine, ajoute deux scénarios réservés à la forge (ban 24 h, portée Ip) et met le foyer en liste blanche — rejoués hors trafic, sabotage compris.
## Le constat chiffré (journal Traefik du 2026-10-08, fenêtre 14:37 → 15:58, 81 min)
| source | requêtes | adresses | temps Gitea | pages |
|---|---|---|---|---|
| 16.216.88.0/24 | 4 389 | **74** (qui tournent ; au plus 9 req/min par adresse) | 16 281 s (moy. 3,7 s) | src/commit 2 145, commits 802, commit 458, blame 424, raw 287 |
| 74.7.227.0/24 | 1 918 | 1 | 4 551 s (moy. 2,4 s) | src 766, commits 457, blame 360, raw 326 |
| un 3ᵉ robot (une adresse) | 328 | 1 | 2 702 s (moy. **8,2 s**) | `/compare/` 325, dont 254 abandonnées (499) |
Coût par famille de pages, tout le trafic de la forge : `/compare/` médiane 9,9 s ; `/commit/` 4,2 s (p90 14 s) ; `/blame/` 2,5 s (p90 10,7 s) ; `/src/commit/` 2,5 s ; `/commits/` 2,3 s ; `/raw/` 0,9 s.
## Pourquoi rien n'a mordu — mesuré, dans l'ordre
1. **L'agent ne lisait rien.** Sur pi1, `/var/log/containers/traefik-…log` → `/var/log/pods/…/traefik/0.log` → `/mnt/arcodange/docker/containers/<id>/<id>-json.log`. Le chart ne monte que `/var/log` et `/var/lib/docker/containers` (l'ancienne racine, plus écrite depuis le 2026-04-07). Dans le pod :
- `head` sur le lien → `No such file or directory` ; les **54** journaux de `/var/log/pods` pointent tous sous `/mnt/arcodange/docker` ;
- journal de l'agent au démarrage (2026-09-26 17:56) : `Error setting up tail for /var/log/containers/traefik-67bdf6f97d-…log: unable to read …` ;
- `cscli metrics` : tables vides ; `/metrics` n'expose même pas `cs_filesource_hits_total` (zéro ligne lue) ;
- LAPI : sur les 7 jours que garde la base (`flush.max_age: 7d`), **2 alertes**, les deux bans manuels.
2. **Même en lisant, aucun scénario n'aurait mordu.** J'ai rejoué les 2 h 45 de journal réel (11 670 lignes, toutes adresses) dans un CrowdSec v1.7.0 local **avec le hub copié de l'agent du cluster** (mêmes collections, mêmes versions) : **7 alertes, toutes sur le routeur `-`** (des scanners sur des hôtes inconnus), **zéro sur la forge**. Les raisons :
- `crowdsecurity/http-crawl-non_statics` (v0.7) compte **par adresse** (`groupby: source_ip + target_fqdn`), capacité 40, fuite 0,5 s : il faut **plus de 2 pages distinctes par seconde, soutenues**, pour déborder. La plage 16.216.88.0/24 répartit sa charge sur 74 adresses (9 req/min au plus chacune) ; 74.7.227.0/24 tourne à 0,4 req/s.
- `crowdsecurity/http-logs` range `.TS` parmi les fichiers statiques (pensé pour la vidéo MPEG-TS) : les pages TypeScript de mediabunny n'entrent même pas dans le seau — **957 des 4 389** requêtes de la plage (dont 699 `.ts`).
- `cscli explain` joué **dans l'agent du cluster** sur 5 lignes réelles des deux plages : `docker-logs` → `traefik-logs` → `dateparse-enrich`, `geoip-enrich`, `http-logs`, `whitelists` : tout se parse ; les 2 lignes en `.ts` ne vont dans **aucun** seau (`static_ressource: true`), les 3 autres seulement dans `http-crawl-non_statics`.
- Collections installées : `traefik`, `http-cve`, `base-http-scenarios`, `linux`, `sshd`, `whitelist-good-actors`.
3. **L'adresse vue est la bonne.** CrowdSec lit `remote_addr` dans la ligne CLF de Traefik, c'est-à-dire le client résolu par Traefik (`entryPoints.web.forwardedHeaders.trustedIPs=10.42.0.0/16`), pas Cloudflare ni le pod `cloudflared` : `cscli explain` → `evt.Meta.source_ip : 16.216.88.176`. Le videur, lui, voit aussi la vraie adresse alors que son `forwardedHeadersTrustedIPs` vaut `['10.0.10.23/32','10.0.20.0/24']` et son `clientTrustedIPs` `['192.168.1.0/24','10.42.0.0/16']` : c'est donc bien Traefik qui la résout en amont.
4. **Un ban de PLAGE n'est pas appliqué par ce videur** (constaté par l'orchestrateur sur les bans manuels : `ServeHTTP:Get ip:16.216.88.122 isBanned:false cache:miss`, 120 requêtes servies en 2 min ; les 512 adresses ont été réimportées une par une). La cause, lue dans le code : en mode `stream`, le plugin `crowdsec-bouncer-traefik-plugin` (**v1.3.3** ici, `.vendored-version` du volume de plugins) range chaque décision sous `decision.Value` tel quel et cherche l'adresse du client par égalité exacte — aucune correspondance de plage. **C'est toujours le cas sur sa branche principale** (relu le 2026-10-08 ; rien dans les notes de version jusqu'à v1.8.0-alpha). Vérifié sur une LAPI v1.7.0 locale : `/v1/decisions?ip=203.0.113.7&banned=true` (mode `live`) rend bien la décision `Range 203.0.113.0/24`, tandis que `/v1/decisions/stream` la livre comme la valeur `"203.0.113.0/24"`. → **Les scénarios de cette PR gardent la portée Ip.** Passer le videur en `live` réglerait les plages au prix d'un appel LAPI par adresse nouvelle (hors de ce dépôt : rôle `crowdsec` de `factory`).
## La règle
- **`agent.extraVolumes` / `extraVolumeMounts`** : `/mnt/arcodange/docker/containers` monté en lecture seule **au même chemin** (`hostPath type: Directory` : si la racine bouge encore, l'agent refuse de démarrer au lieu de redevenir aveugle en silence).
- **`arcodange/forge-aspiration`** (seau qui fuit) : `GET`/`HEAD` sur un routeur dont le nom contient `gitea`, statut ≠ 403 (déjà banni par le videur), chemin `^/<owner>/<repo>/(src/(commit|branch|tag)|commits|commit|blame|compare|raw|archive|media|graph|activity|lastcommit)(/|$)` ; **groupé par /24 (IPv4) ou /64 (IPv6)** ; capacité 60, fuite 4 s (15 pages/min tolérées en continu), blackhole 1 min. Portée Ip : au débordement, une alerte — donc un ban — **par adresse présente dans le seau** (39-40 adresses par débordement sur la plage tournante).
- **`arcodange/forge-comparaisons`** : `compare`/`archive` seulement, capacité 15, fuite 60 s — pour le robot qui enchaîne une comparaison de ~10 s toutes les 15 s, trop lent pour le premier seau.
- **Profil LAPI `forge_aspiration_24h`** : ban **24 h** pour `arcodange/forge-*`, placé devant les deux profils par défaut de l'image, recopiés à l'identique (4 h). ⚠ Ce `profiles.yaml` remplace celui de l'image.
- **Liste blanche `arcodange/foyer-whitelist`** (s02-enrich) : l'IPv4 publique du foyer et son /64 IPv6 (relevés dans `doc/ce-que-traefik-voit.md`, dynamiques). Les plages privées restent couvertes par `crowdsecurity/whitelists` (10/8, 172.16/12, 192.168/16, 127/8) — runners de CI et ArgoCD passent par `gitea.arcodange.lab` (routeur `gitea@file`, 3 108 requêtes dans la fenêtre, **toutes** d'adresses privées).
- `http-crawl-non_statics` n'est pas touché (fichier du hub) : le seau de la forge **est** sa version resserrée pour la forge seule.
- `doc/ce-que-traefik-voit.md` §6 : les deux limites ci-dessus, pour que personne ne relise « le videur juge la vraie IP » comme « la forge est protégée ».
## Les preuves — hors du trafic réel
Banc : conteneur `crowdsecurity/crowdsec:v1.7.0` (la version des pods), hub, parseurs, scénarios et fichiers de données **copiés de l'agent du cluster** (`kubectl exec … tar`, lecture seule), règles **extraites du rendu `helm template` du chart amont 0.20.1 avec ces values** (relu après le dernier commit : identiques au fichier joué). Une LAPI locale (`crowdsec -no-cs`), puis le rejeu `crowdsec -dsn file://… -type docker -label program:traefik -no-api` (l'acquisition du cluster est `type: docker, program: traefik`) ; les lignes réelles de `kubectl logs` sont réemballées au format json-file de Docker. Le verdict est un script à trois sorties : **0 = a débordé, 1 = n'a pas débordé, 2 = instrument aveugle** (rien conclu). A = `forge-aspiration`, B = `forge-comparaisons`. Toutes les commandes de rejeu sont sorties 0.
| # | jeu | A | B | ce que ça montre |
|---|---|---|---|---|
| 1 | **(a) intact** — 200 lignes réelles de chaque plage | **0** | 1 | 16.216.88.0/24 : débordements à 1 min 54 puis 3 min 23 → **61 adresses** ; 74.7.227.0/24 : à 7 min 27. **62 décisions `ban`, portée Ip, 24 h** |
| 2 | **(a) sabotage** — capacité 60 → 100 000 (diff du fichier : sortie 1, sabotage appliqué ; « Adding leaky bucket » au chargement) | **1** | 1 | le seuil est bien ce qui mord |
| 3 | **(a) rétabli** | **0** | 1 | identique à 1 |
| 4 | (b) lecteur : 30 pages coûteuses en 10 min (rafale de 10 en 1 min), dont 4 comparaisons — lignes réelles, adresse 203.0.113.7 | 1 | 1 | une lecture humaine ne mord pas |
| 5 | (b2) lecteur intense : 120 pages en 10 min (rafales de 20/min) | 1 | 1 | marge |
| 6 | témoin de (b2) : les **mêmes** 120 lignes en 120 s | **0** | 1 | les lignes du lecteur entrent bien dans le seau : c'est le débit qui les épargne |
| 7 | (c) = (a) avec 192.168.1.103 | 1 | 1 | liste blanche privée |
| 8 | témoin de (c) : `whitelists.yaml` retiré | **0** | 1 | c'est la liste blanche qui protège |
| 9 | (c2) = (a) avec l'IPv4 publique du foyer | 1 | 1 | liste blanche du foyer |
| 10 | témoin de (c2) : `foyer-whitelist.yaml` retiré | **0** | 1 | idem |
| 11 | (e) = (a) en IPv6 dans un même /64 (2001:db8::/32) | **0** | 1 | le regroupement /64 fonctionne (45 adresses) |
| 12 | (e2) = (a) dans le /64 du foyer | 1 | 1 | liste blanche IPv6 |
| 13 | (d) les 328 comparaisons réelles du 3ᵉ robot (adresse 198.51.100.9) | 1 | **0** | B déborde à 1 min 43 ; A seul ne l'aurait pas vu |
| 14 | **tout le journal (2 h 45), règles du cluster seules** | 1 | 1 | 7 alertes, routeur `-` seulement : **rien sur la forge** |
| 15 | **tout le journal, avec cette PR** | **0** | **0** | les **74** adresses de 16.216.88.0/24, 74.7.227.0/24, le 3ᵉ robot — **et aucune autre adresse** |
| 16 | montage, mode réel : chaîne de liens de pi1 reproduite, `acquis.yaml` du cluster, racine **montée** | **0** | 1 | 400 lignes lues / 400 analysées |
| 17 | **sabotage** : racine **absente** (= le cluster aujourd'hui) | **1** | 1 | `Error setting up tail for /var/log/containers/traefik-…` — le message exact du cluster —, zéro ligne |
| 18 | rétabli : racine montée | **0** | 1 | 400 lues |
Dans 14-15 les débordements se répètent (59 pour A) parce qu'aucun videur n'applique les bans dans un rejeu ; en vrai, les adresses bannies reçoivent 403 et sortent du seau.
## Au déploiement
- L'application ArgoCD `crowdsec` (projet `tools`, chemin `crowdsec`, `targetRevision: HEAD`, `automated: prune + selfHeal`) est **Synced / Healthy sur `259861f`** (synchro du 2026-10-08 06:44 Z) : la fusion se déploie seule.
- Ce qui bouge (diff du rendu `helm template`, hors Secret) : trois ConfigMaps neuves (`crowdsec-scenarios`, `crowdsec-parsers-s02-enrich`, `crowdsec-profiles`), le DaemonSet de l'agent (montages) et le Deployment de la LAPI (montage du profil). **Les deux redémarrent.**
- ⚠ **La LAPI redémarre** (stratégie `Recreate`) — comme à chaque rotation Vault, et très probablement à chaque changement de `crowdsec/` (deux `helm template` successifs tirent deux secrets LAPI différents : le gabarit retombe sur `randAlphaNum` quand `lookup` ne voit pas le cluster, ce qui est le cas d'un rendu ArgoCD). Le videur a `updateMaxFailure: 0` : si sa mise à jour de flux (toutes les 60 s) tombe pendant le redémarrage, **tout `*.arcodange.fr` répond 403 jusqu'à la mise à jour suivante**. Fusionner à un moment calme.
- ⚠ **Tous les scénarios HTTP se rallument** après six mois d'aveuglement, pas seulement ceux de la forge. Sur les 2 h 45 rejouées, ils n'ont mordu que des scanners (routeur `-`) ; le trafic public de kadans a culminé à 9 chemins distincts en 20 s pour une adresse (seuil de `http-crawl-non_statics` : ~40).
- Vérifier après coup (lecture seule) :
```
kubectl --context default -n tools logs ds/crowdsec-agent -c crowdsec-agent | grep -c 'Error setting up tail' # aujourd'hui 2, attendu 0
kubectl --context default -n tools exec ds/crowdsec-agent -c crowdsec-agent -- cscli metrics show acquisition # la ligne file:/var/log/containers/traefik-… avec des lignes lues
kubectl --context default -n tools exec ds/crowdsec-agent -c crowdsec-agent -- cscli scenarios list | grep arcodange
kubectl --context default -n tools exec deploy/crowdsec-lapi -- cscli alerts list -s arcodange/forge-aspiration
```
## Risques et ce qui n'est pas prouvé
- **Le montage n'a pas été essayé dans le cluster** (aucune écriture permise) : il est prouvé sur une reproduction locale de la chaîne de liens (16-18) et par les liens relevés dans le pod. Si `/mnt/arcodange/docker/containers` n'existait pas sur pi1, l'agent ne démarrerait pas (`type: Directory`) — visible dans ArgoCD, sans effet sur le videur.
- **Faux positifs possibles** : plusieurs personnes derrière un même /24 (NAT d'entreprise, opérateur mobile) qui lisent l'historique en même temps se partagent un seau ; un humain qui enchaîne plus de ~15 pages coûteuses par minute pendant plusieurs minutes, ou plus de 15 comparaisons/archives d'affilée. Les adresses du foyer sont dynamiques : si elles changent, la liste blanche ne protège plus (les adresses privées, elles, restent couvertes).
- Le filtre repose sur le **nom du routeur** (`contains 'gitea'`) : renommer l'IngressRoute sans « gitea » rendrait les deux seaux muets.
- Le ban est de 24 h ; un robot qui revient le lendemain reprend un débordement complet (~2 à 7 min). Les bans manuels du jour (168 h) ne sont pas touchés.
- Les journaux rejoués sont ceux de `kubectl logs`, réemballés au format Docker — pas les fichiers bruts de pi1 (inaccessibles depuis l'agent, c'est tout le problème).
- Hors de ce dépôt, à considérer : un `robots.txt` côté Gitea (`custom/public/robots.txt`) qui refuse blame/compare/commits aux robots polis ; le passage du videur en mode `live` pour que les bans de plage servent.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sur pi1, les journaux de conteneurs passent par une chaîne de liens qui finit
sous /mnt/arcodange/docker/containers (racine Docker déplacée ; l'ancienne,
/var/lib/docker/containers, n'est plus écrite depuis le 2026-04-07). Le chart
ne montait que /var/log et l'ancienne racine : le lien pendait dans le pod.
Mesuré le 2026-10-08 : « Error setting up tail for
/var/log/containers/traefik-… » au démarrage de l'agent (2026-09-26), les 54
journaux de /var/log/pods pointent tous sous /mnt/arcodange/docker, et la
métrique cs_filesource_hits_total n'existe pas (zéro ligne lue). Aucun
scénario HTTP n'a donc jamais pu déborder, pendant que la forge était aspirée.
Montage hostPath en lecture seule AU MÊME CHEMIN (le noyau résout le lien dans
l'espace de montage du pod), type Directory pour que l'agent refuse de démarrer
plutôt que de redevenir aveugle en silence si la racine bouge encore.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Le 2026-10-08, 16.216.88.0/24 (74 adresses qui tournent) et 74.7.227.0/24 ont
fait tourner Gitea à 100 % plus d'une heure sur blame/commit/commits/raw.
crowdsecurity/http-crawl-non_statics ne pouvait pas les voir : il compte par
adresse et tolère 2 req/s soutenues ; la plage ne dépassait pas 9 req/min par
adresse, et `.ts` (TypeScript ici) est rangé parmi les fichiers statiques.
- arcodange/forge-aspiration : pages git coûteuses sur le routeur gitea,
regroupées par /24 ou /64, capacité 60, fuite 4 s ;
- arcodange/forge-comparaisons : compare/archive, capacité 15, fuite 60 s
(un troisième robot enchaînait une comparaison de ~10 s toutes les 15 s) ;
- portée Ip conservée (une alerte par adresse présente dans le seau) : le
videur Traefik en mode stream n'applique pas les décisions de plage ;
- profil LAPI : 24 h pour ces deux scénarios, les deux profils par défaut
recopiés tels quels derrière ;
- liste blanche des adresses publiques du foyer (IPv4 et /64 IPv6).
Rejoué hors trafic dans un CrowdSec v1.7.0 local, avec le hub copié de
l'agent du cluster : voir la PR (débordements, sabotage du seuil, lecteur
humain, adresses privées et du foyer).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
La section 6 disait que la convention « crowdsec devant tout » tenait parce
que le videur juge la vraie IP. C'est vrai, mais le 2026-10-08 a montré deux
limites : l'agent ne lisait aucune ligne (racine Docker non montée), et le
plugin en mode stream ignore les décisions de plage (vérifié : la LAPI rend
bien la décision de plage en mode live, le flux stream la livre comme une
valeur que le plugin ne compare qu'à l'égalité).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
En une phrase
CrowdSec n'a rien décidé pendant l'aspiration de la forge du 2026-10-08 parce que l'agent ne lisait aucune ligne de Traefik (racine Docker de pi1 non montée) ; et même en lisant, aucun scénario installé n'aurait mordu. Cette PR monte la bonne racine, ajoute deux scénarios réservés à la forge (ban 24 h, portée Ip) et met le foyer en liste blanche — rejoués hors trafic, sabotage compris.
Le constat chiffré (journal Traefik du 2026-10-08, fenêtre 14:37 → 15:58, 81 min)
/compare/325, dont 254 abandonnées (499)Coût par famille de pages, tout le trafic de la forge :
/compare/médiane 9,9 s ;/commit/4,2 s (p90 14 s) ;/blame/2,5 s (p90 10,7 s) ;/src/commit/2,5 s ;/commits/2,3 s ;/raw/0,9 s.Pourquoi rien n'a mordu — mesuré, dans l'ordre
/var/log/containers/traefik-…log→/var/log/pods/…/traefik/0.log→/mnt/arcodange/docker/containers/<id>/<id>-json.log. Le chart ne monte que/var/loget/var/lib/docker/containers(l'ancienne racine, plus écrite depuis le 2026-04-07). Dans le pod :headsur le lien →No such file or directory; les 54 journaux de/var/log/podspointent tous sous/mnt/arcodange/docker;Error setting up tail for /var/log/containers/traefik-67bdf6f97d-…log: unable to read …;cscli metrics: tables vides ;/metricsn'expose même pascs_filesource_hits_total(zéro ligne lue) ;flush.max_age: 7d), 2 alertes, les deux bans manuels.-(des scanners sur des hôtes inconnus), zéro sur la forge. Les raisons :crowdsecurity/http-crawl-non_statics(v0.7) compte par adresse (groupby: source_ip + target_fqdn), capacité 40, fuite 0,5 s : il faut plus de 2 pages distinctes par seconde, soutenues, pour déborder. La plage 16.216.88.0/24 répartit sa charge sur 74 adresses (9 req/min au plus chacune) ; 74.7.227.0/24 tourne à 0,4 req/s.crowdsecurity/http-logsrange.TSparmi les fichiers statiques (pensé pour la vidéo MPEG-TS) : les pages TypeScript de mediabunny n'entrent même pas dans le seau — 957 des 4 389 requêtes de la plage (dont 699.ts).cscli explainjoué dans l'agent du cluster sur 5 lignes réelles des deux plages :docker-logs→traefik-logs→dateparse-enrich,geoip-enrich,http-logs,whitelists: tout se parse ; les 2 lignes en.tsne vont dans aucun seau (static_ressource: true), les 3 autres seulement danshttp-crawl-non_statics.traefik,http-cve,base-http-scenarios,linux,sshd,whitelist-good-actors.remote_addrdans la ligne CLF de Traefik, c'est-à-dire le client résolu par Traefik (entryPoints.web.forwardedHeaders.trustedIPs=10.42.0.0/16), pas Cloudflare ni le podcloudflared:cscli explain→evt.Meta.source_ip : 16.216.88.176. Le videur, lui, voit aussi la vraie adresse alors que sonforwardedHeadersTrustedIPsvaut['10.0.10.23/32','10.0.20.0/24']et sonclientTrustedIPs['192.168.1.0/24','10.42.0.0/16']: c'est donc bien Traefik qui la résout en amont.ServeHTTP:Get ip:16.216.88.122 isBanned:false cache:miss, 120 requêtes servies en 2 min ; les 512 adresses ont été réimportées une par une). La cause, lue dans le code : en modestream, le plugincrowdsec-bouncer-traefik-plugin(v1.3.3 ici,.vendored-versiondu volume de plugins) range chaque décision sousdecision.Valuetel quel et cherche l'adresse du client par égalité exacte — aucune correspondance de plage. C'est toujours le cas sur sa branche principale (relu le 2026-10-08 ; rien dans les notes de version jusqu'à v1.8.0-alpha). Vérifié sur une LAPI v1.7.0 locale :/v1/decisions?ip=203.0.113.7&banned=true(modelive) rend bien la décisionRange 203.0.113.0/24, tandis que/v1/decisions/streamla livre comme la valeur"203.0.113.0/24". → Les scénarios de cette PR gardent la portée Ip. Passer le videur enliveréglerait les plages au prix d'un appel LAPI par adresse nouvelle (hors de ce dépôt : rôlecrowdsecdefactory).La règle
agent.extraVolumes/extraVolumeMounts:/mnt/arcodange/docker/containersmonté en lecture seule au même chemin (hostPath type: Directory: si la racine bouge encore, l'agent refuse de démarrer au lieu de redevenir aveugle en silence).arcodange/forge-aspiration(seau qui fuit) :GET/HEADsur un routeur dont le nom contientgitea, statut ≠ 403 (déjà banni par le videur), chemin^/<owner>/<repo>/(src/(commit|branch|tag)|commits|commit|blame|compare|raw|archive|media|graph|activity|lastcommit)(/|$); groupé par /24 (IPv4) ou /64 (IPv6) ; capacité 60, fuite 4 s (15 pages/min tolérées en continu), blackhole 1 min. Portée Ip : au débordement, une alerte — donc un ban — par adresse présente dans le seau (39-40 adresses par débordement sur la plage tournante).arcodange/forge-comparaisons:compare/archiveseulement, capacité 15, fuite 60 s — pour le robot qui enchaîne une comparaison de ~10 s toutes les 15 s, trop lent pour le premier seau.forge_aspiration_24h: ban 24 h pourarcodange/forge-*, placé devant les deux profils par défaut de l'image, recopiés à l'identique (4 h). ⚠ Ceprofiles.yamlremplace celui de l'image.arcodange/foyer-whitelist(s02-enrich) : l'IPv4 publique du foyer et son /64 IPv6 (relevés dansdoc/ce-que-traefik-voit.md, dynamiques). Les plages privées restent couvertes parcrowdsecurity/whitelists(10/8, 172.16/12, 192.168/16, 127/8) — runners de CI et ArgoCD passent pargitea.arcodange.lab(routeurgitea@file, 3 108 requêtes dans la fenêtre, toutes d'adresses privées).http-crawl-non_staticsn'est pas touché (fichier du hub) : le seau de la forge est sa version resserrée pour la forge seule.doc/ce-que-traefik-voit.md§6 : les deux limites ci-dessus, pour que personne ne relise « le videur juge la vraie IP » comme « la forge est protégée ».Les preuves — hors du trafic réel
Banc : conteneur
crowdsecurity/crowdsec:v1.7.0(la version des pods), hub, parseurs, scénarios et fichiers de données copiés de l'agent du cluster (kubectl exec … tar, lecture seule), règles extraites du renduhelm templatedu chart amont 0.20.1 avec ces values (relu après le dernier commit : identiques au fichier joué). Une LAPI locale (crowdsec -no-cs), puis le rejeucrowdsec -dsn file://… -type docker -label program:traefik -no-api(l'acquisition du cluster esttype: docker, program: traefik) ; les lignes réelles dekubectl logssont réemballées au format json-file de Docker. Le verdict est un script à trois sorties : 0 = a débordé, 1 = n'a pas débordé, 2 = instrument aveugle (rien conclu). A =forge-aspiration, B =forge-comparaisons. Toutes les commandes de rejeu sont sorties 0.ban, portée Ip, 24 hwhitelists.yamlretiréfoyer-whitelist.yamlretiré-seulement : rien sur la forgeacquis.yamldu cluster, racine montéeError setting up tail for /var/log/containers/traefik-…— le message exact du cluster —, zéro ligneDans 14-15 les débordements se répètent (59 pour A) parce qu'aucun videur n'applique les bans dans un rejeu ; en vrai, les adresses bannies reçoivent 403 et sortent du seau.
Au déploiement
crowdsec(projettools, chemincrowdsec,targetRevision: HEAD,automated: prune + selfHeal) est Synced / Healthy sur259861f(synchro du 2026-10-08 06:44 Z) : la fusion se déploie seule.helm template, hors Secret) : trois ConfigMaps neuves (crowdsec-scenarios,crowdsec-parsers-s02-enrich,crowdsec-profiles), le DaemonSet de l'agent (montages) et le Deployment de la LAPI (montage du profil). Les deux redémarrent.Recreate) — comme à chaque rotation Vault, et très probablement à chaque changement decrowdsec/(deuxhelm templatesuccessifs tirent deux secrets LAPI différents : le gabarit retombe surrandAlphaNumquandlookupne voit pas le cluster, ce qui est le cas d'un rendu ArgoCD). Le videur aupdateMaxFailure: 0: si sa mise à jour de flux (toutes les 60 s) tombe pendant le redémarrage, tout*.arcodange.frrépond 403 jusqu'à la mise à jour suivante. Fusionner à un moment calme.-) ; le trafic public de kadans a culminé à 9 chemins distincts en 20 s pour une adresse (seuil dehttp-crawl-non_statics: ~40).Risques et ce qui n'est pas prouvé
/mnt/arcodange/docker/containersn'existait pas sur pi1, l'agent ne démarrerait pas (type: Directory) — visible dans ArgoCD, sans effet sur le videur.contains 'gitea') : renommer l'IngressRoute sans « gitea » rendrait les deux seaux muets.kubectl logs, réemballés au format Docker — pas les fichiers bruts de pi1 (inaccessibles depuis l'agent, c'est tout le problème).robots.txtcôté Gitea (custom/public/robots.txt) qui refuse blame/compare/commits aux robots polis ; le passage du videur en modelivepour que les bans de plage servent.🤖 Generated with Claude Code