Questo articolo nasce da un esperienza personale, che ha causato diversi problemi al server su cui ospito i miei siti (e anche di altre persone).
Quindi partiamo dall’inizio, e veniamo alle origini del problema, tutto nasce da una svista, nell’abilitare il mod_ajp sul server apache, abilitiamo per errore il parametro:
ProxyRequest On |

Dapprima non succede niente, ma qualche ora iniziano ad arrivare connessioni che ci sfruttano come proxy, e nel giro di 24 ore il nostro server è entrato di diritto in qualche botnet/rete di proxy o chissá cosa, fatto sta che oggi siamo arrivati ad avere centinaia di richieste al secondo, che in pratica saturavano il nostro server e impedivano la normale fruibilitá dei siti ospitati (tempi di risposta sull’ordine dei minuti si avevano).
E qui comincia la fase di indagine. Mettendoci in “tail -f” sul file /var/log/apache2/error_log, riscontravamo un continuo flusso di errori del tipo:
[Sun Apr 06 15:49:21 2014] [error] [client 216.144.249.216] proxy: DNS lookup failure for: ads.yahoo.com returned by http://ads.yahoo.com/get-user-id?ver=2&s=5426229&ts=1396792101&sig=7409dd0b6294e8f2, referer: http://ads.yahoo.com/st?ad_type=iframe&ad_size=728x90§ion=5426229&pub_url=${PUB_URL}
E nell’access.log invece compariva una corrispondente riga con la richiesta:
08.115.242.253 - - [07/Apr/2014:16:31:32 +0200] "GET http://ads.yahoo.com/st?ad_type=iframe&ad_size=728x90§ion=3730175&pub_url=${PUB_URL} HTTP/1.0" 403 523 "http://www.domarketings.com/index.php?option=com_content&view=article&id=2772:Gold-Trading:-The-Original-Heavy-Metal-Tops-the-Charts-Again&catid=161" "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.216 Safari/534.10 ChromePlus/1.5.1.0alpha1"
(in questo esempio le richieste sono differenti, ma solo per comoditá)
Ovviamente la dimensione del log era gia arrivata intorno a 600mb per l’access.log e 200mb per l’error.log. Quindi il primo intervento é stato quello di disabilitare le funzionalitá proxy (e in particolare la ProxyRequest, che ripeto era stata accidentalmente abilitata).
Come ci si aspetta sono terminate le richieste di questo tipo, ma ahinoi sono state sostituite da altri messaggi di errore:
[Mon Apr 07 10:48:31 2014] [error] [client 69.162.102.181] File does not exist: /var/www/st, referer: http://www.bestonlinebizs.com/index.php?option=com_content&view=article&id=776:Bad-Credit-Secured-Personal-Loans:-Money-to-Mend-Credit-Turf&catid=29
Questo probabilmente causato dal fatto che la nostra macchina oramai era stata assorbita da qualche rete misteriosa :). E nell’access.log restavano quel tipo di richieste. Quindi il problema persisteva, perchè ora si non facevamo più da proxy, ma la nostra macchina da sola non era in grado di sopperire a tutte quelle richieste che continuavano ad arrivare.
A questo punto inizia la ricerca su internet. La prima soluzione che viene proposta è quella di abilitare un modulo di apache, chiamato mod_envise, che si occupa di bloccare temporaneamente le richieste che in un certo intervallo di tempo superano una certa soglia, impostabile.
Qui di seguito vi riporto brevemente come l’abbiamo configurata per debian, anche se ha prodotto scarsissimi risultati.
Prima di tutto è un modulo che va installato, il comando apt-get è il seguente:
apt-get install libapache2-mod-evasive |
Dopo di che creiamo in /etc/apache2/mods_available un file chiamato mod_evasive.con e inseriamo le seguenti entry:
<IfModule mod_evasive20.c> DOSHashTableSize 3097 DOSPageCount 2 DOSSiteCount 50 DOSPageInterval 1 DOSSiteInterval 1 DOSBlockingPeriod 60 DOSEmailNotify your@email.it DOSLogDir /var/log/apache2/evasive </IfModule> |
Mentre il file mod_evasive.load, nella stessa cartella dovrebbe essere gia presente. A questo punto diamo il comando:
a2enmond mod_evasive |
Dove:
- DOSSiteCount indica le richieste oltre il quale un ip viene bloccato, e
- DOSBlockingPeriod indica quanto deve durare questo blocco
- DOSSiteCount Indica invece la soglia del numero di richieste sulla stessa pagina
- DOSSiteInterval Indica invece la soglia di tempo per il DOSSiteCount
- DOSPageInterval fa lo stesso per DosPageCount
A questo punto si può riavviare apache con:
service apache2 restart |
Ma come dicevo questo non ha prodotto grandi risultati, infatti le richieste erano calate di un numero abbastanza irrisorio, e le performance non accennavano a migliorare.
Altri siti suggerivano di utilizzare mod_security, ma a quanto riportato anche sul sito di apache, questa soluzione aveva risultati minimi, quindi ho deciso di non perdere tempo nell’abilitare anche questo modulo.
Un altro accorgimento che è stato usato, è stato di dare un errore 403 a tutti i tentativi di accesso a qualsasi url che non era gestito dal nostro server, questo è stato fatto creando un virtual host generico, che gestiva tutti i casi non coperti dagli altri virtual host:
<VirtualHost *:80> DocumentRoot "/var/www" ServerName 195.154.235.160 <Directory "/var/www/"> deny from all </Directory> ErrorLog /var/log/apache2/dummy.log CustomLog /var/log/apache2/dummy-access.log combined </VirtualHost> |
In questo modo tutto il traffico non lecito o non corretto veniva rifiutato, e tutto inviato in un log specifico.
In alcune mailing list si suggeriva di aspettare che il fenomeno scemasse da solo, ma diceva anche che poteva durare ore/giorni/settimane, e non volevo aspettare così tanto. Ma ogni ip faceva diverse centinaia di richieste nell’arco di una giornata, e questo era l’unico aspetto su cui intervenire, cercare di non far arrivare queste richieste all’apache. E qui entra in gioco l’idea di far intervenire il firewall. Perchè non usare lui per fargli bloccare le richieste? E quindi anche con l’aiuto di google, metto a punto uno script per far bloccare le richieste proxy di apache direttamente da iptables:
#!/bin/bash tail -f /var/log/apache2/dummy-access.log | awk ' BEGIN { blocked_ips="" } / http/ { if (! index(blocked_ips, $1)) { blocked_ips = blocked_ips " " $1 "date" | getline current_time close("date") print current_time " :: blocking " $1 iptables_block = "iptables -I INPUT -s " $1 " -j DROP" system(iptables_block) close(iptables_block) } }' |
In pratica questo script a partire dal file dummy-access.log considera tutte le linee che contenevano una richiesta che iniziava per http (e quindi indicava che si trattava di un tentativo di usare il server come proxy. A questo punto visto che il primo elemento della riga di log è proprio l’ip, controlliamo se non si trova gia nella lista di ip bloccati, se non lo è lo aggiungiamo, e inoltre aggiungiamo una regola a iptables che ignora tutte le richieste provenienti da quell’ip. Il comando iptables in questo script è:
iptables -I INPUT -s ipaddressdabloccare -j DROP |
Ora salvate questo script su un file, date il comando:
chmod +x scriptname |
Dove scriptname è il nome che avete dato al file che contiene il vostro script, e lanciatelo con
./scriptname |
E aspettate, vedrete che appena questo script inizierá a macinare ip molto velocemente, ma man mano che passano i minuti, sempre meno ip verranno aggiunti al firewall, e dopo una decina vedrete la situazione tornare pressochè alla normalitá, e i vostri tempi di risposta tornare umani. E soprattuto se ora controllerete i log, i tentativi di accesso come proxy saranno crollati drasticamente, se prima erano molte centinaia al secondo ora sono poche decine al minuto
Sono convinto che non si tratta di una delle soluzioni migliori, ma sicuramente in questa situazione è stata l’unica arma che ho trovato in grado di far tornare i miei siti utilizzabili.
Lo script non è tutto farina del mio sacco (ho modificato uno script esistente trovato qua: https://www.gosquared.com/blog/how-to-stop-a-botnet-attack che era specifico per ubuntu ufw)
Che dire spero che questo articolo vi aiuti se vi trovate in situazioni di emergenza come la mia!