Quando si parla di protezione di un server web, molto spesso si ragiona per compartimenti stagni. Il firewall si occupa di indirizzi IP e porte; Apache o nginx si occupano di HTTP; l’applicazione pensa ai propri utenti e ai propri permessi. Il problema è che un attacco reale attraversa tutti questi livelli contemporaneamente.
Prendiamo una richiesta verso /.git/config. Per il firewall di rete è semplicemente una connessione TCP valida verso la porta 443, identica a migliaia di altre. Per Apache, invece, quella richiesta ha un significato preciso: qualcuno sta cercando un file che normalmente non dovrebbe mai essere pubblico. Lo stesso vale per tentativi verso .env, vecchie console amministrative, path di exploit conosciuti o sequenze di richieste che hanno senso soltanto durante una scansione.
Da questa differenza nasce l’idea di un Reactive Web Firewall: lasciare che il livello HTTP riconosca il comportamento e usare quel segnale per reagire anche a livello di rete. Il primo vantaggio è immediato: dopo il primo match non siamo costretti a far arrivare allo stack web altre cento richieste dello stesso client. Il secondo è architetturale: detection ed enforcement rimangono separati, quindi Apache non deve trasformarsi in un gestore di nftables.
WAF e firewall non sono la stessa cosa
Un WAF decide normalmente sul singolo evento applicativo: questa richiesta passa, questa viene rifiutata. Un firewall stateful decide invece se un flusso di rete può esistere. RwF mette in comunicazione i due mondi senza confonderli: la regola applicativa genera un evento ad alta confidenza, un helper controllato lo traduce in un ban temporaneo e nftables applica la decisione ai pacchetti successivi.
Questa distinzione è importante anche per la sicurezza del sistema stesso. Dare al processo Apache privilegi generali sul firewall significherebbe ampliare enormemente il danno possibile in caso di compromissione del web server. Un helper con un protocollo minimale e un compito limitato riduce invece la superficie.
Il vero problema: sapere chi stiamo bannando
La parte più delicata non è aggiungere un elemento a un set nftables. È stabilire quale indirizzo debba entrarci. Se davanti ad Apache c’è un reverse proxy o una CDN, l’IP della connessione TCP appartiene al proxy. Bannarlo significherebbe poter oscurare il sito a migliaia di utenti con una sola richiesta ostile.
Per questo la catena Real-IP è una parte integrante dell’architettura, non un dettaglio di logging. Un header come X-Forwarded-For ha valore soltanto se sappiamo chi ce lo ha consegnato. Se qualunque client può raggiungere direttamente il backend e scegliere liberamente quell’header, può anche scegliere chi farci bannare.
Ban temporaneo, non lista nera eterna
Infine c’è una scelta di filosofia. Un errore di detection è sempre possibile, quindi il ban deve essere proporzionato. I set nftables con timeout sono particolarmente adatti: l’indirizzo entra, resta bloccato per il tempo previsto e poi scompare automaticamente. Non serve una cronologia infinita di IP e un falso positivo non diventa una condanna permanente.
Con questo modello in mente possiamo passare alla realizzazione concreta.
Architettura
L’implementazione usata qui è composta da quattro pezzi separati: mod_rwf dentro Apache, un helper asincrono raggiunto tramite Unix socket, un meccanismo di fast-ban locale basato su nftables e, opzionalmente, la propagazione del ban verso il firewall di frontiera.
Apache + mod_rwf
|
| match regola
v
403 + evento al helper
|
v
/run/reactive-web-firewall/helper.sock
|
v
fastban_v4 / fastban_v6 (nftables)
|
+--> eventuale propagazione al firewall di frontiera
Prerequisiti e compilazione del modulo Apache
Il modulo va compilato contro la stessa ABI di Apache in uso. Su Debian/Ubuntu il pacchetto utile è apache2-dev, che fornisce apxs.
apt update
apt install apache2-dev build-essential
apxs -c mod_rwf.c
apxs -i mod_rwf.la
apache2ctl configtest
systemctl reload apache2
Dopo l’installazione controlliamo che il modulo sia realmente caricato:
apache2ctl -M | grep rwf
Configurazione Apache
Le regole vengono mantenute fuori dai VirtualHost, in un file dedicato, per esempio /etc/reactive-web-firewall/apache-rules.conf. Il socket dell’helper è /run/reactive-web-firewall/helper.sock.
RwfEnabled On
RwfWhitelistIP 127.0.0.1
RwfWhitelistIP 10.0.20.0/24
RwfRuleExact "/__rwf-test__" "test"
RwfRulePrefix "/.git/" "git-probe"
RwfRulePrefix "/wp-config.php" "wp-config-probe"
RwfRuleRegex "(?i)/(\.env|\.svn|id_rsa)" "secret-probe"
Include /etc/reactive-web-firewall/apache-rules.conf
Dopo ogni modifica:
apache2ctl configtest && systemctl reload apache2
Fast-ban con nftables
Nel nostro schema i set si chiamano fastban_v4 e fastban_v6 dentro la tabella inet custom_web_fastban. I timeout fanno sì che il blocco si auto-estingua senza dover mantenere una lista infinita di IP.
nft list table inet custom_web_fastban
nft list set inet custom_web_fastban fastban_v4
nft list set inet custom_web_fastban fastban_v6
La parte legacy del fast-ban è gestita da /usr/local/sbin/custom-web-fastban e dalla configurazione nftables in /etc/nftables.d/custom-web-fastban.nft. Il servizio che carica le regole deve risultare attivo:
systemctl status custom-web-fastban.service
nft list ruleset | sed -n '/custom_web_fastban/,+80p'
Test end-to-end
Creiamo volutamente una regola innocua sul path di test e richiamiamola da una macchina non in whitelist.
curl -i https://www.example.net/__rwf-test__
La risposta attesa è 403. Subito dopo controlliamo il ban:
nft list set inet custom_web_fastban fastban_v4
# Nel nostro ambiente è disponibile anche:
check-fw-ban 203.0.113.25
Il controllo deve dirci non soltanto “presente/non presente”, ma anche dove: allowlist banIP, set temporaneo locale, firewall di frontiera e timeout residuo.
Reverse proxy e CDN: non bannare Cloudflare
Se Apache riceve la connessione da un reverse proxy o da una CDN, l’IP TCP visto dal backend non è necessariamente quello dell’attaccante. Il sistema deve fidarsi dell’header che contiene il client reale solo quando la connessione arriva da proxy esplicitamente autorizzati. In caso contrario un client potrebbe forgiare l’header e far bannare un indirizzo arbitrario.
Debug
# Apache
LogLevel alert rwf:debug
journalctl -u apache2 -f
journalctl -u custom-web-fastban.service -f
ss -xl | grep reactive-web-firewall
ls -l /run/reactive-web-firewall/helper.sock
Per misurare il costo del modulo possono essere registrati anche i timestamp apache_start_us, rwf_enter_us, rwf_exit_us e apache_end_us. È utile per dimostrare che la decisione applicativa non sta aggiungendo latenze rilevanti.
Cosa deve restare fuori da RwF
RwF non sostituisce patching, autenticazione, rate limiting, segmentazione e logging. È una reazione rapida a un segnale ad alta confidenza. Le regole devono essere poche, spiegabili e verificabili.
Approfondimenti collegati
- Apache Reverse Proxy, il punto di ingresso HTTP/HTTPS su cui costruire correttamente Real IP e logging.
- Apache e log, per conservare client reale, status e contesto che hanno portato alla detection.
- Firewall proattivo, per il modello generale detection → enforcement → timeout → audit.
- IR Stack, per correlare il segnale web con modifiche e accessi sul server.

