== Firewall con due o piu` ADSL in bilanciamento == Questa configurazione serve a creare un firewall in grado di usare contemporanemente due (o piu`) ADSL per servire, con il NAT, una rete di client. Il carico del traffico generato dai client viene distribuito sulle due ADSL in percentuali stabilite in configurazione. Nel caso in cui una delle ADSL muoia, pero`, occorre necessariamente intervenire a mano per "disattivarla", a meno che non venga a mancare il link sulla NIC del firewall, nel qual caso il sistema si rende conto da solo che quel percorso e` morto, e smette di usarlo fino a che non torna il link. Purtroppo per ottenere questo risultato occorre applicare una patch al kernel, perche` non facendolo succede che una parte del traffico nattato esce sull'interfaccia sbagliata (traffico su eth1 con l' ip di eth2 e viceversa). La patch inserisce un ulteriore meccanismo di controllo sullo stato delle connessioni nattate e su dove devono essere inoltrati i pacchetti. === Passo 1: ricompilare il kernel === * Installare il source del kernel e cio` che serve per compilarlo: {{{ aptitude install linux-source-2.6 kernel-package ncurses-dev fakeroot wget bzip2 }}} * Scaricare la patch relativa al proprio kernel dal sito http://www.ssi.bg/~ja/#routes. Per il kernel di Debian Squeeze, versione 2.6.32, useremo la patch http://www.ssi.bg/~ja/routes-2.6.32-16.diff. * Estrarre il tar.bz2 del kernel in /usr/src * Entrare nella directory del sorgente del kernel * Applicare la patch: {{{ patch -p 1 < ../routes-2.6.32-16.diff }}} * Copiare la configurazione del kernel che stiamo usando attualmente (che si suppone sia lo stesso dei sorgenti) da {{{/boot}}}: {{{ cp /boot/config-2.6.32-5-amd64 .config }}} * Dare un {{{make menuconfig}}} e se necessario modificare i parametri del kernel (o modificarli a mano nel file .config). A riguardo leggere le note delle patch, se indicano che occorre modificare qualcosa. Con questa versione del kernel non dovrebbe essere necessario. * Dare un {{{make-kpkg clean}}} * Ora compilare con il parametro {{{append-to-version= una stringa a mia scelta che inizia con "-"}}} {{{ make-kpkg --initrd --append-to-version=-multipath1 kernel-image kernel-headers }}} * Ora puoi andare a farti una pizza, ci vorra` un'ora circa... * Alla fine, installare il kernel-image che e` stato creato in {{{/usr/src}}} (eventualmente anche i kernel-headers se dovessero servire per compilare altri moduli sul kernel): {{{ dpkg -i "package del kernel.deb" }}} * Controlla quale versione fa partire grub ed eventuale correzione in /etc/default/grub * Riavvia e spera che il kernel si carichi bene === Passo 2: configurare le interfacce === Questo firewall richiede necessariamente una interfaccia fisica (o al limite una vlan, ma non un alias) per ogni ADSL, quindi per un esempio con due ADSL servono come minimo tre interfacce (LAN, WAN1, WAN2). Configurare le interfacce normalmente in {{{/etc/network/interfaces}}} avendo cura di NON impostare un gateway predefinito in nessuna delle WAN. In questo modo la macchina parte senza un gateway impostato, quindi di default non va su internet. === Passo 3: configurare il routing === Configurare il routing sembra facile ma non lo e`. Occorre tenere conto del fatto che il nostro firewall puo` essere raggiunto da host che stanno su internet per mezzo di due linee diverse, con indirizzi IP diversi. E che potrebbe decidere di rispondere a chi l'ha chiamato usando il gateway sbagliato, ovvero quello collegato con l'altro dei due pubblici che ha. Nella mia testa sarebbe stato logico pensare che questo non succeda, ovvero mi viene da pensare che se ricevo una connessione da fuori verso il mio indirizzo ip pubblico numero uno, perche` mai dovrei rispondere con l' ip pubblico numero uno, ma via il gateway numero due? Siccome pero` pare (da prove fatte) che succeda, devo evitarlo. Per evitarlo, devo fare in modo che quando un pacchetto ha come indirizzo di mittente il mio ip pubblico uno, esca dal gateway connesso alla interfaccia uno e non dall'altro. Ovviamente lo stesso vale per il due. Si, lo so, piu` ci penso meno sta in piedi, pero` empiricamente e` cosi` che si comporta. Quindi devo mettere assieme delle regole di routing che facciano queste cose: * assicurarsi che quando un pacchetto esce con uno dei miei due indirizzi pubblici, questo vada verso il gateway giusto e non verso l'altro * quando inoltro un pacchetto per conto di uno dei client, questo venga nattato con uno dei due indirizzi pubblici che ho, e vada via dal gateway giusto * quando rispondo a una connessione che viene da fuori, io lo faccia come sopra, usando l' ip e il gateway corretti. * quando parlo con le macchine della LAN, il traffico non finisca su internet per errore ma torni sulla LAN. Questo e` specialmente vero se le macchine della LAN mi mandano pacchetti indirizzati a uno dei miei ip pubblici e non a quello privato. Nel pensare a questa logica, ricordiamoci che il NAT in uscita (source NAT) e` fatto DOPO il routing, non prima. E che il NAT in entrata e` fatto invece PRIMA del routing. Dobbiamo quindi creare uno script che configuri delle regole di routing e delle tabelle di routing. Diciamo per questo esempio che abbiamo due provider e una LAN. La configurazione e` cosi` fatta: * LAN: indirizzo 10.0.0.1 netmask 24 (ovvero 255.255.255.0), interfaccia fisica eth0 * Provider A: indirizzo 1.0.0.2 netmask 29, il router e` 1.0.0.1, interfaccia fisica eth1 * Provider B: indirizzo 2.0.0.2 netmask 29, il router e` 2.0.0.1, interfaccia fisica eth2 Prima di tutto, devo editare il file {{{/etc/iproute2/rt_tables}}} per inserire due alias comprensibili per le due routing tables che devo creare dopo, altrimenti le posso indicare per numero, ma e` scomodo e non capisco immediatamente chi e` chi. Quindi lo modifico aggiungendo in fondo una cosa tipo: {{{ 1 ProviderA 2 ProviderB }}} Fatto questo, devo creare uno script che imposti le regole di routing per ottenere quello che voglio. I comandi sono preceduti da una spiegazione del perche` sono stati fatti cosi`, occorre ovviamente fare uno script con i comandi senza le spiegazioni. * Aggiungere due regole che dicono che quando esco con un indirizzo ip (pubblico) devo andare a vedere il routing sulla relativa tabella. (Nota: Quando esco con un indirizzo che non e` nessuno di quei due, usero` la tabella "main", che e` default) {{{ ip rule add from 1.0.0.2 table ProviderA ip rule add from 2.0.0.2 table ProviderB }}} * Ora devo inserire regole nella tabella ProviderA, che di partenza e` vuota. Ogni riga che finisce con "table ProviderA" inserisce la regola nella tabella "ProviderA". Le prime due regole insegnano al kernel qual'e` la subnet dietro alla interfaccia (10.0.0.0/29), quale interfaccia e` (eth1), e quale indirizzo ip devo usare quando mando pacchetti via per quella interfaccia (1.0.0.2), e poi dicono qual'e` il default gateway se esco da quella interfaccia (il router 1.0.0.1) {{{ ip route add 1.0.0.0/29 dev eth1 src 1.0.0.2 table ProviderA ip route add default via 1.0.0.1 table ProviderA }}} * Le successive regole sono una "copia" delle regole della tabella "main" che mi servono per consentire un routing corretto nel caso in cui una macchina della LAN (diciamo 10.0.0.44) mi mandi un pacchetto indirizzato al mio indirizzo pubblico (1.0.0.2) anziche` al privato (10.0.0.1). Se non mettessi queste regole qui, la risposta verso 10.0.0.44 andrebbe verso il default gateway, ovvero verso internet. Devo dire invece di mandarla via a mezzo di eth0 (LAN). Se avessi altre reti locali collegate ad altre interfacce (DMZ, per dire) dovrei aggiungerle qui come per la LAN. {{{ ip route add 10.0.0.0/24 dev eth0 table ProviderA }}} * Questa secondo me non serve a nulla, non dovrebbe mai succedere che un pacchetto con indirizzo di partenza di provider A vada alla subnet di provider B. Tuttavia non fa male metterla. Anche la successiva, che si riferisce al loopback, secondo me non serve a nulla (come per la precendente) {{{ ip route add 2.0.0.0/29 dev eth2 table ProviderA ip route add 127.0.0.0/8 dev lo table ProviderA }}} * Ora inserisco le regole nella tabella ProviderB, con la stessa identica logica usata per quella di ProviderA, ovviamente con gli indirizzi "scambiati", pero`. Le regole sono, in sequenza, le "compagne" di quelle descritte prima, valgono le stesse considerazioni che non sto a riscrivere. {{{ ip route add 2.0.0.0/29 dev eth2 src 2.0.0.2 table ProviderB ip route add default via 2.0.0.1 table ProviderB ip route add 10.0.0.0/24 dev eth0 table ProviderB ip route add 1.0.0.0/29 dev eth1 table ProviderB ip route add 127.0.0.0/8 dev lo table ProviderB }}} * Per finire, inserisco nella tabella "main" una regola che dice che il traffico uscente verso internet (ovvero via default gateway) deve essere mandato al 50% su ognuna delle due interfacce. Ovviamente questo 50% e` stocastico, non e` perfettamente bilanciato, e inoltre questa regola non verra` mai applicata al traffico che nasce nel firewall, il quale verra` sempre trattato dalle due tabelle ProviderA o ProviderB. {{{ ip route add default scope global nexthop via 1.0.0.1 dev eth1 weight 1 nexthop via 2.0.0.1 dev eth2 weight 1 }}} Lo script risultante, tutto assieme e senza commenti, e` questo: {{{ ip rule add from 1.0.0.2 table ProviderA ip rule add from 2.0.0.2 table ProviderB ip route add 1.0.0.0/29 dev eth1 src 1.0.0.2 table ProviderA ip route add default via 1.0.0.1 table ProviderA ip route add 10.0.0.0/24 dev eth0 table ProviderA ip route add 2.0.0.0/29 dev eth2 table ProviderA ip route add 127.0.0.0/8 dev lo table ProviderA ip route add 2.0.0.0/29 dev eth2 src 2.0.0.2 table ProviderB ip route add default via 2.0.0.1 table ProviderB ip route add 10.0.0.0/24 dev eth0 table ProviderB ip route add 1.0.0.0/29 dev eth1 table ProviderB ip route add 127.0.0.0/8 dev lo table ProviderB ip route add default scope global nexthop via 1.0.0.1 dev eth1 weight 1 nexthop via 2.0.0.1 dev eth2 weight 1 }}} === Passo 4: regole di firewll per il nat === Nel firewall, dobbiamo tenere conto che abbiamo due interfacce pubbliche, quindi ricordiamoci di applicare eventuali blocchi a tutte e due le interfacce. Per configurare correttamente il NAT in uscita dovremo definire due regole, che verranno applicate al traffico uscente a seconda di quale interfaccia avra` imboccato. Ricordiamo che il nat uscente (SNAT) avviene DOPO il routing. Il traffico che esce via eth1 (provider A) viene nattato con l' ip assegnato all'interfaccia stessa. Lo stesso vale per eth2 (provider B ) {{{ iptables -t nat -A POSTROUTING -o eth1 -j SNAT -s 10.0.0.0/24 --to-source 1.0.0.2 iptables -t nat -A POSTROUTING -o eth2 -j SNAT -s 10.0.0.0/24 --to-source 2.0.0.2 }}} Se vogliamo fare NAT in ingresso, verso una macchina della LAN o della DMZ, dovremo farlo su tutte e due le interfacce, anche in questo caso, sempre se vogliamo che la macchina interna sia raggiungibile a tutti e due gli ip pubblici. Un caso del genere e` valido per esempio per un mail server, che ha senso che sia raggiungibile a mezzo di tutte e due le linee, in modo da pubblicare due MX e avere ridondanza della connessione. Un esempio di NAT in ingresso e` questo: {{{ iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 25 -j DNAT --to 10.0.0.100 iptables -t nat -A PREROUTING -i eth2 -p tcp --dport 25 -j DNAT --to 10.0.0.101 }}} NOTA: Dal momento che fuori ho due interfacce, per evitare che si possa creare confusione (il traffico che entra da una interfaccia riceve risposte attraverso l'altra per colpa del NAT che natta sull'indirizzo sbagliato) allora assegno due NAT diversi (a due indirizzi privati diversi) a seconda se il traffico entra dall'una o dall'altra ADSL. Questi due indirizzi sono alias della stessa NIC sul mail server, in modo che di fatto per tutto il processo di NAT il mail server sia visto come due macchine distinte. Siccome il mail server risponde usando come source addresso lo stesso indirizzo al quale ha ricevuto il traffico, i pacchetti in uscita saranno mandati via per la stessa interfaccia da cui sono entrati. Non so se esista un modo per evitare di dare due indirizzi alla macchina interna. === Debug e verifica === Se si hanno comportamenti buffi, o comunque si vuole capire se non ci siamo persi pezzi per strada, possiamo innanzitutto salvarci tutte le regole di routing prima di fare tutto questo lavoro, quando cioe` usiamo ancora un singolo gateway. Per farlo, possiamo salvare l'output dei comandi: {{{ ip rule ip route show table local ip route show table main }}} Dopo aver modificato tutto quanto, possiamo ridare gli stessi comandi, aggiungendo pero` anche un {{{ip rule show table }}} per ognuna delle routing tables aggiuntive che abbiamo creato, ovvero quelle che sono menzionate nell'output del comando {{{ip rule}}}. Cosi` possiamo renderci conto per esempio di abbiamo fatto casino nelle tabelle predefinite, local e main, e se ci sono errori palesi nelle altre. Un altro utile comando e` {{{ip route flush cache}}} che pulisce la cache del routing e quindi rende realmente attive le modifiche che avete fatto, le quali altrimenti potrebbero essere "nascoste" dietro un comportamento che era in cache prima che le applicaste. Per provare a capire "cosa succederebbe se", ovvero "cosa succede se un pacchetto fatto cosi` e cosa` passa dalla macchina?" potete usare il comando {{{ip route get}}} con tutti i parametri del caso. Suggerisco di leggere il man di "ip" e cercare "get" per capire quali sono le possibilita` di interrogazione con il comando get. L'output del comando "get" riporta la regola di routing che viene applicata al pacchetto con le caratteristiche indicate nei parametri della get. Purtroppo non ci mostra esattamente quale sequenza di decisioni viene presa dal kernel per arrivare al risultato mostrato.