git clone/fetch/push en HTTPS fait systématiquement un aller-retour 401 avant d'envoyer les credentials (challenge WWW-Authenticate). Le jail traefik-auth (maxretry=10, findtime=5m sur tout 401/403/429/5xx) bannissait donc une IP légitime au bout d'une poignée d'opérations git.
Correctif
Ajout d'un ignoreregex au filtre /etc/fail2ban/filter.d/traefik.conf, limité aux 401 sur info/refs, git-upload-pack et git-receive-pack (protocole smart-HTTP). Les 401 sur toute autre route (UI, API) restent couverts par le failregex existant, inchangé.
Idempotence
Le bloc qui écrit traefik.conf était déjà inconditionnel (heredoc tee à chaque run) — l'idempotence était donc déjà acquise avant ce changement. Un rejeu du script réapplique simplement la version corrigée du filtre, sans effet de bord ni duplication.
Vérification
shellcheck proxy/install.sh : aucune nouvelle alerte (une alerte SC2006 préexistante en ligne 270, non liée à ce changement).
Heredoc déjà quoté (<< 'EOF'), la regex passe telle quelle sans expansion shell.
## Contexte
`git clone`/`fetch`/`push` en HTTPS fait systématiquement un aller-retour 401 avant d'envoyer les credentials (challenge `WWW-Authenticate`). Le jail `traefik-auth` (maxretry=10, findtime=5m sur tout 401/403/429/5xx) bannissait donc une IP légitime au bout d'une poignée d'opérations git.
## Correctif
Ajout d'un `ignoreregex` au filtre `/etc/fail2ban/filter.d/traefik.conf`, limité aux 401 sur `info/refs`, `git-upload-pack` et `git-receive-pack` (protocole smart-HTTP). Les 401 sur toute autre route (UI, API) restent couverts par le `failregex` existant, inchangé.
## Idempotence
Le bloc qui écrit `traefik.conf` était déjà inconditionnel (heredoc `tee` à chaque run) — l'idempotence était donc déjà acquise avant ce changement. Un rejeu du script réapplique simplement la version corrigée du filtre, sans effet de bord ni duplication.
## Vérification
- `shellcheck proxy/install.sh` : aucune nouvelle alerte (une alerte SC2006 préexistante en ligne 270, non liée à ce changement).
- Heredoc déjà quoté (`<< 'EOF'`), la regex passe telle quelle sans expansion shell.
Closes #17
Git smart-HTTP always fires an unauthenticated request first, gets a
401 challenge, then retries with credentials. On a private repo, ~10
git operations in 5 minutes hit maxretry and ban the legitimate
client for an hour. Excludes 401s on info/refs, git-upload-pack and
git-receive-pack while leaving other 401 sources (UI, API) covered.
Closes#17
Le correctif est juste et le commentaire explique bien le pourquoi. Un point à corriger avant merge.
L'ignoreregex ne couvre qu'un seul ordre de champs, alors que le failregex juste au-dessus en couvre deux.
Le commentaire existant est explicite : « Two patterns cover both possible field orderings in the JSON. » L'ignoreregex ajouté suppose au contraire que RequestPath précède toujours DownstreamStatus :
Si Traefik sérialise dans l'autre ordre, l'exclusion ne matche pas et le ban sur git fetch revient — c'est-à-dire précisément le bug que cette PR corrige, mais silencieusement et seulement dans certaines conditions. ignoreregex accepte plusieurs lignes comme failregex, donc :
Validation attendue : fail2ban-regex sur un vrai access.log montrant les 401 git en ignored, et un 401 sur /user/login toujours en matched.
Le reste est bon — heredoc quoté, écriture inconditionnelle donc idempotente, restart déjà en place.
Le correctif est juste et le commentaire explique bien le pourquoi. Un point à corriger avant merge.
**L'`ignoreregex` ne couvre qu'un seul ordre de champs, alors que le `failregex` juste au-dessus en couvre deux.**
Le commentaire existant est explicite : « Two patterns cover both possible field orderings in the JSON. » L'`ignoreregex` ajouté suppose au contraire que `RequestPath` précède toujours `DownstreamStatus` :
```
^.*"RequestPath":"[^"]*/(info/refs|...)[^"]*".*"DownstreamStatus":401
```
Si Traefik sérialise dans l'autre ordre, l'exclusion ne matche pas et le ban sur `git fetch` revient — c'est-à-dire précisément le bug que cette PR corrige, mais silencieusement et seulement dans certaines conditions. `ignoreregex` accepte plusieurs lignes comme `failregex`, donc :
```ini
ignoreregex = ^.*"RequestPath":"[^"]*/(info/refs|git-upload-pack|git-receive-pack)[^"]*".*"DownstreamStatus":401
^.*"DownstreamStatus":401.*"RequestPath":"[^"]*/(info/refs|git-upload-pack|git-receive-pack)[^"]*"
```
**Validation attendue** : `fail2ban-regex` sur un vrai `access.log` montrant les 401 git en `ignored`, et un 401 sur `/user/login` toujours en `matched`.
Le reste est bon — heredoc quoté, écriture inconditionnelle donc idempotente, restart déjà en place.
L'ignoreregex n'excluait que l'ordre RequestPath puis DownstreamStatus,
alors que le failregex juste au-dessus gère explicitement les deux
ordres possibles de sérialisation JSON de Traefik. Si Traefik
sérialise dans l'autre ordre, l'exclusion ne matchait pas et le ban
sur git fetch revenait silencieusement.
Ajoute la seconde ligne (DownstreamStatus puis RequestPath), symétrique
au failregex.
Les 4 lignes 401 sur info/refs/git-upload-pack/git-receive-pack, dans les deux ordres de champs → ignored.
Les 2 lignes 401 sur /user/login, dans les deux ordres de champs → matched.
shellcheck proxy/install.sh : aucune nouvelle alerte (SC2006 préexistant, non lié).
Corrigé (25bd5f2) : ajout de la seconde ligne `ignoreregex` (`DownstreamStatus` puis `RequestPath`), symétrique au `failregex` existant.
Vérifié avec `fail2ban-regex` sur un `access.log` synthétique couvrant les deux ordres de champs, pour les deux cas (git et non-git) :
```
Failregex: 2 total
| 1) [1] ^.*"ClientHost":"<HOST>".*"DownstreamStatus":(401|403|429|5[0-9]{2})
| 2) [1] ^.*"DownstreamStatus":(401|403|429|5[0-9]{2}).*"ClientHost":"<HOST>"
Ignoreregex: 4 total
| 1) [2] ^.*"RequestPath":"[^"]*/(info/refs|git-upload-pack|git-receive-pack)[^"]*".*"DownstreamStatus":401
| 2) [2] ^.*"DownstreamStatus":401.*"RequestPath":"[^"]*/(info/refs|git-upload-pack|git-receive-pack)[^"]*"
Lines: 6 lines, 4 ignored, 2 matched, 0 missed
```
- Les 4 lignes 401 sur `info/refs`/`git-upload-pack`/`git-receive-pack`, dans les deux ordres de champs → `ignored`.
- Les 2 lignes 401 sur `/user/login`, dans les deux ordres de champs → `matched`.
`shellcheck proxy/install.sh` : aucune nouvelle alerte (SC2006 préexistant, non lié).
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.
Contexte
git clone/fetch/pushen HTTPS fait systématiquement un aller-retour 401 avant d'envoyer les credentials (challengeWWW-Authenticate). Le jailtraefik-auth(maxretry=10, findtime=5m sur tout 401/403/429/5xx) bannissait donc une IP légitime au bout d'une poignée d'opérations git.Correctif
Ajout d'un
ignoreregexau filtre/etc/fail2ban/filter.d/traefik.conf, limité aux 401 surinfo/refs,git-upload-packetgit-receive-pack(protocole smart-HTTP). Les 401 sur toute autre route (UI, API) restent couverts par lefailregexexistant, inchangé.Idempotence
Le bloc qui écrit
traefik.confétait déjà inconditionnel (heredocteeà chaque run) — l'idempotence était donc déjà acquise avant ce changement. Un rejeu du script réapplique simplement la version corrigée du filtre, sans effet de bord ni duplication.Vérification
shellcheck proxy/install.sh: aucune nouvelle alerte (une alerte SC2006 préexistante en ligne 270, non liée à ce changement).<< 'EOF'), la regex passe telle quelle sans expansion shell.Closes #17
Le correctif est juste et le commentaire explique bien le pourquoi. Un point à corriger avant merge.
L'
ignoreregexne couvre qu'un seul ordre de champs, alors que lefailregexjuste au-dessus en couvre deux.Le commentaire existant est explicite : « Two patterns cover both possible field orderings in the JSON. » L'
ignoreregexajouté suppose au contraire queRequestPathprécède toujoursDownstreamStatus:Si Traefik sérialise dans l'autre ordre, l'exclusion ne matche pas et le ban sur
git fetchrevient — c'est-à-dire précisément le bug que cette PR corrige, mais silencieusement et seulement dans certaines conditions.ignoreregexaccepte plusieurs lignes commefailregex, donc :Validation attendue :
fail2ban-regexsur un vraiaccess.logmontrant les 401 git enignored, et un 401 sur/user/logintoujours enmatched.Le reste est bon — heredoc quoté, écriture inconditionnelle donc idempotente, restart déjà en place.
Corrigé (
25bd5f2) : ajout de la seconde ligneignoreregex(DownstreamStatuspuisRequestPath), symétrique aufailregexexistant.Vérifié avec
fail2ban-regexsur unaccess.logsynthétique couvrant les deux ordres de champs, pour les deux cas (git et non-git) :info/refs/git-upload-pack/git-receive-pack, dans les deux ordres de champs →ignored./user/login, dans les deux ordres de champs →matched.shellcheck proxy/install.sh: aucune nouvelle alerte (SC2006 préexistant, non lié).Pull request closed