Andrea Sodomaco - divagazioni ICT
010101010111001101100101001000000111010001101000011001010010000001110011011011110111010101110010011000110110010100101100001000000100110001110101011010110110010100100001
Come bloccare rapidamente con un semplicissimo firewall l'accesso ad alcuni ip su sistema linux utilizzando iptables e il kernel network filtering, in maniera facile, sicura e gratuita. Esempi pratici relativi ad un web server.
L’argomento ha una vasta gamma di soluzioni sia con l'utilizzo di funzioni del S.O. che con l'adozione di innumerevoli software che operano a svariati livelli. Non è scopo di questa pagina analizzare l'argomento in maniera esaustiva né di fornire una soluzione per la protezione completa di un web server.
Lo scopo è fornire una soluzione rapida, sempre disponibile ed efficace che richiede una certa conoscenza teorica e dà la possibilità di essere sviluppata e customizzata.
Se le applicazioni pratiche di questa soluzione non sono particolarmente ampie lo è la valenza teorica dell'approccio che può avere uno scopo didattico.
Quando si gestisce un sito web, che questo sia o meno protetto da altri meccanismi di sicurezza, si può avere la necessità di bloccare rapidamente alcuni ip che eseguono operazioni pericolose per la sicurezza o il corretto funzionamento del sito. A volte si tratta di veri e propri attacchi hacker ma a volte anche solo dei bot tarati male o attività più maldestre che pericolose.
Ci sono svariati modi per farlo come ad esempio firewall esterni o configurazioni del web server. Lo strumento che propongo è l'utilizzo del sistema di network filtering integrato nel server linux. Il vantaggio di questo approccio è di essere sempre disponibile e molto efficiente: non tutti dispongono di un firewall esterno e il filtraggio di ip a livello di web server potrebbe essere poco efficace in caso ad esempio di DoS Attack molto intensi.
Utilizzare in maniera scorretta i comandi iptables può interrompere completamente il networking del sistema. Potrebbe essere impossibile accedere con ssh dopo l'esecuzione di un comando errato.
Per i sistemi locali assicurarsi di avere l'accesso fisico in console. Per i sistemi remoti assicurarsi di avere un sistema di console remota indipendente da ssh.
I comandi iptables non sono permanenti, in caso di blocco totale del networking della macchina, il reboot ripristinerà la situazione pregressa. Verificare di poter eseguire il reboot da remoto anche in assenza di connessione internet verso la macchina.
Il network filtering di linux può essere usato da altri tools e software in uso sul vostro sistema (esempi abbastanza diffusi sono firewalld e fail2ban). Per verificare ciò, esegui il seguente comando e controlla l'output
# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Se il risultato è diverso da quello riportato qua, è molto probabile che ci siano altri tool che utilizzano il network filtering del kernel: non procedete se non sapete quello che state facendo!
Faccio riferimento al caso di un web-server ma l'esempio può essere facilmente declinato a un mail server o ad un installazione di mysql o altro.
Ecco un esempio di log di nginx che molti webmaster avranno visto
97.74.229.113 - - [01/Nov/2022:05:04:59 +0100] "GET /admin//config.php HTTP/1.1" 404 20 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 45.227.254.54 - - [01/Nov/2022:05:54:01 +0100] "\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 400 166 "-" "-"
Se e quanto questo genere di accessi possano essere realmente preoccupanti esula da questa trattazione. Supponiamo di voler comunque bloccare l'ip.
Per chi è completamente digiuno di iptables, prima di iniziare consiglio di dare un'occhiata a questo how-to
Soluzione immediata e drastica per bloccare l'ip 45.227.254.54:
# iptables -I INPUT -s 45.227.254.54 -j DROP
Il comando va eseguito da root o va anteposto il comando sudo.
Questo semplice comando blocca qualsiasi accesso al networking della macchina dall'indirizzo specificato. Se l'attività di un determinato ip sta causando malfunzionamenti al tuo server web in questo modo risolvi il problema alla radice.
Dopo aver eseguito il comando la situazione sarà questa:
# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination DROP all -- 45.227.254.54 0.0.0.0/0 Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
Per eliminare tutte le regole della INPUT chain eseguire il seguente comando (sempre da root o anteponendo sudo)
# iptables -F INPUT
Questo ripristinerà la situazione iniziale
Il network filtering del kernel fa passare tutto il traffico in entrata attraverso le cosiddette chain ossia degli elenchi di regole che sono formate da due parti: i criteri di selezione (match) e l'azione associata (target).
In particolare ci sono le 3 chain predefinite INPUT, FORWARD, OUTPUT. La parte che ci interessa è che tutto il traffico entrante passa per la chain INPUT che inizialmente è vuota e la policy di default è ACCEPT per cui tutto il traffico viene accettato e passa all'elaborazione del kernel che lo rende disponibile ai software della macchina.
Il comando
# iptables -I INPUT -s 45.227.254.54 -j DROP
inserisce all’inizio (-I) alla chain INPUT la regola che individua tutto il traffico proveniente (-s sta per source) dall'ip 45.227.254.54 e lo associa all'azione DROP (ossia la cancellazione immediata)
Nella situazione che si viene a creare dopo l'esecuzione del comando tutto il traffico entrante che appunto passa per la chain INPUT viene confrontato con la regola inserita e se corrisponde, ossia se proviene dall'ip specificato, l'azione associata (target) viene applicata. In questo caso il traffico viene cancellato. Il traffico che non corrisponde alla regola passa alla regola successiva se c'è. Non essendoci altre regole il traffico viene accettato.
Sviluppiamo meglio la soluzione per renderla più gestibile e soprattutto più robusta nell'evitare interferenze con altri tool o altri servizi.
Partiamo dall'osservare che di solito gli attacchi riguardano solo pochi servizi e dunque poche porte tcp. Anche nel caso si tratti ad esempio di un'attività di port scanning questa risulterà fastidiosa solo sui servizi attivi e sulle relative porte esposte.
Riprendiamo l’esempio pratico sopra descritto: l'idea è di gestire il traffico di questo servizio in una chain separata. Questa soluzione per lo meno evita interazioni indesiderate con altri servizi ed in particolare con ssh.
Procediamo con i seguenti comandi
# iptables -N myhttp # iptables -N myblocked # iptables -I INPUT -p tcp --dport 80 -j myhttp # iptables -I INPUT -p tcp --dport 443 -j myhttp # iptables -I myblocked -j DROP # iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination myhttp tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 myhttp tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain myblocked (0 references) target prot opt source destination DROP all -- 0.0.0.0/0 0.0.0.0/0 Chain myhttp (2 references) target prot opt source destination
In questo modo abbiamo creato due chain. Sulla chain myhttp viene indirizzato tutto il traffico http e https (quello in entrata sulle porte 80 e 443). Il traffico che non riguarda http o https non corrisponde alle regole inserite nella chain INPUT e dunque verrà comunque accettato. La chain myhttp al momento non contiene alcuna regola e anche tutto il traffico http e https pur entrando nella chain passerà liberamente. L'altra chain denominata "myblocked" blocca tutto il traffico indirizzato ad essa e al momento non viene usata.
Ora per bloccare il traffico http e https proveniente da un determinato ip sarà sufficiente il seguente comando:
# iptables -I myhttp -s 45.227.254.54 -j myblocked
Questo comando inserisce nella chain myhttp una regola che inoltra sulla chain myblocked il traffico che proviene dall'ip 45.227.254.54. A sua volta la chain myblocked lo bloccherà.
Il vantaggio di questo approccio è che una volta fatta l'impostazione iniziale siamo sicuri di non interferire con gli altri servizi (evitando il rischio di bloccare ad esempio ssh) visto che andremo a modificare esclusivamente la chain myhttp. Inoltre se inseriamo vari ip fra quelli che puntano a myblocked nella chain myhttp, potremmo gestire uniformemente il tipo di comportamento voluto. Ad esempio sostituendo
The advantage of this approach is that once the initial setup is done, we are sure not to interfere with other services (avoiding the risk of blocking, for example, ssh) since we will be modifying exclusively the myhttp chain. Furthermore, if we insert various IPs into myhttp that point to myblocked chain, we can uniformly manage the desired behavior. For examp... -j DROP
con
.... -j REJECT --reject-with icmp-port-unreachable
Oppure aggiungendo un log rispetto agli ip bloccati con
# iptables -I myblocked -j LOG --log-prefix "MYBLOCKED:"
Ottenendo una chain fatta così:
# iptables -n -L myblocked Chain myblocked (1 references) target prot opt source destination LOG all -- 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix "MYBLOCKED:" REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
Con questa soluzione otteniamo due risultati. Il primo è di ottenere un log in syslog ogni volta che del traffico viene bloccato. Il secondo è di adottare un modo più corretto per bloccare il traffico.
Fra "DROP" e "REJECT --reject-with icmp-port-unreachable" la differenza sostanziale è che nel primo caso il traffico è totalmente ignorato mentre nel secondo c'è una risposta del server che comunica al mittente che il traffico viene rifiutato (vedi icmp protocol).
Con il DROP l'effetto che si ottiene è che il mittente bloccato percepisce una situazione identica a quando il server è spento o scollegato dalla rete. Inoltre con il DROP si ha il minimo effort per il kernel che non viene impiegato a dare una risposta seppur minima, rendendo eventuali DoS attack ancora meno efficaci. Per contropartita un client che viene bloccato dal DROP riproverà più volte a connettersi al server come previsto dal protocollo TCP/IP rinunciando solo dopo un timeout abbastanza lungo.
Con il REJECT with icmp-port-unreachable invece il client avrà evidenza immediata di essere stato bloccato (non potrà ovviamente sapere se il servizio non è attivo sul server o se solo il suo ip è stato bloccato). Nel caso di client che comunque rispettano i protocolli questa modalità risulterà più efficiente e corretta. Questa soluzione ha come controindicazione che rende evidente ad un potenziale hacker che il server esiste e potrebbe avere altre vulnerabilità.
Un caso specifico che vale la pena citare è che nel caso si protegga un server SMTP (porta 25) che invia posta esterna, va usato il REJECT with icmp-port-unreachable perché i filtri antispam potrebbero verificare l'esistenza del server. Test che usando il DROP fallirebbe.
Un'alternativa che potrebbe essere presa in considerazione è icmp-host-unreachable
Ci sono molti temi da approfondire e possibili enhancement come ad esempio rendere persistenti le modifiche, gestire in maniera performante un gran numero di ip o dei range, integrarsi in maniera automatica con la propria applicazione.
Spero di riuscire ad approfondire alcuni di questi aspetti in altri articoli anche in base ai feedback che riceverò.
Screen-shot del risultato della verifica dell'ip sul sito abuseipdb.com
Se dai log risulta un'attività sospetta di un determinato ip prima di bloccarlo si può verificare l'attività malevola dell'ip su un sito specializzato. Io utilizzo spesso abuseipdb.com
I siti di questo tipo non solo consentono di verificare la pericolosità di un ip ma possono anche fornire delle black-list da usare preventivamente.
iptables non è l'unico tool che permette di manipolare il net filtering del kernel. Delle valide alternative potrebbero essere ufw (ritenuto più user friendly) o nftables (la versione più moderna di iptables). Io preferisco iptables perché sono spesso preinstallate, hanno una vasta documentazione disponibile online, ma non vedo controindicazioni nell'utilizzo delle alternative.
Un caratteristica di iptables (a differenza ad esempio di ufw) è che, manipolando le tabelle in memoria del kernel, le modifiche eseguite non sono permanenti e svaniscono al reboot in mancanza di accorgimenti espliciti per salvarli. Questo è un onere in più per lo sviluppo di applicazioni basate sul tool, ma un grosso vantaggio in caso di test su macchine remote in quanto in casi di problemi gravi causati da comandi inappropriati sarà possibile rimediare riavviando il sistema.