Come configurare un firewall con iptables
Ho reinstallato da qualche giorno il server che gestisce la mia rete locale:
erano quattro anni che gli era stato installato un Linux, non so più neanche quale versione
(forse era una Red Hat 5.1), e a furia di aggiornarne i pacchetti era diventato un bordello; meglio
a questo punto ripartire da zero, riconfigurando per bene tutti i programmi.
Una volta messi a posto i servizi essenziali (DNS, posta, Web, file server)
devo ora sistemare il firewall.
Tra le altre cose, sono passato al nuovo kernel 2.4: ormai lo ritengo sufficientemente
maturo, anche se so che nei primi tempi dovrò stargli particolarmente dietro con gli
aggiornamenti. La nuova versione del kernel ha tra le altre cose una gestione avanzata dei filtri
di rete, quelli che stanno appunto alla base della creazione delle regole di un firewall: piuttosto
che riadattare il mio vecchio script ho deciso quindi di ripartire anche in questo caso da zero.
Il primo suggerimento che vi do, almeno finché state sperimentando
tutte le possibilità che vi vengono offerte, è quello di compilare
le opzioni della sezione netfilter come moduli, in modo
tale da poterli caricare e scaricare a piacimento, attivando quindi in maniera dinamica
le opzioni che interessano. In questo modo poi se come nel mio caso disponete già
di uno script che utilizza ipchains, e volete utilizzarlo in attesa di convertirlo a iptables,
potete caricare il modulo ipchains e utilizzare in maniera quasi indolore la vecchia
sintassi (i problemi sono più che altro legati al caricamento dei moduli wrapper per
connessioni particolari come IRC e FTP). Nel momento in cui volete fare esperimenti con iptables
rimuovete questo modulo dal sistema e caricate quelli nuovi.
Vediamo ora quindi quali sono le funzioni che mi interessano in un firewall
e cosa sono riuscito ad ottenere:
-
Il server deve funzionare da gateway per tutta la rete locale: deve
quindi gestire e rimappare correttamente i collegamenti TCP e UDP tra le macchine
collegate alla LAN e le macchine su Internet.
-
Nel mentre le macchine della rete locale possono considerarsi fidate, devono
essere filtrati tutti i tentativi di accesso dall'esterno a servizi che non
voglio vengano utilizzati.
-
Devono essere in una qualche maniera controllati e filtrati anche eventuali tentativi
di attacco più consistenti, quali scanport, flood e DoS in genere. Un
ulteriore requisito potrebbe essere quello di bloccare temporaneamente
l'accesso agli indirizzi da cui proviene l'attacco.
-
Voglio che sia disponibile dall'esterno l'accesso al servizio VNC di
una macchina che sta all'interno della rete.
-
Tutti i tentativi di intrusione devono essere loggati, possibilmente
in un database SQL cosicché sia possibile ricavarsi facilmente delle statistiche periodiche.
Armato di pazienza mi sono letto a fondo il testo
Iptables tutorial che,
come dice il titolo, spiega nel dettaglio il funzionamento dei filtri ed illustra alcuni esempi di
firewall di diverse tipologie (con e senza DMZ, con e senza gateway, con server DHCP o modem ADSL)
commentandoli nel dettaglio. Sono partito appunto dalla versione base dello script proposto per aggiungerci
ciò di cui ho bisogno.
In pratica ho accolto l'idea di creare canali differenziati su cui far confluire
il traffico dei pacchetti TCP, UDP e ICMP, in modo tale da alleggerire il carico della CPU e
ottimizzare i tempi di reazione; nella sezione ICMP ho però aggiunto un paio di linee che
controllano la lunghezza dei pacchetti di ping ricevuti: se eccedono la lunghezza
standard (solitamente 84 byte, però il traceroute di Windows 2000 utilizza pacchetti lunghi
92 byte) vengono scartati, in maniera tale da bloccare primitivi tentativi di flood.
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -m length \
--length 93: -m limit --limit 1/minute -j ULOG \
--ulog-nlgroup 1 --ulog-prefix "Ping flood: "
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -m length \
--length 93: -j DROP
Sempre a proposito della lunghezza dei pacchetti e del traceroute ho aggiunto poi una regola
nel controllo dei pacchetti UDP in arrivo per permettere l'accesso dei pacchetti dei
traceroute: in pratica controllo se lunghezza e TTL corrispondono e se si tratta di
una porta alta, nel qual caso accetto il pacchetto.
$IPTABLES -A udp_packets -p UDP -s 0/0 --destination-port 33000:
-m length --length 38 -m ttl --ttl 2 -j ACCEPT
Ho aggiunto inoltre all'inizio della definizione del canale di input due linee che sfruttano
il modulo esterno ipt_psd per individuare e loggare scansioni delle porte,
scartando immediatamente i pacchetti in arrivo.
$IPTABLES -A INPUT -m psd -m limit --limit 1/minute -j \
ULOG --ulog-nlgroup 1 --ulog-prefix "Scanport: "
$IPTABLES -A INPUT -m psd -j DROP
Con un piccolo trucchetto ho reso invece invisibile il server durante la fase di
forward, facendolo sparire anche dai traceroute. In pratica nella tabella
mangle del canale di prerouting ho aggiunto una regola che incrementa di uno
il TTL di tutti pacchetti in transito.
$IPTABLES -t mangle -A PREROUTING -j TTL --ttl-inc 1
Il passo successivo è stato quello di reindirizzare le porte 5800 e 5900,
associate al server VNC, alla macchina che voglio sia amministrabile dall'esterno. Fate attenzione
che non è sufficiente limitarsi ad inserire nella tabella di prerouting
del nat la porta del server con l'indirizzo locale, ma occorre rendere accessibile
tale porta dall'esterno e attivare il forward dal server alla porta della macchina
all'interno della rete:
$IPTABLES -A tcp_packets -p TCP -s 0/0 --dport 5800 -j allowed
$IPTABLES -A tcp_packets -p TCP -s 0/0 --dport 5900 -j allowed
$IPTABLES -A FORWARD -p TCP -i $INET_IFACE -o $LAN_IFACE \
-d 192.168.4.7 --dport 5800 -j ACCEPT
$IPTABLES -A FORWARD -p TCP -i $INET_IFACE -o $LAN_IFACE \
-d 192.168.4.7 --dport 5900 -j ACCEPT
$IPTABLES -t nat -A PREROUTING -p tcp -d $INET_IP --dport 5800 \
-j DNAT --to 192.168.4.7:5800
$IPTABLES -t nat -A PREROUTING -p tcp -d $INET_IP --dport 5900 \
-j DNAT --to 192.168.4.7:5900
Come ultima cosa vediamo in che maniera sono riuscito a far registrare tutti i log
del firewall in una tabella di MySQL. Come avrete notato negli esempi
che vi ho inserito, io non utilizzo il target LOG bensì il più nuovo
ULOG, che lavora in userspace e permette quindi, tramite un
programma in continuo ascolto, di gestire tali informazioni nella maniera più
disparata. Il programma da me utilizzato si chiama ulogd e permette di salvare
il log in un formato molto simile a quello del log si sistema, in un formato più schematico,
ma sopprattutto permette di importare tutte le informazioni in una tabella MySQL, presto anche
Postgres. Tutto quello che occorre fare, a parte caricare il modulo per attivare il target ULOG,
è di specificare nella regola di iptables il canale sul quale mandare
tali informazioni (ne gestisce fino a 32), dopodiché bisogna configurare il demone
affiché utilizzi lo stesso canale e possa utilizzare il database.
Eccovi la mia configurazione:
nlgroup 1
logfile /var/log/ulogd.log
loglevel 5
plugin /usr/lib/ulogd/ulogd_BASE.so
mysqlhost localhost
mysqldb Firewall
mysqltable ulog
mysqlpass *****
mysqluser *****
plugin /usr/lib/ulogd/ulogd_MYSQL.so
Nella versione 1.1 dello script ho aggiunto la possibilità
di bloccare gli IP degli attaccanti per un periodo stabilito, impedendo
quindi che possano accedere al sistema. Per fare questo utilizzo il target NETLINK,
che richiede il caricamento di un modulo apposito, e un programmino scritto da me che
accede al dispositivo /dev/netlink, legge i pacchetti in arrivo e ne blocca i
mittenti. Allo script ho dovuto aggiungere le seguenti regole
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -m length \
--length 85:9999 -j NETLINK --nldrop
$IPTABLES -A INPUT -m psd -j NETLINK --nldrop
relative al filtro dei ping flood e dei portscan; il parametro --nldrop mi permette di
scartare subito il pacchetto senza dover aggiungere una ulteriore regola. Quando il demone riceve
un pacchetto dal dispositivo, si ricava il mittente e aggiunge dinamicamente al firewall
una regola che scarta qualsiasi nuovo pacchetto originato da tale indirizzo;
contemporaneamente utilizza il comando di sistema at per posticipare dell'intervallo di
tempo voluto la regola che cancella quella precedentemente inserita. La sintassi del programma
è molto intuitiva e permette di specificare il numero di minuti desiderato o il
nome di un altro dispositivo.
Usage: BlockBadIP [-hvbds] [-m minutes] [-f device] [-p pidfile]
-h This help.
-v Show version.
-b Force daemon into the background.
-d Turn on debugging mode.
-s Syslog blocked adresses.
-m minutes Block bad IPs for specified minutes instead of 120.
-f device Use specifed device instead of /dev/netlink.
-p pidfile Write the daemon PID to this file.
Non tutti i moduli da me utilizzati fanno parte della distribuzione ufficiale
del kernel, molti vengono aggiunti da una release all'altra una volta dichiarati stabili. Per
inserirli quindi del proprio pinguino consiglio di utilizzare
Patch-O-Matic,
un sistema interattivo incluso nel pacchetto iptables che permette di scegliere quali patch
inserire nel proprio kernel tra quelle ancora non presenti oppure aggiornate.
RIFERIMENTI
Vi pongo qui lo script di gestione del firewall nell'ultima versione da me
realizzata, vi inserisco inoltre i link alla documentazione e ai programmi che ho utilizzato e citato
nel documento.