REGEX : filtre fail2ban pour nginx

Depuis peu j’ai ouvert mon port 80 sur l’extérieur, et je découvre les joies du remplissage de log.
J’ai eu en particulier une IP plutôt insistante qui a testé l’existence d’une bonne quantité de fichiers, jetée par une 404 à chaque fois :

dave@HAL9000:/var/log$ grep "81.17.20.38" nginx/error.log | wc -l 924 dave@HAL9000:/var/log$ grep "81.17.20.38" nginx/error.log | head -n1 2014/09/09 22:30:59 [error] 17799#0: *18 open() "/home/dave/dotslashplay.it/html/w00tw00t.at.blackhats.romanian.anti-sec:)" failed (2: No such file or directory), client: 81.17.20.38, server: localhost, request: "GET /w00tw00t.at.blackhats.romanian.anti-sec:) HTTP/1.1", host: "*.*.*.*" dave@HAL9000:/var/log$ grep "81.17.20.38" nginx/error.log | tail -n1 2014/09/10 01:39:28 [error] 17799#0: *947 open() "/home/dave/dotslashplay.it/html/phpMyAdmin3/scripts/setup.php" failed (2: No such file or directory), client: 81.17.20.38, server: localhost, request: "GET /phpMyAdmin3/scripts/setup.php HTTP/1.1", host: "*.*.*.*"

Pour bloquer ce genre de scan (pour limiter la charge sur mon serveur), je pensais créer une règle fail2ban qui bloque les IP enchaînant au moins 10 erreurs 404 en une minute.

Le 10 erreurs en une minute, c’est OK avec :

findtime = 60 maxretry = 10

Par contre je bloque pour la regex à utiliser dans le champ failregex. Je cherche à repérer les lignes contenant la chaîne “No such file or directory”, mais n’ayant jamais manipulé d’expressions régulières je n’ai aucune idée de la façon dont je dois le formuler.

Merci d’avance pour votre aide !

Merci à captnfab sur le chan #debian-facile sur freenode pour sa solution :

17:53:06 +captnfab | vv222: tu veux filtrer juste les lignes avec No such file or directory ou tu veux plus de précision ? 17:53:16 +captnfab | (salut vv222 :) ) 17:54:29 vv222 | (salut aussi) 17:54:42 vv222 | Juste les lignes avaec cette expression, je pense que ça suffira. 17:55:02 vv222 | Et ça me paraît poser moins de risques de faux-positifs que de filtrer sur "404". 17:56:49 +captnfab | genre « .*No such file or directory.* » alors 17:57:09 +captnfab | .* signifie « n'importe quel caratcère, autant de fois que nécessaire » 17:57:39 +captnfab | donc en gros, la regex signifie «whatever No such file or directory whatever»

C’est woutwout, il est connu depuis longtemps mais pas méchant.
[strike]maxretry = 10[/strike] ==>maxretry = 1

Avec [mono]maxretry = 1[/mono], je risque de bloquer toute origine tombant sur du 404, non ?
Il n’y a pas de risque de bloquer des utilisateurs légitimes avec ça ?

Encore qu’à la réflexion mon site est pour l’instant limité à une unique page, alors à peu près tout ce qui tente de piocher autre chose a de grandes chances d’être une attaque.

Bon, les logs de nginx m’indiquent clairement que ma nouvelle règle fail2ban ne fonctionne pas…

[code]dave@HAL9000:/var/log/nginx$ grep “218.38.136.36” access.log | wc -l
55

dave@HAL9000:/var/log/nginx$ grep “218.38.136.36” access.log | head -n1
218.38.136.36 - - [11/Sep/2014:10:33:00 +0200] “GET /muieblackcat HTTP/1.1” 404 168 “-” “-”

dave@HAL9000:/var/log/nginx$ grep “218.38.136.36” access.log | tail -n1
218.38.136.36 - - [11/Sep/2014:10:33:34 +0200] “GET //phpMyAdmin-2.5.5/index.php HTTP/1.1” 404 168 “-” “-”[/code]

La règle en question :

[code]dave@HAL9000:/etc/fail2ban$ cat jail.d/nginx.conf
[nginx-http-auth]
enabled = true

[nginx-404]
enabled = true
findtime = 60
maxretry = 10
filter = nginx-404
port = http,https
logpath = /var/log/nginx/access.log

dave@HAL9000:/etc/fail2ban$ cat filter.d/nginx-404.conf

fail2ban filter configuration for nginx

[Definition]

failregex = .404 168., client: , server: \S+, request: “\S+ \S+ HTTP/\d+.\d+”, host: “\S+”\s*$

ignoreregex =[/code]

Évidemment, rien de suspect dans les logs de fail2ban qui aurait pu m’aiguiller :

dave@HAL9000:/var/log$ tail fail2ban.log 2014-09-11 16:08:37,263 fail2ban.filter [5318]: INFO Added logfile = /var/log/nginx/access.log 2014-09-11 16:08:37,263 fail2ban.filter [5318]: INFO Set maxRetry = 10 2014-09-11 16:08:37,264 fail2ban.filter [5318]: INFO Set findtime = 60 2014-09-11 16:08:37,264 fail2ban.actions[5318]: INFO Set banTime = 600 2014-09-11 16:08:37,269 fail2ban.jail [5318]: INFO Jail 'ssh' started 2014-09-11 16:08:37,272 fail2ban.jail [5318]: INFO Jail 'nginx-http-auth' started 2014-09-11 16:08:37,293 fail2ban.jail [5318]: INFO Jail 'vsftpd' started 2014-09-11 16:08:37,305 fail2ban.jail [5318]: INFO Jail 'postfix' started 2014-09-11 16:08:37,308 fail2ban.jail [5318]: INFO Jail 'recidive' started 2014-09-11 16:08:37,317 fail2ban.jail [5318]: INFO Jail 'nginx-404' started

J’en appelle à vos lumières, je soupçonne fortement ma ligne [mono]failregex[/mono] de /etc/fail2ban/filter.d/nginx-404.conf d’être mal construite.

essaye voir ton filtre sur le log directement, avec grep -E. Au moins tu verras directement le résultat

Le filtre lui-même semble efficace :

dave@HAL9000:/var/log/nginx$ grep -E ".*404 168.*" access.log | wc -l 1007
C’est donc probablement dans la suite de la ligne [mono]failregex[/mono] que se cache mon erreur…
M’en vais me brancher le man de fail2ban en perfusion, en espérant en sortir quelque chose de pertinent.

t’as répondu trop vite, j’étais en train d’éditer :slightly_smiling:

Je disais :

Je me demande aussi comment fail2ban gère le fait que t’aies plusieurs regex séparées par des “,”. S’il les cumule, le résultat à peu de chance de fonctionner, car si je prends juste failregex = .404 168., client: si ta première regex peu donner un résultat, la 2ème à peu de chance d’aboutir, car il faudrait que ta ligne ne contienne que "client: "

j’aurai essayé un truc du genre :
failregex = .404 168., .client: ., .server: \S+., …

Bon, je viens de découvrir l’outil fail2ban-regex qui devrait me donner un coup de main.
Pour le moment, quoi que j’essaie je ne rencontre que deux possibilités :
_soit je ne mets que [mono].404 168.[/mono] dans ma regex, qui dans ce cas matche avec les lignes concernés de mes logs mais fail2ban me renvoie l’erreur suivante :

_soit je mets la ligne complète, avec ou sens encadrement par des [mono].*[/mono] (le reste de l’expression est une simple copie de ce qui se trouve dans le fichier /etc/fail2ban/filter.d/nginx-http-auth.conf, fourni par le paquet fail2ban), et là ça ne matche plus.


J’ai enfin trouvé la page de manuel REGEX(7), je commence déjà à pleurer des larmes de sang…

Tu as essayé ce qu’il y a ici ? : fail2ban.org/wiki/index.php/NginX

Au pire tu crées plusieurs groupes de règles pour nginx (ca t’évite de mettre plusieurs regex dans la même commande)

dave@HAL9000:/var/log/nginx$ grep -E ".*404 168.*" /var/log/nginx/access.log | wc -l 1007

[code]dave@HAL9000:/var/log/nginx$ fail2ban-regex /var/log/nginx/access.log “.404 168.

Running tests

Use failregex line : .404 168.
Traceback (most recent call last):
File “/usr/bin/fail2ban-regex”, line 430, in
fail2banRegex.readRegex(cmd_regex, ‘fail’) or sys.exit(-1)
File “/usr/bin/fail2ban-regex”, line 227, in readRegex
’add%sRegex’ % regextype.title())(regex.getFailRegex())
File “/usr/share/fail2ban/server/filter.py”, line 95, in addFailRegex
raise e
server.failregex.RegexException: No ‘host’ group in ‘.404 168.’[/code]

[code]dave@HAL9000:/var/log/nginx$ fail2ban-regex /var/log/nginx/access.log ".404 168., client: "

Running tests

Use failregex line : .404 168., client:
Use log file : /var/log/nginx/access.log

Results

Failregex: 0 total

Ignoreregex: 0 total

Date template hits:
|- [# of hits] date format
| [1247] Day/MONTH/Year:Hour:Minute:Second
`-

Lines: 1247 lines, 0 ignored, 0 matched, 1247 missed
Missed line(s): too many to print. Use --print-all-missed to print all 1247 lines[/code]
Je sens que quelque chose d’évident m’échappe…
En tous cas mon insistance avec [mono]client: [/mono] ne m’amènera nulle part tel que c’est parti. M’est avis que l’absence du terme “client:” dans les lignes que je cherche à filtrer ne doit pas y être étranger.

Bon, ce n’est apparemment pas en tâtonnant que je vais trouver quelque chose qui fonctionne, j’ai peur de ne plus avoir d’échappatoire à la lecture de [mono]man 7 regex[/mono].

Oui je crois… qu’il va falloir. Il faut y passer un jour !

sinon : expreg.com/memo.php

C’est sûr que si “client” n’est pas dans tes log, ça va coincer. Moi ce qui m’intrigue, c’est ces virgules…

Sauvé, je vais pouvoir remettre mon apprentissage des regex à demain :041
La ligne suivante fait l’affaire :

failregex = ^<HOST> -.*404 168.*

source :
snippets.aktagon.com/snippets/55 … h-fail2ban

:079

[quote=“vv222”]Avec [mono]maxretry = 1[/mono], je risque de bloquer toute origine tombant sur du 404, non ?
Il n’y a pas de risque de bloquer des utilisateurs légitimes avec ça ?

Encore qu’à la réflexion mon site est pour l’instant limité à une unique page, alors à peu près tout ce qui tente de piocher autre chose a de grandes chances d’être une attaque.[/quote]
Tous mes maxretry sont entre 1 et 3 maxi avec ssh = 1