Avanti Indietro Indice

8.6 Prime catene

Ok, due sezioni fa abbiamo detto al kernel di buttare via tutto quanto può avere a che fare col nostro computer. Per andare avanti nella nostra configurazione, dobbiamo quindi indicare cosa invece lasciare passare. Questo si fa aggiungendo delle regole. Una soluzione potrebbe essere quella di aggiungere tutte le regole relative al traffico che vogliamo lasciare passare nelle 3 catene di base. Funzionerebbe, ma vi posso garantire che sarebbe molto facile commettere degli errori e vi posso garantire che altrettanto facilmente diventerebbero così tante da non poter essere più gestibili.

Uno degli approcci più semplici nella gestione delle regole di firewalling è quindi quello di dividere il traffico in ``flussi'' di dati dopodichè considerare indipendentemente ogni singolo flusso, evitando così di avere tutte le regole insieme e formalizzandosi in modo da evitare gli errori più comuni.

Analizzando il nostro esempio, potremmo quindi dividere il nostro traffico in 6 grandi flussi:

Possiamo poi, per esempio, creare per ogni flusso una catena e ad ogni catena associare un suo insieme di regole indipendenti da quello di tutte le altre catene.

Ma andiamo per gradi: iniziamo a creare delle nuove catene. Per fare questo dobbiamo eseguire iptables seguito da un ``-N'' e da un nome per la catena:

16: iptables -N landmz       #dalla scheda di rete eth0 alla scheda di rete eth1
17: iptables -N laninet      #dalla scheda di rete eth0 alla scheda di rete eth2
18: iptables -N dmzinet      #dalla scheda di rete eth1 alla scheda di rete eth2
19: iptables -N dmzlan       #dalla scheda di rete eth1 alla scheda di rete eth0
20: iptables -N inetdmz      #dalla scheda di rete eth2 alla scheda di rete eth1
21: iptables -N inetlan      #dalla scheda di rete eth2 alla scheda di rete eth0
Per il kernel, però, il nome che abbiamo dato ed il commento non hanno alcun significato. Dobbiamo quindi trovare un modo per indicare quali catene devono essere percorse in quali situazioni. Per fare questo, possiamo specificare 6 semplici regole. Trattandosi di regole per traffico che non è né originato né destinato al firewall, dovremo inserirle nella catena di FORWARD. Per esempio, potremmo dare dei comandi come:
23: iptables -A FORWARD -i eth0 -o eth1 -j landmz
24: iptables -A FORWARD -i eth0 -o eth2 -j laninet
25: iptables -A FORWARD -i eth1 -o eth2 -j dmzinet
26: iptables -A FORWARD -i eth1 -o eth0 -j dmzlan
27: iptables -A FORWARD -i eth2 -o eth1 -j inetdmz
28: iptables -A FORWARD -i eth2 -o eth0 -j inetlan
Il significato di queste righe è molto semplice: aggiungi (-A, append) alla catena FORWARD, una regola per cui se un pacchetto proviene dalla scheda di rete eth0 (-i) ed è destinato ad uscire dalla scheda eth1 (-o), la sua sorte deve essere decisa (-j) dalla catena landmz e così via. In pratica, abbiamo diviso tutti i pacchetti in transito nella catena di forward in 6 categorie, dove ogni categoria ha associata una catena e quindi una serie di regole. Riassumendo: Abbiamo visto quindi come una regola può identificare dei pacchetti in base alle schede di rete coinvolte. E' però possibile utilizzare molti altri criteri, per esempio: Dove ogni protocollo può aggiungere dei criteri di classificazione. Per esempio, se specifichiamo ``-p tcp'' per indicare il protocollo tcp, possiamo poi dividere i pacchetti in base anche Anche in questo caso, oltre alle estensioni fornite dai protocolli, è possibile utilizzare delle estensioni fornite da moduli.

A questo punto però, vi sarà venuto spontaneo chiedervi perché effettuare una prima classificazione in base all'hardware e non, ad esempio, in base all'indirizzo ip sorgente o all'indirizzo ip destinazione. Ancora una volta, la risposta è molto semplice: il contenuto di un pacchetto si può falsificare. Il fatto che arrivi su un'interfaccia piuttosto che un'altra, no. Utilizzando almeno per le prime regole le interfacce di input e quelle di output, avremo quindi la certezza che i pacchetti saranno valutati dalle regole contenute nella catena corretta e che nessuno potrà imbrogliarci utilizzando ip fasulli (ancora, cosa succederebbe se basassimo le nostre regole solo sugli indirizzi ip e qualcuno ci mandasse dei pacchetti provenienti da 127.0.0.1 con l'rp_filter disabilitato?).

Dalla LAN alla DMZ

Ricordandoci della configurazione della nostra ipotetica rete, abbiamo deciso che dalla nostra lan vogliamo che tutte le richieste per pagine web vengano deviate sul nostro server proxy (transparent proxy) in maniera trasparente, che i nostri utenti non controllino altre caselle di posta elettronica se non quelle da noi fornite, che possano collegarsi al nostro server web direttamente e che siano in grado di scaricare file con ftp direttamente da internet. Dall'esterno, vogliamo soltanto rendere accessibile il nostro dns, il server di posta elettronica, il nostro server web ed il server ftp. Ma lavoriamo anche qua per flussi, in modo da semplificare un po' le cose.

Iniziamo allora a creare le regole per la nostra lan, lasciando indietro (per ora) ciò che riguarda il ``deviare''. Dalla lan alla dmz, vogliamo quindi consentire:

Aggiungiamo quindi le regole corrette, esattamente come indicato qua sopra:
30: iptables -A landmz -s ! 192.168.200.0/24 -j DROP
31: iptables -A landmz -p tcp -d nostro.server.web --dport www -j ACCEPT
32: iptables -A landmz -p tcp -d nostro.server.smtp --dport smtp -j ACCEPT
33: iptables -A landmz -p tcp -d nostro.server.pop3 --dport pop3 -j ACCEPT
34: iptables -A landmz -p tcp -d nostro.server.proxy --dport webcache -j ACCEPT
35: iptables -A landmz -p tcp -d nostro.dns --dport domain -j ACCEPT
36: iptables -A landmz -p udp -d nostro.dns --dport domain -j ACCEPT
Prima di tutto, qua dentro sarebbe molto meglio indicare direttamente degli indirizzi ip al posto dei nomi dei computer. Come abbiamo già detto, infatti, il protocollo dei DNS deve essere considerato insicuro. Se proprio volessimo usare i nomi dei computer, potremmo utilizzare il vecchio file degli hosts, in /etc/hosts, dove ogni linea è formata dall'indirizzo ip di un computer seguito dal suo nome (e separati da spazi).

Come potete vedere, questa volta le regole le ``appendiamo'' (-A) non più alla catena di FORWARD bensì alla catena landmz, trattandosi di regole che andranno a discriminare il traffico tra la nostra lan e la nostra dmz.

Ora, la prima regola indica che non vogliamo che passi niente che non abbia un indirizzo ip proveniente dalla nostra rete interna. Questa regola eviterà quindi che qualcuno dei nostri utenti possa fare spoofing verso la nostra dmz o che comunque tenti di imbrogliarci con dei giochi strani sull'indirizzo ip. E' una regola un po' superflua, avendo già abilitato la protezione del kernel, ma a volte è meglio aggiungere qualche regola in più piuttosto che avere qualche regola in meno (e poi, siete sicuri che nelle prossime versioni del kernel il filtro sarà ancora disponibile o che si comporterà sempre nello stesso modo?).

Le altre sono ancora regole molto semplici. Le uniche novità introdotte rispetto prima sono la negazione (il !), che indica che perché una regola venga applicata una certa condizione non deve essere soddisfatta, ed il fatto che finalmente potete vedere diversi criteri messi in pratica. -s, specifica un ip sorgente e può essere seguito da un indirizzo ip o da un indirizzo di rete (indicato come x.x.x.x/y) o dal nome di un host. -p può essere seguito dal nome o dal numero di un protocollo (file /etc/protocols conserva le associazioni). -d, ancora, indica la destinazione di un pacchettino e può essere seguito sempre da un indirizzo ip, da un nome di host o da un indirizzo di rete (con lo stesso formato indicato prima). --dport, invece, indica una porta di destinazione e può essere seguito da un numero o dal nome di una porta (il file /etc/services conserva le associazioni). In tutti i casi, è possibile usare la negazione dopo l'indicazione del criterio.

Infine, ACCEPT dice ad iptables di accettare tali pacchetti nel caso in cui la regola risulti applicabile. Per configurare un firewall, è necessaria una buona conoscenza dei vari protocolli di rete: l'ultima riga è stata aggiunta in quanto il protocollo per la comunicazione con i dns utilizza il più delle volte connessioni udp, mentre utilizza connessioni tcp solo in condizioni particolari. Senza questa regola, il dns avrebbe funzionato sempre tranne qualche volta, e sarebbe stato estremamente difficile trovare il problema (se mai ce ne fossimo resi conto).

Per chi di voi si fosse invece chiesto come si fa a specificare una rete o che senso ha il /24, basti sapere che si tratta di un sistema estremamente comodo per indicare le netmask. Per esempio, una netmask 255.255.255.0 indica che se i primi 24 bit di due indirizzi ip sono uguali, allora i due si trovano sulla stessa rete (255.255.255.0 scritto in notazione binaria sarebbe 11111111.11111111.11111111.00000000, con 24 uno). 255.255.255.0 è quindi equivalente a /24 (ogni ottetto - 255 - sono 8 bit).

Visto però che si tratta delle prime regole ``serie'' della nostra trattazione, vediamo comunque di descriverle a parole, un po' come abbiamo fatto prima:

Ad alcuni di voi sarà venuto spontaneo chiedersi come mai è stata messa come prima regola un ``butta via'' con una negazione, anzichè una più semplice accetta (qualcosa del tipo: accetta ogni pacchetto in transito nella catena landmz che viene da un indirizzo ip facente parte della nostra lan). Il problema è che il controllo delle regole si ferma alla prima regola soddisfatta. Se fosse stata quindi un'accept, la regola sarebbe stata soddisfatta per ogni pacchetto proveniente dalla lan, e la scansione delle regole non sarebbe andata avanti a quelle successive, vanificando buona parte del nostro lavoro. Un'altra possibilità sarebbe stata quella di omettere la prima regola e di specificare ogni volta esplicitamente l'indirizzo ip, aggiungendo alle righe dalla 31 alla 36 qualcosa come ``-s 192.168.200.0/24''. Siccome però sono una persona molto pigra, ho scelto il metodo più veloce da scrivere.

L'ultima regola indicata tra parentesi, poi, non è stata da noi scritta: è implicitamente aggiunta dalla policy (DROP) che abbiamo deciso di utilizzare.

L'esperienza però mi insegna che a questo punto potrebbe essere conveniente inserire come regola 38 qualcosa di simile a:

38: iptables -A landmz -p tcp -j REJECT --reject-with tcp-reset
Questo ha a che vedere con i meandri del tcp. Ma vediamo di spiegarla in poche parole: quando avviene una connessione TCP/IP, ha luogo il solito ``three way handshake''. In pratica, per avere la certezza che i pacchetti arrivino a destinazione, il computer A manda un messaggio del tipo ``questo è il mio numero, e se ci sei, mandami il tuo'', dopodichè il computer B risponde con un messaggio del tipo ``ho ricevuto il tuo numero, questo è il mio'', infine il computer A risponde dicendo ``ho ricevuto il tuo numero'' e la comunicazione ha inizio. Normalmente però, rispettando il protocollo TCP/IP, se il computer A dovesse tentare di collegarsi ad una porta chiusa (ad un servizio non disponibile) su B, allora B dovrebbe rispondere con ``la porta è chiusa''.

Ma vediamo cosa succede nel nostro caso:

Bene, la regola 38 dice che per ogni connessione tcp arrivata in fondo alla catena (essendo stata aggiunta per ultima), invece di essere buttato via il pacchetto, deve essere utilizzata l'estensione (modulo esterno) REJECT per mandare un ``tcp-reset'', il messaggio che indica il fatto che la porta è chiusa.

Vediamo però che il target REJECT è stato specificato semplicemente con un -j, come per tutti gli altri target. Quando si parla di target, infatti, non c'è alcuna differenza tra ``estensioni'' esterne e target forniti direttamente da iptables, se non la pagina di manuale.

Alcuni di voi si potranno chiedere a questo punto ``ma che diavolo, cosa mi interessa se l'altro tentando di accedere ad una porta non consentita rimane in attesa?? Così impara per la prossima volta...''. Beh, ci sono almeno tre buoni motivi per non lasciarlo in attesa:

Questa regola la vedrete apparire molto spesso nelle catene seguenti, sempre per lo stesso motivo... Dovremmo quindi aver finito con il traffico dalla LAN alla nostra DMZ.

Dalla DMZ alla LAN

Ok, qua (a dirsi) le cose sono molto più semplici: dobbiamo consentire soltanto quei pacchettini in risposta alle richieste partite dalla LAN (i server non si devono connettere di loro spontanea volontà ai nostri client -- teoricamente, nessuno dovrebbe usarli).

Fare questo in ipchains era una cosa abbastanza complessa: bisognava specificare delle regole per consentire ogni tipo di pacchetto che ci sarebbe potuto tornare in risposta (giocando con l'opzione --syn, che seleziona i pacchetti che stabiliscono nuove connessioni) e si doveva quindi avere una buona conoscenza (se non ottima) dei vari protocolli. iptables facilita molto le cose tramite l'introduzione di ``moduli per il tracciamento delle connessioni'' (conntrack -- uno dei molti vantaggi dei firewall statefull rispetto quelli stateless).

Vediamo quindi una delle soluzioni che potremmo adottare:

40: iptables -A dmzlan -s ! 123.45.67.9/29 -j DROP
41: iptables -A dmzlan -m state --state ESTABLISHED,RELATED -j ACCEPT
42: iptables -A dmzlan -p tcp -j REJECT --reject-with tcp-reset
Bene, il primo comando è la solita regola per evitare lo spoofing. Il secondo invece, è diverso da ogni altro comando finora incontrato: -m chiede ad iptables di caricare un modulo esterno, in questo caso il modulo state (si ricorda lo stato delle connessioni), per il tracciamento delle connessioni. L'opzione --state relativa al modulo state è quello che ci permette di identificare i pacchetti: in pratica, la regola ha come significato quello di consentire tutti i pacchetti facenti parte connessioni già stabilite (ESTABLISHED, e se sono già stabilite vuol dire che sono state consentite) e tutte quelle connessioni relative a connessioni già esistenti (RELATED).

In questo caso, la parolina magica è proprio RELATED. Ci consente cioè di scaricare sul modulo state la responsabilità di identificare i pacchettini che non soltanto sono risposte a pacchetti già inviati, ma anche quelli che fanno parte dello stesso protocollo.

Vediamo però qualche esempio:

La differenza è quindi questa: ESTABLISHED, fa passare tutti i pacchetti che fanno parte di una connessione già stabilita, mentre RELATED fa passare tutti i pacchetti di controllo (errori, piuttosto che...) o connessioni relative alle connessioni conosciute.

Se state configurando un firewall, però, e la vostra preoccupazione principale è la sicurezza, vi sarà sorta spontanea una domanda: ``e se qualcuno imbrogliasse il modulo di state, facendo aprire connessioni non volute?''. Bene, in passato qualche problema di questo tipo è stato sollevato, ma il codice del netfilter dovrebbe essere attualmente abbastanza maturo per evitare questo tipo di problemi.

Vi consiglio però di seguire le mailing list dedicate, per essere eventualmente prontamente avvisati di possibili problemi, e di leggere qualche documento in più sul funzionamento del protocollo ftp.

Attenzione però che perché le regole sopra elencate funzionino, potrebbe essere necessario caricare dei moduli nel kernel, con comandi del tipo ``modprobe ip_conntrack'', ``modprobe ip_conntrack_ftp'', ``modprobe ip_conntrack_altri_moduli_di_protocolli_che_volete_utilizzare''.

Per quanto riguarda le prestazioni, il modulo state utilizza un po' più di risorse rispetto alle regole manuali, ma comunque nulla di rilevante se rapportato ai vantaggi che offre (alcune cose, non è proprio possibile farle con firewall stateless, a meno di non aprire migliaia di porte).

Un'altra cosa da dire è che prima, nella definizione della catena dalla LAN alla DMZ, ho volutamente dimenticato la regola 37:

37: iptables -A landmz -m state ESTABLISHED,RELATED -j ACCEPT
che sarebbe stata troppo prematura da discutere. In pratica, anche dalla lan alla dmz è importante far passare tutti quei dati relativi a connessioni già stabilite o comunque i messaggi di errore. Probabilmente non molto importante per reti di piccole dimensioni, ma rilevante in reti più grosse o con regole più complicate. Anche questa regola la vedrete apparire molto spesso.

Dalla LAN ad Internet

Dalla nostra lan ad internet vogliamo invece essenzialmente che passi traffico ftp (che non può essere deviato sul proxy), in modo che gli utenti possano scaricare direttamente il loro materiale. Per fare questo, basta aggiungere le regole:

44: iptables -A laninet -s ! 192.168.200.0/24 -j DROP
45: iptables -A laninet -p tcp --dport ftp -j ACCEPT
48: iptables -A laninet -m state ESTABLISHED,RELATED -j ACCEPT
49: iptables -A laninet -p tcp -j REJECT --reject-with tcp-reset
Dove però non c'è nulla di nuovo di cui parlare...

Da Internet alla LAN

Infine, per terminare con la nostra lan:

50: iptables -A inetlan -s 192.168.200.0/24 -j DROP
51: iptables -A inetlan -s 123.15.67.9/29 -j DROP
52: iptables -A inetlan -m state --state ESTABLISHED,RELATED -j ACCEPT
53: iptables -A inetlan -p tcp -j REJECT --reject-with tcp-reset
In questo caso, le prime due regole bloccano tutti quei pacchetti che hanno come mittente l'indirizzo di una delle nostre due reti.

La terza regola, invece, consente a tutte le connessioni create dall'interno di poter ricevere una risposta, senza dover scrivere troppe regole.

All'epoca di ipchains invece, non c'era un modo di ``tenere traccia'' delle connessioni ed era quindi necessario aprire molte più porte per consentire a ftp di funzionare, cosa che diminuiva notevolmente l'efficacia del firewall.

Da Internet alla DMZ

Andiamo avanti ora con la configurazione della nostra dmz. Abbiamo detto che da internet saranno accessibili i seguenti servizi:

Che si traduce linearmente in qualcosa del tipo:
54: iptables -A inetdmz -s 192.168.200.0/24 -j DROP
55: iptables -A inetdmz -s 123.15.67.9/29 -j DROP
56: iptables -A inetdmz -p tcp -d nostro.server.web --dport www -j ACCEPT
57: iptables -A inetdmz -p tcp -d nostro.server.smtp --dport smtp -j ACCEPT
58: iptables -A inetdmz -p tcp -d nostro.dns --dport domain -j ACCEPT
59: iptables -A inetdmz -p udp -d nostro.dns --dport domain -j ACCEPT
60: iptables -A inetdmz -p tcp -d nostro.ftpserver --dport ftp -j ACCEPT 
61: iptables -A inetdmz -m state --state ESTABLISHED,RELATED
62: iptables -A inetdmz -p tcp -j REJECT --reject-with tcp-reset
In questo caso, la prima regola è un po' superflua ma estremamente importante. I nostri server, infatti, potrebbero essere configurati per essere un po' meno restrittivi nei confronti dei nostri utenti. Se qualcun altro riuscisse da internet ad usare i nostri indirizzi ip, sarebbe in grado di usufruire di questi benefici.

Dalla DMZ ad Internet

Dalla DMZ ad Internet, invece

63: iptables -A dmsinet -s ! 123.15.67.9/29 -j DROP
64: iptables -A dmzinet -p tcp -s nostro.server.smtp --dport smtp -j ACCEPT
65: iptables -A dmzinet -p udp -s nostro.server.dns --dport domain -j ACCEPT
66: iptables -A dmzinet -p tcp -s nostro.server.dns --dport domain -j ACCEPT
67: iptables -A dmzinet -p tcp -s nostro.server.proxy --dport www -j ACCEPT
68: iptables -A dmzinet -m state --state ESTABLISHED,RELATED -j ACCEPT
69: iptables -A dmzinet -p tcp -j REJECT --reject-with tcp-reset
Qui qualche commento è doveroso farlo...

La prima regola consente al server smtp di inviare la posta elettronica dei nostri utenti.

Dello stesso tipo sono la seconda e terza regola, che consentono invece al dns server di effettuare delle query ricorsive ed eventualmente degli zone transfer.

La quarta regola, infine, è quella che consente al proxy server di uscire all'esterno a procurarsi le pagine non in cache. Attenzione, però, che a secondo delle configurazioni del vostro proxy server, potrebbe essere necessario aprire più porte (per fare comunicare più proxy tra di loro, per consentire l'utilizzo al proxy del protocollo ftp...).

Le ultime due sono le solite regole: consenti le risposte alle richieste effettuate, chiudi le connessioni tcp vietate.

Traffico da e per il firewall

Come ultima cosa, ci rimane da filtrare il traffico originato o destinato al firewall direttamente. Giusto a titolo esemplificativo, ecco alcune regole:

71: iptables -A INPUT -m limit --limit 10/min -p tcp --syn --dport ssh -j ACCEPT
72: iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
73: iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
74: iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset

75: iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
Buona norma sarebbe quello di consentire il meno possibile verso il firewall, e di bloccare tutto ciò che può da lui essere originato. In questo caso, viene accettato il traffico ssh ed i ping (non sempre una buona idea) e tutto il traffico relativo, mentre in uscita vengono consentite solo le risposte ai pacchettini ricevuti. Se utilizzate demoni come zebra o simili, potrebbe essere necessario aprire più porte ed allentare un po' la cintura soprattutto per quanto riguarda icmp. Ricordatevi inoltre che se non viene specificata esplicitamente una interfaccia, la catena di INPUT e di OUTPUT determineranno il comportamento di input e output da tutte le interfacce (compreso il loopback!!!). State quindi molto attenti a quello che fate.

Potete vedere anche l'utilizzo di un nuovo modulo: il limit, che consente di impostare dei limiti di frequenza, e per la prima volta dell'opzione (tcp) --syn, che seleziona soltanto le nuove connessioni (i famosi pacchettini ``mandami il tuo numero''). Questo per evitare un problema con ssh segnalato diverso tempo fa (e probabilmente già corretto) per cui in determinate condizioni e con reti molto veloci era possibile fare hijacking di una connessione tentando di indovinare alcuni parametri.

Ultima cosa importante da dire prima di passare ad altro: a differenza da ipchains, in iptables ciò che non è esplicitamente consentito non passa (o vice versa, a secondo della policy). Per esempio, nel nostro caso un client interno alla rete non potrebbe ``pingare'' alcun server all'esterno né sarebbe in grado di utilizzare altri messaggi ICMP che non siano imparentati (related) con connessioni esistenti.

Con questo paragrafo viene chiuso l'argomento filtraggio. Nel prossimo paragrafo si parlerà infatti del NAT e di come fare quelle ``deviazioni'' di cui ci siamo temporaneamente dimenticati.

Lo scopo era quello di introdurre l'utente all'uso di iptables e di metterlo nelle condizioni di poter leggere facilmente le pagine del manuale, dove può trovare un elenco di tutti i possibili match che si possono effettuare (divisi per protocollo e modulo) e di tutti i target che si possono utilizzare. Per le target extension installate con il patch-o-matic, riferitevi alla documentazione fornita.


Avanti Indietro Indice