Col paragrafo precedente abbiamo completato la configurazione di una piccola rete per quanto riguarda il filtraggio.
Argomento di questa sezione è il NAT, ovvero ``Network Address Translation''.
Il NAT consente ad un firewall o router linux di non limitarsi a bloccare o consentire i pacchetti in transito, bensì anche di modficarli secondo delle regole ben definite.
Il codice che in linux gestisce il NAT è stato completamente ridisegnato per iptables ed il risultato è stato un sistema molto più pulito e flessibile rispetto quello utilizzato da ipchains. ipchains, per effettuare modifiche ai pacchetti, utilizzava semplicemente dei target diversi come MASQ (per mascherare i pacchetti). Questo però creava molta confusione sul meccanismo di scansione delle regole ed andava ad interferire con le regole di filtraggio. In iptables, invece, la parte di filtraggio dei pacchetti è completamente indipendente da quella di modifica e di gestione delle regole di nat. Per raggiungere questa indipendenza è stato infatti introdotto il concetto di ``tabella'' di cui fin'ora non avevamo parlato. Ebbene, le 3 catene principali di cui abbiamo parlato fin'ora (INPUT, OUTPUT, FORWARD) si trovano nella tabella ``filter''. La tabella di filter è quella che viene scelta in automatico se non viene specificata un'altra tabella (per questioni di compatibilità).
Esistono quindi altre tabelle in iptables, ognuna delle quali mette a disposizione diverse catene di base:
La distinzione tra mangle e nat non è così netta, si tratta soltanto di un criterio di base, ed il manuale vi saprà sicuramente indicare la tabella migliore da utilizzare a secondo delle vostre necessità.
Ma guardiamo meglio i nomi delle catene: nella tabella filter, ci troviamo le solite INPUT, OUTPUT, FORWARD, col significato che ormai conosciamo. Nella tabella di nat abbiamo invece 3 nuove catene: la catena di PREROUTING, la catena di OUTPUT e la catena di POSTROUTING. La catena di OUTPUT mantiene lo stesso significato che aveva nella tabella di filter. La catena di PREROUTING e quella di POSTROUTING classificano invece i pacchetti diversamente: non più in base alla direzione dei pacchetti, bensì al fatto che sia già stata presa una decisione di routing su questi pacchetti. Vediamo di spiegare meglio l'idea: quando un pacchetto di qualsiasi protocollo deve uscire da una macchina connessa ad una rete (poco importa la provenienza del pacchetto) deve essere presa una decisione di routing. Deve cioè essere stabilito da che interfaccia dovrà uscire questo pacchetto e con quali caratteristiche dovrà essere mandato (es, il TTL dovrebbe essere decrementato, l'MTU potrebbe essere diverso e necessitare una frammentazione, l'indirizzo hardware specificato potrebbe essere quello di un gateway...). Dal punto di vista logico, quindi, ogni pacchetto che deve uscire da un'interfaccia di rete passa prima dalla catena di PREROUTING, viene presa una decisione sull'interfaccia da cui fare uscire il pacchetto, dopodichè passa dalla catena di POSTROUTING.
PREROUTING e POSTROUTING sono state introdotte proprio per consentire di realizzare diversi comportamenti: ad esempio, se si vuole cambiare l'indirizzo di destinazione di un pacchetto sarà importante farlo prima che venga deciso da dove fare uscire il pacchetto (se così non fosse, il pacchetto uscirebbe dall'interfaccia sbagliata, a meno che questo non sia l'effetto desiderato). Mentre, al contrario, quando si cambia il mittente di un pacchetto potrebbe essere importante farlo dopo che una decisione di routing è stata presa (in questo caso però l'errore non sarebbe così palese).
Comunque sia, in ognuna di queste tabelle è possibile specificare dei target differenti utilizzando però le stesse opzioni e gli stessi moduli per creare delle regole. Di base, i target più conosciuti per la tabella di NAT sono proprio:
Realizzare questo a livello di regole di iptables è estremamente facile. Bisogna però fare una piccola introduzione e dare un minimo di spiegazioni. A molti di voi, ad esempio, sarà sorto spontaneo chiedersi come diavolo può fare un firewall a nascondere 20 computer (per esempio) dietro un unico indirizzo ip. Voglio dire, in uscita funziona bene: il client FOO dietro il firewall manda un pacchettino al gateway (il nostro firewall), il nostro firewall cambia l'indirizzo ip sorgente col suo (valido su internet) e manda avanti il pacchettino, il server remoto risponde, il pacchettino torna indietro, e... il pacchettino che gli torna indietro ha come mittente il server remoto, come destinatario il firewall... non sembra funzionare tanto... come fa il firewall a capire a quale dei nostri client deve rimandare il pacchetto, se l'indirizzo di destinazione è il suo indirizzo esterno? E anche ipotizzando di avere un indirizzo ip solo per i client, come farebbe a mandare la risposta al client corretto? Se guardasse l'indirizzo ip del server remoto, solo un client per volta potrebbe navigare su un determinato sito... non proprio funzionale. Una soluzione potrebbe essere quella di avere tanti ip esterni quanti client. Ma allora perché usare il nat? La sicurezza potrebbe essere un buon motivo, ma normalmente il nat si usa quando ci sono meno (o molti meno) indirizzi esterni a disposizione rispetto ai client della propria rete (attualmente, gli indirizzi ip hanno un costo, e non indifferente).
Beh, la soluzione che è stata trovata per risolvere questo problema si basa sull'idea di porta. Quando stabilite una connessione tcp verso un server, l'unica porta veramente importante è quella di destinazione, che identifica univocamente un servizio. Quella mittente, invece, non viene quasi mai guardata e spesso viene scelta a caso.
Con il NAT, il nostro firewall linux cambierà (se necessario) la porta sorgente dei pacchetti in uscita ed utilizzerà quella per identificare univocamente un client. Se la soluzione vi convince poco, provate a pensare al flusso dei pacchetti... il client A manda un pacchetto proveniente da A porta x al server B porta 80. Il firewall riceve il pacchetto, cambia x in y (dove x può essere uguale a y, se tale porta non è utilizzata da nessuno), si segna che y è associata solo ad A e cambia l'indirizzo ip sorgente con il suo, il pacchetto arriva al server remoto R porta 80 come proveniente da F:y, e risponde creando un pacchetto proveniente da R:80 e destinato al firewall F porta y. Firewall riceve il pacchetto, vede che la connessione non è stata creata direttamente da lui (ogni sistema operativo sa quali connessioni ha iniziato) cerca nella tabella e vede che la porta y era in origine x ed era associata al client A. Prende il pacchetto, modifica il destinatario in modo che indichi A porta x e rimanda il pacchetto ad A. Sembra funzionare, no? L'idea è abbastanza furba e permette di identificare univocamente un client utilizzando qualche bit in più di informazioni. Il problema è che a volte questa tecnica non funziona:
Per vedere dove sta il problema, proviamo a vedere un esempio. Ipotizziamo che il solito client FOO si colleghi a uno strano server con uno strano protocollo. Per via di questo strano protocollo, il server deve aprire una connessione verso la porta 789 del client e tenta quindi di collegarsi. Quello che però per lui è l'indirizzo del client è in realtà l'indirizzo del firewall, per via del NAT che ha cambiato l'indirizzo del mittente.
Così facendo, il server tenterà di collegarsi al firewall, che però questa volta non avrà nessuna associazione nella sua tabella (l'associazione si crea solo per le connessioni che escono, e non è possibile indovinarle per quelle che entrano, a meno che non vengano stabilite a priori dall'amministratore) e sarà quindi costretto a buttare via il pacchetto rispondendo al server con un messaggio icmp con scritto qualcosa del tipo ``la porta è chiusa e io non so chi tu sia''.
Infatti, i moduli del kernel prima caricati che si occupavano di aprire o chiudere dinamicamente le porte sono anche in grado di aggiungere o rimuovere associazioni dalla famosa tabella, o di fare delle piccole modifiche al protocollo in modo che questo possa funzionare. Per fare questo però, se non avete compilato staticamente il kernel, dovete caricare anche i moduli ``ip_nat_ftp'', ``ip_nat_irc'' o ``ip_nat_protocollo_strano_che volete usare'' con il solito ``modprobe''.
Il problema che a volte si incontra, però, è che questi moduli non sono proprio facili da scrivere, per cui è relativamente facile trovarne per i protocolli standard, mentre non esistono per quei protocolli inventati dalla fantasia dei venditori di software di turno, che non sentono il dovere morale di rispettare degli standard.
Normalmente i problemi si incontrano solo per parti dei vari ``protocolli'' (ad esempio, dietro un firewall linux di questo tipo, col messanger sarebbe difficile farsi mandare dei file da altri utenti, pur funzionando per tutte le altre cose). E se per ftp e IRC (anche netmeeting, ma sperimentale) sono stati scritti dei moduli, per altri protocolli meno utilizzati credo che sarà difficile che qualcuno si occupi di realizzarli.
In questi casi, un ``socks server'' vi consentirà il più delle volte di rendere felici i vostri utenti. Il prezzo da pagare è che un socks server di base è molto aperto ed una configurazione errata o superficiale di questo potrebbe vanificare completamente i vostri sforzi. In più, preparatevi a ricevere chiamate a tutte le ore del tipo ``come si fa ad impostare il socks server su fuffolo 3.0 piuttosto che kazzat 2.8''...