Articoli Manifesto Tools Links Canali Libri Contatti ?
Sicurezza / Linux

Firewall e meccanismi di protezione di rete in ambiente Gnu/Linux

Abstract
Analisi dei principali meccanismi di protezione di una rete in ambiente GNU/Linux con particolare enfasi sulla costruzione di un firewall ad-hoc.
Data di stesura: 08/04/2003
Data di pubblicazione: 07/05/2003
Ultima modifica: 04/04/2006
di Valentino Squilloni Discuti sul forum   Stampa

Prerequisiti

Una macchina collegata ad un network (anche internet), oppure che abbia più di un'interfaccia di rete.

Una minima conoscenza della rete e del tcp/ip è molto utile per capire quello che si sta facendo.

Essere in grado di ricompilare il kernel; anche se vengono descritte tutte le opzioni da inserire per il corretto funzionamento del firewall, si assume che si sia in grado di abilitare tutte le opzioni necessarie a far funzionare il proprio hardware e tutto il resto.

Cosa è un firewall?

Il firewall è una struttura che si occupa di analizzare il traffico entrante e uscente da una macchina e decidere in base alle regole definite come trattare i vari pacchetti.
Questo tipo di funzionalità è detto packet filtering.

A chi serve un firewall?

A chiunque abbia una connessione ad internet, in particolare se è una connessione permanente. Quando siamo connessi ad una rete siamo potenziali vittime di attacchi, e in molti casi, se la nostra macchina venisse compromessa e facesse dei danni ad altre macchina, potremmo essere considerati responsabili dell'accaduto.
È quindi importante una sensibilizzazione sui pericoli delle reti.

Un firewall serve anche e soprattutto nei punti periferici della rete, accoppiato ad un router per definire le regole di passaggio da una LAN a internet (o ad un'altra intranet).

Configurazione base dei servizi per una macchina in rete

Prima di mettere una macchina Linux su di una qualsiasi rete bisogna decidere esattamente quali servizi offrire e disabilitare di conseguenza tutti quelli superflui, in modo da diminuire i possibili buchi del sistema.

Ora occupiamoci dei servizi che invece vogliamo lasciare attivi, ma solo per un uso locale, quindi non dovremmo avere porte tcp in ascolto sull'interfaccia esterna (quella a contatto con internet), ne' porte udp.
Controlliamo quindi la situazione con "netstat -tunap" che oltretutto ci dice anche quale processo tiene aperta quella data porta e su che interfaccia; se il Local Address è 0.0.0.0 significa che quella porta è in ascolto su tutte le interfacce.
Anche se un firewall ben configurato non permetterebbe comunque, a livello di packet filtering, di raggiungere quelle porte, è sempre meglio creare una situazione di ridondanza. Come? Facciamo un piccolo esempio:

root@damun:~# netstat -tunap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address    Foreign Address  State   PID/Program name
tcp        0      0 0.0.0.0:22       0.0.0.0:*        LISTEN  1303/sshd
[...]
In questo caso sshd è in ascolto sulla porta 22 di tutte le interfacce; come fare a imporre a ssh di stare in ascolto solo su una data interfaccia, ad esempio quella di loopback? Semplicissimo, nel file di configurazione di ssh c'è un'opzione ListenAddress che di default è 0.0.0.0, andiamo a cambiare questa opzione in ListenAddress 127.0.0.1, riavviamo ssh, e quindi
root@damun:~# netstat -tunap | grep 22
tcp        0      0 127.0.0.1:22       0.0.0.0:*        LISTEN 1310/sshd
Un'altra cosa che ci può aiutare a creare robustezza e a migliorare la sicurezza del nostro sistema è l'utilizzo (se possibile) del tcpwrapper tcpd e dei file per il controllo degli accessi /etc/hosts.allow e /etc/hosts.deny.

Consiglio come al solito di negare ogni accesso ad ogni servizio (basta inserire la stringa "ALL : ALL" in hosts.deny) e di abilitare esplicitamente in hosts.allow i servizi che devono essere permessi.

Questo consiglio (negare ogni cosa e abilitare solo quello che serve) è un MUST quando si parla di sicurezza, infatti quando andremo a costruire il firewall seguiremo la stessa strategia.

Purtoppo non tutti i servizi possono essere controllati da tcpd (quelli che non partono da inetd/xinetd e non sono linkati alle libwrap) e non tutti possono essere associati ad un'interfaccia specifica ed è qui che interviene il firewall.

Implementazione dello strato di packet filtering in Linux

Andremo ora ad introdurre le funzionalità di firewalling disponibili con il kernel di Linux.

Nei kernel 2.0 la sezione che si occupa di packet filtering è chiamata ipfwadm, che è stata sostituita nei kernel 2.2 da ipchains e ora nei kernel 2.4 da iptables.

La maggior parte di questi software è inclusa direttamente nel kernel di linux, quindi essi sono molto più veloci di qualsiasi software che gira in user-mode.

Il passaggio da ipchains a iptables è stato segnato da grandissime novità tra le quali spicca la stateful inspection grazie al modulo di connection tracking (approfondiremo questo concetto in seguito).

In questa sede andremo ad analizzare prevalentemente iptables, viste le importanti novità introdotte, anche se gran parte dei concetti saranno validi anche per ipchains.

Predisposizione del sistema per l'utilizzo del firewall (iptables)

Per avere l'ultima versione di netfilter è probabilmente necessario "patchare" il kernel, quindi consiglio caldamente di recuperare un kernel cosiddetto "vanilla" (cioè proveniente da kernel.org o da uno dei suoi mirrors ma non i sorgenti che vengono allegati alla vostra distribuzione preferita che probabilmente saranno già abbondantemente modificati) e di seguire questa semplice procedura.

Scompattiamo i sorgenti dell'ultima versione di iptables e spostiamoci nella directory appena creata:

tar xjf iptables-1.2.7a.tar.bz2
cd iptables-1.2.7a
Ora procediamo alla vera e propria patch dei sorgenti del kernel:
make pending-patches KERNEL_DIR=/path/to/your.kernel.source
dove /path/to/your.kernel.source è la directory che contiene i sorgenti del kernel, solitamente /usr/src/linux.

Qui partirà il programma "Patch-o-matic" che analizzerà i sorgenti del kernel, trovando le possibili patch da applicare e vi chiederà, per ogni patch, se volete applicarla o no (in generale è consigliato applicare ogni patch a meno di evidenti controindicazioni). Se tutto va a buon fine, alla fine dovreste leggere qualcosa come:

Excellent!  Kernel is now ready for compilation.
Una volta completata questa semplice operazione è necessario ricompilare lo stesso kernel abilitando le seguenti opzioni:
[ Code maturity level options ]
* Prompt for development and/or incomplete code/drivers (CONFIG_EXPERIMENTAL)
  YES

[ Loadable module support ]
* Enable loadable module support (CONFIG_MODULES)
  YES
* Kernel module loader (CONFIG_KMOD)
  YES

[ Networking options ]
* Packet socket (CONFIG_PACKET)
  YES (anche se ai fini del firewall non e` indispensabile,
       permette di poter usare tcpdump per debuggare)
* Packet socket: mmapped IO (CONFIG_PACKET_MMAP)
  YES (velocizza il Packet protocol)
* Network packet filtering (replaces ipchains) (CONFIG_NETFILTER)
  YES (questa e` l'opzione che permettera` di configurare la
       sottosezione tcp-ip del kernel con netfilter, abilitando
       il sottomenu IP: Netfilter Configuration )
* Unix domain sockets (CONFIG_UNIX)
  YES
* TCP/IP networking (CONFIG_INET) 
  YES

[ Networking options --> IP: Netfilter Configuration ] 

In questa sezione vi consiglio di abilitare modularmente tutte le
opzioni trovate (sono molte, probabilmente, ma se il kmod e`
adeguatamente configurato non dovrete occuparvi di caricare
manualmente i moduli di cui avete bisogno, ci pensera` il kernel
quando ne avra` bisogno).

[ Network device support ]
* Network device support (CONFIG_NETDEVICES)
  YES

[ File systems ]
* /proc filesystem support (CONFIG_PROC_FS)
  YES, e` richiesto per configurare dinamicamente i meccanismi di 
  forwarding e di nat
Le opzioni elencate qui sopra sono solo alcune di quelle che riguardano netfilter, ovviamente sarà necessario selezionare tutte le altre opzioni che ci servono per il corretto funzionamento della macchina.

A questo punto dobbiamo essere sicuri che esista il programma iptables, visto che abbiamo i sorgenti dell'ultima versione, ci conviene ricompilarlo anche se potrebbe già essere presente nel sistema.
Sempre dalla directory contenente i sorgenti di iptables:

make KERNEL_DIR=/path/to/your.kernel.source
make install KERNEL_DIR=/path/to/your.kernel.source
Bene, a questo punto dobbiamo costruire solo le regole che andranno a costituire il nostro firewall.

Iptables, concetti generali

Un filtro di pacchetti (come già detto) controlla gli header dei pacchetti e in base alle regole definite ne stabilisce la sorte.
Può decidere di:
  • scartare il pacchetto
  • accettare il pacchetto, che quindi sarà passato ai livelli superiori (tcpwrappers o direttamente l'applicazione destinata a gestire quel pacchetto)
  • compiere altre operazioni più complesse, alcune delle quali saranno analizzate in seguito
Il comando iptables viene usato per compiere ogni tipo di operazioni sulle tabelle delle regole del kernel; ci sono tre tabelle indipendenti:
  • filter, la tabella di default che si occupa di filtrare o far passare i pacchetti
  • nat, che si occupa di tradurre gli indirizzi
  • mangle, per alterazioni speciali dei pacchetti
Ogni tabella contiene un certo numero di "chain", alcune sono built-in nella tabella e non possono essere cancellate, altre possono essere definite dall'utente. Ogni chain è costituita da un insieme numerato di regole che possono identificare un certo sottoinsieme di pacchetti.

Quando arriva un pacchetto, dunque, vengono analizzate in ordine le regole della chain che gli compete (vedere più sotto la spiegazione di $CHAIN) e appena viene trovata una regola che corrisponde a quel pacchetto, quella regola deciderà la sorte [TARGET] del pacchetto stesso.

La forma di default di una regola di iptables è fatta in questo modo:

iptables -t $TABLE $ACTION $CHAIN -m $modulo -p $proto -i $interface_in \
  -o $interface_out -s $source -d $destination -j $TARGET
Dove:
  • $TABLE è la tabella alla quale ci si sta riferendo; In questo articolo tratteremo solo la tabella filter (questa è la tabella presa in considerazione se l'opzione -t non è specificata).
  • $ACTION è l'azione da compiere sulla chain, ad esempio -A (aggiungi una nuova regola alla chain), -P (imposta una policy di default), -F (cancella tutte le regole di una chain), -N (crea una nuova chain), -X (cancella una chain definita dall'utente).
  • $CHAIN è il nome di una chain; per ogni tabella esistono delle chain built-in e delle chain che possono essere create dall'utente a partire dalle chain di base; le catene già costruite della tabella filter sono: INPUT (per i pacchetti destinati alla macchina stessa), OUTPUT (per i pacchetti generati localmente) e FORWARD (per i pacchetti che devono essere instradati attraverso due interfacce della macchina).
  • $modulo è un modulo speciale da caricare per analizzare la regola in oggetto; utilizzeremo soprattutto il modulo "state" che ci permette di tracciare le connessioni in base al loro stato grazie al modulo del kernel ip_conntrack.
  • $proto è il protocollo (tcp, udp, icmp, ...).
  • $interface_in è l'interfaccia dalla quale sta entrando il pacchetto (chain INPUT e FORWARD); $interface_out è l'interfaccia dalla quale sta uscendo il pacchetto (chain OUTPUT e FORWARD).
  • $source e $destination sono indirizzi o classi di indirizzi per identificare rispettivamente l'indirizzo sorgente e quello destinazione del pacchetto.
  • $TARGET può essere un target conclusivo, cioè un target che pone fine all'attraversamento della chain e ne decide la sorte, o una chain definita dall'utente; i target conclusivi della tabella filter sono essenzialmente tre: ACCEPT (fa passare il pacchetto), DROP (butta il pacchetto) e REJECT (butta il pacchetto mandando un pacchetto icmp tipo network-unreachable all'indirizzo sorgente di quel pacchetto).
Ci sono fondamentalmente due modi per andare a scrivere le regole:
  1. possiamo inserire tutti i comandi all'interno di uno script (come farò poi io in seguito)
  2. possiamo scrivere le nostre regole direttamente dalla linea di comando e quindi saranno subito attive.
In quest'ultimo caso per salvare la configurazione delle nostre regole in un dato momento basta lanciare il comando
iptables-save > MiaConfig
Dove MiaConfig sarà il nome del file con la vostra configurazione, mentre per richiamarla in un altro momento basta lanciare il comando:
iptables-restore < MiaConfig
Per vedere tutte le regole impostate all'interno delle varie chain si usa il comando "iptables -L" (List).

Una spiegazione particolare è d'obbligo per i concetti di stateful inspection e connection tracking perché è grazie a questi che la configurazione di iptables risulta decisamente più semplice ed efficace rispetto a ipchains.

Il tracciamento delle connessioni (di cui è responsabile il modulo del kernel ip_conntrack) mantiene in memoria delle tabelle dei pacchetti entranti e uscenti dalla macchina in modo da poter avere un controllo non solo in base alle caratteristiche del pacchetto analizzato, ma anche e soprattutto in base alla sua relazione con i pacchetti precedenti, cioè allo "stato" della connessione.

L'opzione di iptables che ci permette di utilizzare questa importantissima feature è l'estensione "state"; caricando questo modulo possiamo specificare la sotto-opzione --state $STATO dove $STATO è una lista di stati separati da virgole.

Sono definiti 4 tipi diversi di stato:

  1. NEW per un pacchetto che crea una nuova connessione (cioè un pacchetto tcp col flag SYN impostato oppure pacchetti udp o icmp non dovuti a connessioni già validate)
  2. ESTABLISHED per un pacchetto che fa parte di una connessione già stabilita, cioè che ha già avuto dei pacchetti in risposta.
  3. RELATED per un pacchetto relativo a connessioni esistenti ma che non fa parte di una connessione esistente (come ad esempio un pacchetto icmp di errore o una connessione ftp-data [porta 20] relativa ad una connessione ftp)
  4. INVALID per pacchetti che per alcune ragioni non possono essere classificati in altro modo.

Costruzione di un firewall di base

Siamo nella situazione di avere una macchina connessa saltuariamente oppure permanentemente a internet e caricheremo il firewall all' attivarsi dell'interfaccia ppp o all'avvio della macchina rispettivamente.
L'esempio proposto è molto semplice, rimando ad un eventuale articolo futuro un esempio più realistico.
  1. #!/bin/sh 
  2.  
  3. ### definizione dell'interfaccia pubblica, cambiare se necessario 
  4. EXT-IF="ppp0" 
  5.  
  6. ### verifica della correttezza dei moduli all'interno del kernel 
  7. depmod -ae 
  8.  
  9. ### caricamento dei moduli necessari 
  10. /sbin/insmod ip_conntrack 
  11. /sbin/insmod ip_conntrack_ftp 
  12. /sbin/insmod ip_conntrack_irc 
  13.  
  14. ### Utile per interfacce dial-up con indirizzo IP variabile. 
  15. echo "1" > /proc/sys/net/ipv4/ip_dynaddr 
  16.  
  17. ### azzeramento delle regole delle chain e definizione delle policy di base 
  18. iptables -F 
  19. iptables -P INPUT DROP 
  20. iptables -P OUTPUT ACCEPT 
  21.  
  22. ### creazione di una chain user-defined 
  23. iptables -N chain-spoof 
  24.  
  25. ### [*] accetto su tutte le interfacce le connessioni stateful 
  26. iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 
  27.  
  28. ### accetto tutti i pacchetti inviati sull'interfaccia di loopback 
  29. iptables -A INPUT -i lo -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT 
  30.  
  31. ### accetto gli icmp di tipo 3 (destination unreachable) che mi servono 
  32. ### per evitere lunghi timeout in caso di risposte d'errore 
  33. iptables -A INPUT -p icmp --icmp-type 3 -j ACCEPT 
  34.  
  35. ### redirigo alla chain-spoof tutti i pacchetti che arrivano dall' 
  36. ### interfaccia esterna con indirizzi sorgente privati 
  37. iptables -A INPUT -i $EXT-IF -s 224.0.0.0/3 -j chain-spoof 
  38. iptables -A INPUT -i $EXT-IF -s 192.168.0.0/16 -j chain-spoof 
  39. iptables -A INPUT -i $EXT-IF -s 172.16.0.0/12 -j chain-spoof 
  40. iptables -A INPUT -i $EXT-IF -s 10.0.0.0/8 -j chain-spoof 
  41. iptables -A INPUT -i $EXT-IF -s 127.0.0.0/8 -j chain-spoof 
  42. iptables -A INPUT -i $EXT-IF -s 169.254.0.0/16 -j chain-spoof 
  43.  
  44. ### log dei pacchetti spoofati (la spiegazione del modulo limit e del 
  45. ### target LOG saranno fatte prossimamente) 
  46. iptables -A chain-spoof -m limit --limit 1/s -j LOG --log-prefix \\ 
  47.   "Pacchetti forse spoofati" --log-level warn 
  48.  
  49. ### log di tutti i tentativi di connessione tcp (anche qui rimandiamo 
  50. ### ls spiegazione dell'opzione tcp --syn) 
  51. iptables -A input -p tcp --syn -i $EXT-IF -j LOG --log-prefix \\ 
  52.   "Tentativo di connessione su $EXT-IF" --log-level warn 
In questo primo esempio non si è toccata per ora la chain OUTPUT in quanto si presume che se questa macchina è un home-computer usato solo da persone "fidate" non debbano essere fatte limitazioni sul traffico in uscita, in futuro vedremo anche come limitare il traffico uscente.

Invece la policy di default di INPUT è stata impostata a DROP, cosicché se un pacchetto non dovesse ricadere in nessuna regola nella nostra chain verrà automaticamente eliminato.

La regola fondamentale che ci permette in modo così semplice di definire i pacchetti che possono entrare è la [*], come si nota è l'unica regola con target ACCEPT della chain di INPUT (a parte i pacchetti sull'interfaccia di loopback).

Questa regola accetterà tutti e soli i pacchetti di connessioni già stabilite e relative, quindi, essendo la policy di default a DROP, con questa regola scartiamo automaticamente tutti i tentativi di connessione tcp e tutti i pacchetti udp e icmp non appartenenti a connessioni già validate e comunque iniziate da un pacchetto in uscita inviato dalla nostra macchina.

Conclusione

Per ora siamo riusciti a proteggere adeguatamente la nostra macchina dall'esterno, pur non avendo in nessun modo limitato le nostre possibilità sulla rete.

Nei prossimi articoli vedremo di aggiungere al nostro schema una LAN da proteggere (e da cui proteggersi) quindi ci occuperemo di NAT e dei principi di routing che nella situazione descritta qui non erano necessari.

Si cercherà anche di analizzare target e opzioni particolari che non abbiamo potuto trattare in questo articolo ed altri argomenti inerenti:

  • Aggiunta di una rete locale da proteggere
  • Condivisione della connessione e routing
  • NAT: DNAT e redirection e SNAT e MASQUERADING
  • Controllo del traffico in uscita (magari con proxy trasparente http in aiuto)
  • Porte da bloccare per file sharing, chat...
  • Estensioni tcp, udp e icmp; target speciali (LOG,...); moduli particolari.

Informazioni sull'autore

Valentino Squilloni, laureando in Informatica all' Università degli Studi di Milano, è interessato all'architettura delle reti e ai relativi problemi di sicurezza soprattutto in ambiente GNU/Linux. Appassionato anche di algoritmi, intelligenza artificiale e reti neurali.

È possibile consultare l'elenco degli articoli scritti da Valentino Squilloni.

Altri articoli sul tema Sicurezza / Linux.

Risorse

  1. Homepage del progetto netfilter/iptables, suggerita la lettura degli HOWTO nella sezione documentation, alcuni dei quali opportunamente tradotti. Per chi è completamente a digiuno sulle reti e sul tcp/ip è caldamente consigliato il networking-concepts-HOWTO. È qui dove troverete sempre l'ultima versione di iptables.
    http://www.netfilter.org
  2. Collezione di script, FAQ, tutorial e HOWTO su netfilter/iptables.
    http://www.linuxguruz.org/iptables/
  3. HOWTO sulla ricompilazione del kernel linux. Da vedere anche e soprattutto il file README della Documentazione inclusa nei sorgenti del kernel.
    http://ildp.pluto.linux.it/HOWTO/Kernel-HOWTO.html
  4. Archivio dei sorgenti del kernel Linux, qui troverete i sorgeti delle ultime release.
    http://www.kernel.org/pub/linux/
  5. approfondimenti su tcpwrapper, consigliate le letture delle man-pages di: tcpd(8), hosts_access(5), inetd.conf(5), inetd(8), hosts_options(5), tcpdchk(8), tcpdmatch(8)
Discuti sul forum   Stampa

Cosa ne pensi di questo articolo?

Discussioni

Questo articolo o l'argomento ti ha interessato? Parliamone.