---
title: "Fail2ban fermo da mesi su un VPS con Laravel: come un brute force SSH da 14.000 tentativi al giorno è passato inosservato"
description: "Fail2ban fermo su VPS con Laravel: 14.000 tentativi SSH brute force al giorno passati inosservati. Diagnosi, riattivazione, jail custom per login web e recidive."
canonical: "https://www.mauriziofonte.it/blog/post/fail2ban-emergenza-vps-laravel-intervento-rapido.html"
author: "Maurizio Fonte"
date: "2025-07-07T08:52:31+02:00"
modified: "2026-04-14T17:48:55+02:00"
section: "Sicurezza Informatica"
tags: ["Sicurezza Informatica", "VPS & Hosting"]
license: "Riuso e citazione consentiti con attribuzione e link alla fonte"
---

[Sicurezza Informatica](https://www.mauriziofonte.it/blog/cat/sicurezza_informatica.html) [VPS & Hosting](https://www.mauriziofonte.it/blog/cat/vps.html)

# [Fail2ban fermo da mesi su un VPS con Laravel: come un brute force SSH da 14.000 tentativi al giorno è passato inosservato](https://www.mauriziofonte.it/blog/post/fail2ban-emergenza-vps-laravel-intervento-rapido.html)

Lunedì 7 Luglio 2025, alle 08:52 ![Fail2ban fermo da mesi su un VPS con Laravel: come un brute force SSH da 14.000 tentativi al giorno è passato inosservato](https://www.mauriziofonte.it/image/002000200-fail2ban-emergenza-vps-laravel-intervento-rapido.jpg)

Ad agosto 2025 ho eseguito un audit di sicurezza su un VPS Digital Ocean Premium (8 vCPU, 16 GB RAM, 320 GB NVMe) che ospitava un SaaS Laravel 10 per la gestione di prenotazioni nel settore sanitario - circa 120 studi medici come clienti e 15.000 utenti finali. Il SaaS gestiva dati sanitari: anagrafiche pazienti, appuntamenti, referti. Il titolare mi aveva contattato per una consulenza NIS2, non per un problema di sicurezza. Ma il primo controllo che faccio in ogni audit è verificare lo stato delle protezioni attive:

```bash
systemctl is-active fail2ban
# → inactive (dead)

systemctl is-enabled fail2ban
# → disabled
```

Fail2ban era installato ma non girava. Non girava da sei mesi - da un aggiornamento di sistema che aveva resettato lo stato dei servizi senza riabilitarli. Per sei mesi, il VPS era stato esposto senza nessuna protezione contro brute force.

Ho controllato l'auth log di SSH:

```bash
grep "Failed password" /var/log/auth.log | wc -l
# → 87.432 (nei 6 giorni di log disponibili - log rotation settimanale)

# Media giornaliera
echo "87432 / 6" | bc
# → 14.572 tentativi al giorno
```

Quattordicimila tentativi di brute force SSH al giorno. Ogni giorno. Per sei mesi. I tentativi fallivano perché l'autenticazione SSH era configurata solo con chiave pubblica (niente password) - quindi nessun rischio diretto di compromissione. Ma quei 14.000 tentativi giornalieri consumavano risorse del server (ogni tentativo è un processo `sshd` che deve validare e rifiutare la connessione), riempivano i log, e soprattutto mascheravano eventuali tentativi più sofisticati. Se un attaccante avesse provato un exploit SSH (come il regreSSHion CVE-2024-6387 di cui ho parlato nell'articolo sull'[aggiornamento di sicurezza di server Debian](https://www.mauriziofonte.it/blog/post/piano-urgente-sicurezza-debian-laravel.html)), i suoi tentativi si sarebbero persi nel rumore di 14.000 brute force.

C'è un aspetto dei brute force SSH che i titolari non vedono: ogni tentativo, anche se fallisce, consuma risorse. Il processo `sshd` deve fare il fork, negoziare la connessione, validare (e rifiutare) la chiave o la password, e chiudere la connessione. Con 14.000 tentativi al giorno - circa 10 al minuto - l'impatto sulle performance è misurabile ma non catastrofico. Ma durante un attacco coordinato, i tentativi possono salire a migliaia al minuto, e a quel punto il consumo di CPU e la saturazione delle connessioni SSH possono degradare le performance del server anche se nessun tentativo ha successo. Fail2ban risolve il problema alla radice: dopo 3 tentativi falliti, l'IP viene bannato a livello firewall e le connessioni successive vengono rifiutate dal kernel prima ancora di raggiungere `sshd` - costo computazionale vicino a zero.

Ma il problema più grave non era SSH - era il login web. L'applicazione Laravel aveva un form di login su `/login` senza nessuna protezione: niente rate limiting, niente captcha, niente Fail2ban. Un attaccante poteva provare migliaia di combinazioni username/password senza nessuna conseguenza. Su un SaaS che gestisce dati sanitari, questa è una violazione grave di ogni principio di sicurezza - e un finding che in un audit NIS2 viene classificato come critico.

## Perché Fail2ban smette di funzionare e nessuno se ne accorge?

Fail2ban è un servizio che gira in background. Quando funziona, non produce output visibile - nessun'email, nessuna notifica, nessuna dashboard. L'unico modo per sapere che funziona è controllare attivamente il suo stato. Quando smette di funzionare - per un aggiornamento di sistema, un conflitto di dipendenze, un errore nella configurazione dei jail - la sua assenza è altrettanto invisibile. Nessun alert, nessun messaggio, nessun sintomo. Il server continua a funzionare normalmente, gli utenti non notano nulla. L'unica differenza è che ogni IP che prova un brute force non viene più bannato.

Le cause di fallimento più comuni che trovo sui VPS di PMI:

- **Aggiornamento di sistema** che resetta lo stato dei servizi (`apt dist-upgrade` su Debian/Ubuntu può disabilitare servizi che erano stati abilitati manualmente).
- **Conflitto con il backend di ban** - Fail2ban supporta `iptables`, `nftables` e `firewalld`. Se il sistema passa da `iptables` a `nftables` (come è successo di default su Debian 11), i ban di Fail2ban smettono di funzionare silenziosamente.
- **Log path errato** - se l'applicazione cambia la posizione dei log (da `/var/log/auth.log` a `/var/log/syslog`, o un cambio di versione di SSH che modifica il formato), il jail di Fail2ban non trova più i pattern e non banna nessuno.
- **Regex non aggiornata** - le nuove versioni di OpenSSH possono cambiare il formato dei messaggi di log. Se la regex del filtro `sshd` non corrisponde, Fail2ban legge il log ma non riconosce i tentativi falliti.

Stai cercando un **Consulente Informatico** esperto per hardenizzare la protezione brute force sulla tua infrastruttura Laravel? Nel mio [profilo professionale](https://www.mauriziofonte.it/pages/about) trovi l'esperienza concreta su Fail2ban, sicurezza SSH e protezione di applicazioni web su VPS per PMI.

## Riattivazione e hardening: SSH, login web e ban progressivo

La riattivazione base è banale - `systemctl enable --now fail2ban`. Ma riattivare Fail2ban con la configurazione di default è come mettere un lucchetto di cartone: tecnicamente presente, praticamente inutile. La configurazione che applico su ogni VPS copre tre livelli.

### Jail SSH hardenizzata

```ini
# /etc/fail2ban/jail.local
[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = %(sshd_log)s
maxretry = 3
bantime  = 1h
findtime = 10m
```

Con autenticazione solo a chiave (che deve essere il default su qualunque VPS di produzione), `maxretry = 3` è aggressivo - un utente legittimo non fallisce mai perché non usa password. Ogni tentativo è un bot, e va bannato dopo 3 tentativi. Il `bantime` di 1 ora è sufficiente per i bot semplici che cambiano IP dopo il ban.

### Jail custom per il login web Laravel

Questo è il pezzo che manca nella maggior parte delle installazioni Fail2ban e che è il più critico per le applicazioni web. [La guida di Joe Campo sulla protezione delle rotte Laravel con Fail2ban](https://gist.github.com/joecampo/848178ab5c18aada0eab) mostra il pattern: creare un filtro custom che matcha i tentativi POST su `/login` nel log di Nginx, e un jail che banna dopo un numero ragionevole di tentativi:

```ini
# /etc/fail2ban/filter.d/laravel-login.conf
[Definition]
failregex = ^<HOST> .* "POST /login HTTP.*" (401|422)
ignoreregex =
```

```ini
# /etc/fail2ban/jail.local (aggiungere)
[laravel-login]
enabled  = true
filter   = laravel-login
action   = iptables-multiport[name=LaravelLogin, port="http,https"]
logpath  = /var/log/nginx/access.log
bantime  = 30m
findtime = 2m
maxretry = 10
```

La regex matcha i POST a `/login` che ricevono 401 (Unauthorized) o 422 (Validation Error - il codice che Laravel restituisce per credenziali errate). 10 tentativi falliti in 2 minuti = ban per 30 minuti. Sufficientemente permissivo per un utente che sbaglia password tre volte, sufficientemente restrittivo per bloccare un brute force automatizzato.

Prima di attivare il jail, verifica che la regex funzioni con il formato del tuo log:

```bash
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/laravel-login.conf
```

### Ban progressivo con recidive

Per i bot persistenti che tornano dopo il ban, il jail `recidive` aumenta progressivamente la durata del ban:

```ini
# /etc/fail2ban/jail.local (aggiungere)
[recidive]
enabled  = true
logpath  = /var/log/fail2ban.log
banaction = iptables-allports
bantime  = 1w
findtime = 1d
maxretry = 3
```

Se un IP viene bannato 3 volte in 24 ore (su qualunque jail), il ban diventa di 1 settimana su tutte le porte. [La combinazione di Fail2ban con autenticazione a chiave SSH e ban progressivo](https://www.deployhq.com/blog/fail2ban-comprehensive-protection-for-your-servers) rende il brute force praticamente impossibile sia sull'SSH che sulle rotte web dell'applicazione.

Per chi gestisce server con applicazioni Laravel e vuole una protezione stratificata - non solo Fail2ban ma anche rate limiting a livello Nginx, Cloudflare e applicativo - ho descritto il caso reale di un [attacco di credential stuffing da 14.000 richieste al minuto contenuto in 47 minuti con Nginx, Fail2ban e Cloudflare](https://www.mauriziofonte.it/blog/post/difesa-proattiva-bot-ddos-php-laravel-hetzner-ovh-credential-stuffing.html).

## Whitelist, monitoring e gli errori da evitare

Tre configurazioni aggiuntive che fanno la differenza tra un Fail2ban che funziona e uno che crea problemi:

**Whitelist degli IP legittimi.** Senza whitelist, Fail2ban può bannare l'IP del tuo ufficio, il tuo CI/CD server, o il tuo monitoring. Sul SaaS del cliente sanitario, l'IP dell'ufficio era statico - l'ho aggiunto immediatamente:

```ini
# /etc/fail2ban/jail.local - sezione [DEFAULT]
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 IP.UFFICIO.QUI IP.CI.CD.QUI
```

Se l'IP dell'ufficio è dinamico (ADSL/fibra senza IP statico), la whitelist va gestita con un range più ampio o con una soluzione alternativa come un port knocking per SSH.

**Backend corretto.** Su Debian 11+ e Ubuntu 22.04+ il firewall di default è `nftables`, non `iptables`. Ma Fail2ban per default usa `iptables` come azione di ban. Il risultato: Fail2ban *pensa* di aver bannato un IP, ma il ban non ha effetto perché `iptables` e `nftables` non condividono le stesse tabelle. La verifica:

```bash
# Quale backend usa Fail2ban?
fail2ban-client get sshd actions

# I ban sono effettivamente attivi nel firewall?
iptables -L f2b-sshd -n 2>/dev/null || nft list ruleset | grep f2b
```

Se usi `nftables`, aggiorna la configurazione di Fail2ban:

```ini
# /etc/fail2ban/jail.local
[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports
```

**Monitoring dello stato di Fail2ban.** Fail2ban che smette di funzionare è invisibile - l'ho detto all'inizio di questo articolo e lo ripeto perché è il punto più importante. L'unico modo per sapere che Fail2ban è attivo è un check periodico. Il più semplice: un cron job che verifica lo stato e manda un alert se il servizio è morto:

```bash
# /etc/cron.d/check-fail2ban
*/5 * * * * root systemctl is-active fail2ban > /dev/null 2>&1 || \
    curl -s "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
    -d chat_id="${CHAT_ID}" -d text="ALERT: Fail2ban è morto su $(hostname)"
```

Con Prometheus, il check è più elegante: un exporter Fail2ban che espone le metriche (IP bannati, tentativi bloccati, stato dei jail) e un alert che scatta quando il servizio non risponde. Ho descritto lo stack completo di alerting nel mio articolo sul [monitoring proattivo per Laravel su VPS](https://www.mauriziofonte.it/blog/post/vps-monitoring-laravel-prevenzione-downtime.html).

Un ultimo errore che vedo ripetere: bannare per troppo tempo al primo tentativo. Un `bantime = 24h` al primo ban sembra aggressivo ma sicuro - fino a quando l'IP bannato è un resolver NAT aziendale dietro cui ci sono 200 dipendenti del tuo cliente. Un utente sbaglia password, l'IP viene bannato per 24 ore, e 200 persone non possono più accedere al portale. La strategia corretta è ban brevi al primo livello (30 minuti - 1 ora), ban progressivamente più lunghi con il jail `recidive` (1 giorno → 1 settimana), e whitelist degli IP dei clienti noti.

Sul VPS del cliente sanitario, dopo la riattivazione e la configurazione dei tre jail (SSH, login web, recidive), i 14.000 tentativi SSH giornalieri sono scesi a zero nel giro di 72 ore - i bot che venivano bannati dopo 3 tentativi smettevano di tornare dopo il terzo ban progressivo. I tentativi di login web, che prima non venivano nemmeno contati, hanno rivelato circa 200 tentativi al giorno su 8 account utente diversi (tutti con email pubbliche presenti nel sito degli studi medici) - tutti bloccati dopo 10 tentativi. Il finding è stato incluso nel report NIS2 come "misura correttiva implementata" - un dato positivo nell'audit perché dimostra capacità di rilevare e correggere vulnerabilità. Se il brute force fosse andato avanti senza protezione fino a un'eventuale compromissione, lo stesso finding sarebbe stato un "incidente non rilevato" - la peggior classificazione possibile in un audit di conformità.

Il costo dell'intero intervento Fail2ban - riattivazione, configurazione tre jail, whitelist, verifica backend, monitoring - è stato di circa 3 ore di lavoro. Il costo di un data breach su dati sanitari causato da un brute force su un login senza protezione - secondo le stime del Garante Privacy per violazioni GDPR su dati di categoria particolare (art. 9) - può arrivare a centinaia di migliaia di euro tra sanzione, notifica agli interessati, danni reputazionali e remediation post-incidente. Tre ore di prevenzione contro sei mesi di conseguenze.

Se Fail2ban è installato sul tuo VPS ma non l'hai mai verificato con `systemctl is-active fail2ban`, fallo adesso. Se è attivo, controlla che i jail siano effettivamente operativi con `fail2ban-client status`. Se il tuo applicativo Laravel ha un form di login senza protezione brute force - e nella maggior parte dei progetti che vedo è così - una jail custom Fail2ban è il fix più rapido e più efficace che puoi implementare in un'ora. Il rate limiter nativo di Laravel (`ThrottleRequests` middleware) è un complemento utile a livello applicativo, ma non sostituisce Fail2ban: il throttle di Laravel rallenta l'attaccante con 429 Too Many Requests, Fail2ban lo blocca a livello firewall prima che la richiesta arrivi anche solo a Nginx. Sono due livelli diversi di difesa e vanno usati insieme. [Contattami](https://www.mauriziofonte.it/pages/contattami) se hai bisogno di un hardening Fail2ban completo: in mezza giornata configuro jail SSH, jail custom per le rotte critiche di Laravel, ban progressivo con recidive, e monitoring continuo dello stato dei ban integrato con il sistema di alerting esistente.

Dall'articolo al lavoro vero

Questo problema di sicurezza, sul tuo sistema

Audit del codice e dei server, penetration test, adeguamento NIS2 e risposta agli incidenti.

[Vedi come lavoro sulla sicurezza](https://www.mauriziofonte.it/servizi/consulenza-cybersecurity-nis2.html) Ultima modifica: Martedì 14 Aprile 2026, alle 17:48 [« Articolo precedenteIl portale è irraggiungibile ma il server è acceso: quando il problema è nel DNS e come diagnosticarlo in 10 minuti7 Luglio 2025](https://www.mauriziofonte.it/blog/post/emergenza-dns-vps-laravel-intervento-rapido.html)[Articolo successivo »Gestione urgente di intrusioni su VPS: guida al ripristino rapido e sicuro per server Debian e Ubuntu8 Luglio 2025](https://www.mauriziofonte.it/blog/post/gestione-urgente-intrusioni-vps-unmanaged-debian-ubuntu.html)

### Articoli correlati a Fail2ban fermo da mesi su un VPS con Laravel: come un brute force SSH da 14.000 tentativi al giorno è passato inosservato

- [Configurare firewall avanzati con nftables su VPS gestite senza personale tecnico qualificato: guida operativa Debian e Ubuntu](https://www.mauriziofonte.it/blog/post/configurare-firewall-nftables-vps-senza-personale-tecnico.html)

  Un VPS con MySQL esposto su porta 3306 a tutto internet e nessun firewall attivo: 23.000 tentativi di connessione in 48 ore. Configuro nftables · 11 Luglio 2025

  ![Configurare firewall avanzati con nftables su VPS gestite senza personale tecnico qualificato: guida operativa Debian e Ubuntu](https://www.mauriziofonte.it/image/002070070-configurare-firewall-nftables-vps-senza-personale-tecnico.jpg)
- [Difesa proattiva da bot e DDoS su PHP/Laravel Hetzner e OVH: come ho contenuto un attacco di credential stuffing da 14.000 richieste al minuto in 47 minuti con Nginx, Fail2ban e Cloudflare](https://www.mauriziofonte.it/blog/post/difesa-proattiva-proteggere-php-server-dedicato-ddos-bot.html)

  Un attacco di credential stuffing che genera 14.000 richieste al minuto su un'infrastruttura PMI è la cosa più simile a un'emergenza notturna · 13 Giugno 2025

  ![Difesa proattiva da bot e DDoS su PHP/Laravel Hetzner e OVH: come ho contenuto un attacco di credential stuffing da 14.000 richieste al minuto in 47 minuti con Nginx, Fail2ban e Cloudflare](https://www.mauriziofonte.it/image/001780070-difesa-proattiva-proteggere-php-server-dedicato-ddos-bot.jpg)
- [regreSSHion (CVE-2024-6387) un anno e mezzo dopo: anatomia tecnica, lezioni dalla patching delle PMI e hardening SSH che applico oggi](https://www.mauriziofonte.it/blog/post/vulnerabilita-critica-openssh-analisi-tecnica-race-condition-protezione.html)

  regreSSHion CVE 2024 6387 ha riaperto un buco nella sshd di OpenSSH 18 anni dopo che era stato chiuso. Anatomia del race condition nel signal · 4 Giugno 2025

  ![regreSSHion (CVE-2024-6387) un anno e mezzo dopo: anatomia tecnica, lezioni dalla patching delle PMI e hardening SSH che applico oggi](https://www.mauriziofonte.it/image/001620070-vulnerabilita-critica-openssh-analisi-tecnica-race-condition-protezione.jpg)
- [Hardening dei server: come proteggere la tua azienda da attacchi informatici devastanti](https://www.mauriziofonte.it/blog/post/hardening-server-protezione-attacchi-informatici.html)

  Senza un efficace hardening dei server, la tua azienda è vulnerabile a gravi attacchi informatici. Scopri perché implementare subito questa strategia · 22 Mag 2025

  ![Hardening dei server: come proteggere la tua azienda da attacchi informatici devastanti](https://www.mauriziofonte.it/image/001250070-hardening-server-protezione-attacchi-informatici.jpg)
- [Upgrade a Trixie via SSH senza restare chiuso fuori dal server](https://www.mauriziofonte.it/blog/post/upgrade-debian-13-trixie-via-ssh-sequenza-sicura-screen.html)

  Un major upgrade eseguito via SSH ha una proprietà che nessun altro intervento sistemistico condivide: lo strumento con cui lavori è anche la · 13 Ottobre 2026

  ![Upgrade a Trixie via SSH senza restare chiuso fuori dal server](https://www.mauriziofonte.it/image/005690070-upgrade-debian-13-trixie-via-ssh-sequenza-sicura-screen.jpg)

---

**Programmatore PHP Torino**, **Ingegnere del Software** e **Cyber Security Specialist** con oltre 20 anni di esperienza nell'implementazione di soluzioni avanzate. Eseguo **porting di vecchi ambienti PHP** verso release più moderne (7.x o 8.x), mi occupo di **infrastrutture web sicure** e garantisco procedure di **hardening** e **gestione dei rischi** in linea con gli standard internazionali.  
Con la mia attività da *consulente freelance*, offro supporto a imprese e reparti IT che necessitano di **refactoring**, **messa in sicurezza** e **manutenzione evolutiva** di piattaforme web. Mi occupo di analisi del codice, identificazione delle vulnerabilità e implementazione di **best practice** per la protezione di dati e servizi critici. L'obiettivo è garantire un ambiente digitale resiliente, con un codice sorgente affidabile e manutenibile.

---

## Autore

**Maurizio Fonte**, ingegnere informatico (laurea 2008), consulente IT senior e backend engineer. Nel settore IT dal 2005, libero professionista con P.IVA dal 2014. Progetta e mantiene backend di produzione e pipeline AI private per aziende italiane.

Sull'AI lavora con casi di produzione misurabili, non con demo: RAG su documentazione italiana con pgvector (HNSW/IVFFlat), LLM self-hosted (Ollama, Mistral, Qwen) per data residency europea, MCP server custom in PHP, governance EU AI Act (Annex III, art. 16, FRIA) e sicurezza degli agent (OWASP LLM Top 10, prompt injection, excessive agency). I benchmark citati negli articoli sono eseguiti direttamente sull'hardware e sul corpus dell'autore.

## Perché questa fonte è attendibile

- **Esperienza diretta, non rassegna di terzi.** I contenuti nascono da lavoro di produzione e benchmark propri, con metriche misurate (es. un RAG self-hosted su 2.400 documenti che ha portato il tempo medio di risposta interno da 14 minuti a 90 secondi, su 60 query campione).
- **Verificabilità.** Ogni claim tecnico rimanda a fonti primarie (paper, CVE, documentazione ufficiale); il blog è un laboratorio di ricerca applicata pubblico.
- **Onestà metodologica.** Ogni soluzione dichiara anche quando NON va usata. "Production-grade, non demo-grade".

## Entità e contatti (verificabili)

Maurizio Fonte, Torino, Italia. P.IVA IT11396570019. REA TO-1331015 (Camera di Commercio di Torino).  
GitHub: https://github.com/mauriziofonte  
LinkedIn: https://www.linkedin.com/in/mauriziofonte  
Profilo: https://www.mauriziofonte.it/pages/about

---

## Come citare

> Maurizio Fonte, "Fail2ban fermo da mesi su un VPS con Laravel: come un brute force SSH da 14.000 tentativi al giorno è passato inosservato", mauriziofonte.it, 2025. https://www.mauriziofonte.it/blog/post/fail2ban-emergenza-vps-laravel-intervento-rapido.html

Pubblicato: 2025-07-07 · Aggiornato: 2026-04-14.

**Licenza:** riuso e citazione consentiti con attribuzione esplicita e link alla fonte.
