Go Back

Impostazioni del Firewall su ubuntu server
Il serverino dovrà operare come gateway quindi è necessario che sistema operativo sia in grado di effettuare il tipo di NAT (una manipolazione degli indirizzi di rete nei pacchetti in transito) che si chiama "Masquerading" ossia il mascheramento degli indirizzi della rete locale dietro all'IP della macchina router.
Per fare, questo la solita guida dall'inesauribile sito suggerisce diverse possibilità, io preferisco in questa sede utilizzare la modalità più manuale, principalmente perchè non sono molto pratico di networking e ho necessità di capire bene come funziona la gestione delle reti sul mio sistema, per poterlo anche manutenere quando ce ne sarà necessità.

Apro il mio terminale e faccio login via ssh sulla mio serverino.
codice:
ssh matteo@192.168.100.1
ora che sono loggato posso anche diventare root. Va bene che non mi piace, ma quasi tutte le operazioni saranno da fare come root, perciò è inutile usare sudo davanti ad ogni comando... Sulla macchina root non è abilitato a loggarsi via ssh ma una volta che l'utente ha fatto login, nessuno mi vieta di cambiare utente con il comando
codice:
su
seguito dalla password di root.

Ovviamente stò cercando di gestire la macchina via rete e stò cercando di capire come funziona il firewall: E' necessario fare attenzione perchè un comando sbagliato (come una regola troppo restrittiva) potrebbe interrompere la connessione e tagliarmi fuori, obbligandomi a fare login sulla macchina o a riavviarla per annullare la modifica.
NETFILTER, il componente del kernel di Linux che si occupa di filtrare i pacchetti, è configurato "Live" quindi ogni comando impartito con "iptables" è immediatamente applicato, salvo alcune condizioni particolari. Tali configurazioni vengono però perse al riavvio del sistema ed è pertanto necessario, una volta ottenuto un set di regole adeguato, utilizzare uno script che ad ogni reboot ripassi a netfilter tutte le istruzioni su come gestire il traffico di rete. Intanto però dobbiamo ottenerlo, questo set di istruzioni...

Il comando
codice:
iptables -L
mostra le regole attualmente definite nel firewall. La policy di default è "ACCEPT" quindi ora il nostro firewall stà accettando tutto il traffico in transito. Il comando
codice:
iptables -F
invece effettua il flush di tutte le regole definite, riportando le policy tutte su "ACCEPT" ed è utile per ripulire la configurazione e rincominciare.


eth0 è la mia interfaccia pubblica quindi in base alla guida di prima utilizzo il comando
codice:
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
per abilitare il mascheramento degli IP attraverso questa interfaccia. Dopo aver premuto "Invio" il sistema non restituisce nessun messaggio di errore e questo è generalmente
buono.
Ora il firewall è configurato per il Masquerading dei pacchetti che transitano per eth0 ma la la mia rete locale è collegata su eth1.
Il sistema deve poter operare come router e quindi essere abilitato a girare i pacchetti in ingresso dalla LAN eth1 che non sono direttamente destinati a lui verso l'interfaccia WAN eth0 e viceversa. Per fare questo è necessario operare su un parametro del kernel definito "ip-forwarding". La modifica si può fare al volo ma andrebbe persa al reboot. Questa preferisco renderla subito permanente quindi faccio una copia di backup del file /etc/sysctl.conf e con il solito editor di testo apporto le necessarie modifiche
codice:
cp /etc/sysctl.conf /etc/sysctl.conf.backup
nano /etc/sysctl.conf


Come detto in precedenza, per abilitare una riga è sufficiente cancellare il simbolo "#" che la inizia. Ho decommentato/aggiunto le righe in rosso:

codice:
# # /etc/sysctl.conf - Configuration file for setting system variables
# See /etc/sysctl.d/ for additonal system variables
# See sysctl.conf (5) for information.
#

#kernel.domainname = example.com

# Uncomment the following to stop low-level messages on console
#kernel.printk = 3 4 1 3

##############################################################
# Functions previously found in netbase
#

# Uncomment the next two lines to enable Spoof protection (reverse-path filter)
# Turn on Source Address Verification in all interfaces to
# prevent some spoofing attacks
net.ipv4.conf.default.rp_filter=1 net.ipv4.conf.all.rp_filter=1

# Uncomment the next line to enable TCP/IP SYN cookies
# See http://lwn.net/Articles/277146/
# Note: This may impact IPv6 TCP sessions too
net.ipv4.tcp_syncookies=1

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

# Uncomment the next line to enable packet forwarding for IPv6
# Enabling this option disables Stateless Address Autoconfiguration
# based on Router Advertisements for this host
#net.ipv6.conf.all.forwarding=1

###################################################################
# Additional settings - these settings can improve the network
# security of the host and prevent against some network attacks
# including spoofing attacks and man in the middle attacks through
# redirection. Some network environments, however, require that these
# settings are disabled so review and enable them as needed.
#
# Do not accept ICMP redirects (prevent MITM attacks)
#net.ipv4.conf.all.accept_redirects = 0
#net.ipv6.conf.all.accept_redirects = 0
# _or_
# Accept ICMP redirects only for gateways listed in our default
# gateway list (enabled by default)
net.ipv4.conf.all.secure_redirects = 1
#
# Do not send ICMP redirects (we are not a router)
#net.ipv4.conf.all.send_redirects = 0
# E invece sei un router, quindi mandale!!
net.ipv4.conf.all.send_redirects = 1

#
# Do not accept IP source route packets (we are not a router)
#net.ipv4.conf.all.accept_source_route = 0
#net.ipv6.conf.all.accept_source_route = 0
# E invece sei un router, quindi accettale!!
net.ipv4.conf.all.accept_source_route
= 1

#
# Log Martian Packets
#net.ipv4.conf.all.log_martians = 1
#
## Ignora finti messaggi di errore ICMP
net.ipv4.icmp_ignore_bogus_error_responses= 1
## Non risponde ai ping inviati al broadcast della subnet
net.ipv4.icmp_echo_ignore_broadcasts = 1

Per ora IPv6 non mi interessa quindi ignoro le righe relative. Non sò perchè ma la guida di debianizzati usa degli "/" al posto dei "." nei comandi di configurazione. La sintassi corretta però dovrebbero essere i "."
Dalla guida ho preso solo gli ultimi due comandi e la protezione anti-spoofing: non sono del tutto convinto che la riga
codice:
## Non accetta pacchetti ICMP di route redirection
net/ipv4/conf/all/accept_redirects = 0
sia corretta, dovendo operare come router. A questo punto o riavvio il sistema oppure ricarico la configurazione per applicare le modifiche con il comando:
codice:
sysctl -p /etc/sysctl.conf


Per verificare che tutto funzioni il metodo più semplice è provare: Configuro un client per usare il serverino come gateway e provo un trace route. Questo comando mostra tutto il percorso che un pacchetto di rete segue dal pc di partenza ad una specifica destinazione.
codice:
tracert www.nexthardware.com
da un pc Windows oppure da una macchina Linux
codice:
traceroute www.nexthardware.com
Funziona!


Quello che mi interessa sono sostanzialmente le prime 3 righe: Il primo hop va verso l'IP che ho configurato come gateway (il serverino), il secondo all'IP del mio router Wi-Max su una rete diversa (192.168.50.X invece che la rete 192.168.100.x di partenza) ed il terzo al gateway del mio ISP.
Se avessi riavviato il serverino per applicare le modifiche che ho fatto al file /etc/sysctl.conf avrei dovuto, prima di provare a navigare, riapplicare il comando iptables che abilitata il Masquerading in quanto, non avendo ancora definito uno script che ricarichi la configurazione di netfilter, il nostro serverino dopo il riavvio non avrebbe applicato il mascheramento degli ip e non ci avrebbe permesso di navigare.
Il router è pronto!
parte forse più importante e delicata per mettere davvero in funzione il mio serverino e cioè il setup del firewall.

Per quanto se ne dica Linux, al pari di tutti gli altri sistemi operativi, è tutt'altro che invulnerabile alle infiltrazioni indesiderate. E' perfettamente possibile bucare un sistema Linux così come è possibile bucare qualunque altro sistema se questo è mal configurato: Anzi, la disponibilità a tutti del codice sorgente permette a chiunque ne abbia le capacità di analizzare il codice di qualunque software disponibile per il nostro sistema e scoprire eventuali falle o vulnerabilità che possono essere sfruttate per compromettere un sistema. Inoltre, identificare una compromissione (a meno che questa non sia plateale) e ripristinare completamente un sistema Linux è estremamente difficile, forse molto più che non intervenire su un sistema Windows. Proprio perchè il sistema è tutto composto da file di configurazione, script modificabili con un semplice editor di testo e binari di cui è disponibile il sorgente e che possono quindi essere ricompilati a piacere rende quasi impossibile sanificare con sicurezza con sistema a cui un malintenzionato abbia avuto un accesso amministrativo e spesso la soluzione più veloce e sicura è proprio la formattazione e reinstallazione del sistema, con la sostituzione di tutti gli script e i file anche degli stessi dati che compongono, ad esempio, un sito web.
Stiamo appunto parlando di un sistema che verrà esposto sulla rete pubblica e quando verificheremo i log del nostro sistema non sarà improbabile rilevare ogni giorno le tracce di almeno uno o due tentativi di intrusione...

Per contrastare questi attacchi il metodo migliore è muoversi in anticipo, riducendo al minimo la superficie d'attacco e cioè lo spettro di possibilità che un eventuale attaccante può sfruttare. Questo significa adottare una serie di accorgimenti che vanno dal non lasciare attivi ed esposti servizi inutili, proteggere al meglio possibile gli accessi al sistema, adottare sistemi di autenticazione efficaci (password lunghe, nomi utente non banali o conosciuti da tutti come "root" appunto) o complessi fino all'adozione di sistemi di ban automatizzato che analizzano i log del sistema e inseriscono tramite iptables dei ban per gli indirizzi IP da cui provengono i potenziali attacchi ma alla base di tutto c'è quindi una corretta implementazione e gestione del firewall.

netfilter è il componente del kernel Linux che si occupa di fare questo ed iptables lo strumento che mi permette di configurarne il comportamento. Purtroppo netfilter ed iptables sono strumenti molto potenti ma piuttosto complessi quindi se qualcuno avesse delle critiche da muovere alla mia configurazione è pregato di farlo e suggerire le eventuali modifiche.

Come detto in precedenza, è utile creare uno script da caricare all'avvio che inserisca in netfilter tramite iptables le regole che mi servono. Ci sono in giro un'infinità di guide che suggeriscono possibili configurazioni ma una configurazione standard difficilmente potrà adattarsi a tutti i casi. E' pertanto necessario capire come funziona questo strumento e adattare quello che si trova alle proprie esigenze. Io ho fatto riferimento principalmente a questa guida sempre di debianizzati.org e questo tool che è uno splendido wizard per la generazione di uno script iptables.

La struttura di netfilter prevede di default tre tabelle ("tables" FILTER, NAT e MANGLE) che a loro volta contengono alcune catene "chain" predefinite. Per la verità è stata introdotta anche un quarta table (RAW) la cui applicazione è però un pò particolare.
Ogni pacchetto in transito su un sistema attraversa obbligatoriamente una o più tables e nell'ambito di queste una o più chain. Una "chain" è una sequenza di regole che il pacchetto deve attraversare: Quando il pacchetto trova una regola che gli corrisponde subisce l'azione associata alla regola. La tabella "FILTER" è quella che principalmente di occupa dell'analisi del traffico di rete e del firewall, le altre servono principalmente per la configurazione di router più avanzati. Quello che mi interessa ora si trova nelle tabelle "FILTER" e "NAT".

Intanto devo partire da una situazione nota quindi azzero tutte le regole eventualmente presenti nel firewall:
codice:
#Cancellazione delle regole presenti nelle
#chain predefinite delle tabelle di netfilter
iptables -t filter -F
iptables -t nat -F
iptables -t mangle -F
iptables -t raw -F
#Eliminazione delle chain non standard vuote
iptables -t filter -X
iptables -t nat -X
iptables -t mangle -X
Il comando
codice:
iptables -F
è generalmente equivalente a
codice:
iptables -t
filter -F
quindi se non si specifica una table con il parametro "-t" iptables opera sulla tabella "FILTER". Il comando "-F" esegue il flush di tutte le regole nella tabella. In questa configurazione, tutto il traffico di rete è permesso (occhio che anche il masquerading viene cancellato).

Come prima cosa imposto il firewall per droppare tutte le connessioni su tutte le interfacce di rete. Con questa configurazione, il mio serverino è assolutamente "muto" sulla rete, il firewall scarta tutti i pacchetti di rete in ingresso, uscita e transito senza dare alcuna risposta. E' un metodo in pò brutale ma è più efficace negare tutto e permettere solo quello che mi interessa piuttosto che ragionare al contrario e autorizzare tutto, negando solo quello che non si vuole. In questo modo, a meno che non impostiamo una regola che autorizzi il transito di un determinato tipo di traffico di rete, il pacchetto attraverserà tutta la chain senza trovare corrispondenze e cozzerà contro la policy di default, venendo droppato.

Uso questi 3 comandi per impostare quindi quella che è la policy di default (per la tabella FILTER) del firewall:
codice:
# Imposta le policy predefinite per il sistema
# Drop di tutte le connessioni in ingresso al sistema
iptables -P INPUT DROP
# Drop di tutte le connessioni che attraversano il sistema, quindi quelle
# generate dai client della LAN o dirette verso essi.
iptables -P FORWARD DROP
# Drop di tutte le connessioni in uscita dal sistema
iptables -P OUTPUT DROP
Al posto di DROP si potrebbe usare REJECT (che ha lo stesso effetto ma con un messaggio di errore) ma, come prima cosa, rifiutare un pacchetto richiede più risorse al sistema che dropparlo ed in secondo luogo se il nostro sistema restituisce un messaggio di reject svela innanzi tutto la propria esistenza e la presenza di un firewall configurato. Magari non è molto ma sempre per il discorso della superficie d'attacco meno informazioni non necessarie si danno e meglio è...

Questa regola fà eccezione: L'interfaccia di Loopback non è una vera e propria interfaccia di rete ma un'interfaccia fittizia che il sistema e alcune le applicazioni utilizzano per il loro funzionamento interno. Autorizzarne tutto il traffico è sicuro e consigliabile però la policy di default che ho inserito droppa anche il traffico su questa interfaccia quindi inserisco queste regole per "liberare" il traffico sul loopback:
codice:
# Autorizzo tutto il traffico di rete su lo
iptables -A INPUT -i lo -j ACCEPT iptables-A OUTPUT -o lo -j ACCEPT
Dalla guida prendo pari pari anche il codice che aggiunge alla tabella "FILTER" una chain personalizzata a quelle di default, per la neutralizzazione gli attacchi SYN flood. A quanto ho capito, i pacchetti SYN sono essenzialmente il traffico "buono" della rete ma questo traffico può essere utilizzato in volumi massicci per saturare un sistema, portandolo sostanzialmente al blocco. Sono sincero, non ho idea di cosa siano: Però mi fido, la catena esegue dei "DROP" e non degli ACCEPT e se mi dovesse dare problemi la posso sempre rimuovere...
codice:
# Chain personalizzata per il contrasto degli attacchi syn-flood
iptables -N syn-flood iptables -A INPUT -i eth0 -p tcp syn -j syn-flood
iptables -A syn-flood -m limit limit 1/s limit-burst 4 -j RETURN
iptables -A syn-flood -j DROP
"Iptables -N" genera una nuova chain denominata, in questo caso, "syn-flood" definisce prima di tutto l'interfaccia di riferimento (eth0) ed il tipo di traffico cui si riferisce la chain (SYN), la regola di accettabilità del traffico e la policy di default per la chain (DROP).
La guida ora utilizza un funzionalità di ispezione dei pacchetti del nostro firewall per esaminare il traffico di rete in ingresso dalla WAN. Individuare precocemente il traffico utile e scartare il resto è un ottimo modo per ridurre il carico sul sistema quindi la adotto anche io:
codice:
## Uso la Stateful Inspection droppare tutto il traffico TCP non di tipo SYN: iptables -A INPUT -i eth0 -p tcp syn -m state state NEW -j DROP
Inizio ora a popolare di regole le mie chain predefinite.
Un prima regola necessaria al funzionamento del mio server come router e quindi da applicare al firewall l'ho già definita ed è appunto quella per abilitare il masquerading:
codice:
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Commentare ogni singola riga sarebbe un lavoro molto lungo soprattutto perchè, come detto, non sono un esperto di networking e finirei per fare un gran copia incolla, dovendo ammettere molta ignoranza. Suggerisco di sfruttare il firewall generator che ho linkato sopra, esaminando le varie regole che propone
Ho preso una configurazione "semplice" dal wizard, tanto per iniziare. Ho assegnato IP statici ad entrambe le interfacce e specificato che il sistema opererà come firewall/gateway. L'impostazione del firewall e modifiche alla configurazione adottata ci accompagneranno fino alla fine, dell'installazione quindi è inutile sperare di prendere ora una configurazione che vada bene fino alla fine.
Lo script è piuttosto lungo e la presenza dei commenti tende ad essere dispersiva ma è necessaria. In sostanza lo script inizialmente imposta le variabili che utilizzerà per definire le regole (indirizzi IP e percorsi dei file utilizzati per inviare i comandi al firewall), carica dei moduli del kernel specifici che potrebbero non essere attivati di default e reimposta il firewall ad una configurazione di base, eliminando tutte le regole eventualmente presenti, eliminando tutte le chain non standard eventualmente presenti ed impostando la policy di default per tutte le chain predefinite su "DROP".

In questa configurazione il nostro firewall è assolutamente "muto" ed inaccessibile via rete. Il passo successivo potrebbe essere già autorizzare i servizi che ci interessano ma il wizard è piuttosot avanzato e fà prima una cosa diversa: Genera delle chain personalizzate in cui farà confluire determinate tipologie di traffico. Questo permette diverse cose, da un miglioramento delle prestazioni generali (perchè traffico identificato da subito come malevolo viene droppato immediatamente) ad una approfondita analisi dei log, per individuare comportamenti anomali o tentativi reiterati di attacco. iRedMail, il tool che utilizzerò per configurare il server di posta installa e configura fail2ban, che è un tool di analisi dei log per il ban automatico degli IP o degli host sospettati di essere malevoli e fà largo uso proprio di chain personalizzate. E' quindi utile inserirne fin da subito nel nostro script e capire come funzionino perchè dopo dovremmo rimetterci mano.

Nello specifico lo script definisce le seguenti chain personalizzate e le popola con le regole indicate nella sezione "Populate User Chains":
codice:
# User-Specified Chains
# Create a chain to filter INVALID packets
$IPT -N bad_packets
# Create another chain to filter bad tcp packets
$IPT -N bad_tcp_packets
# Create separate chains for icmp, tcp (incoming and outgoing),
# and incoming udp packets.
$IPT -N icmp_packets
# Used for UDP packets inbound from the Internet
$IPT -N udp_inbound
# Used to block outbound UDP services from internal network
# Default to allow all
$IPT -N udp_outbound
# Used to allow inbound services if desired
# Default fail except for established sessions
$IPT -N tcp_inbound
# Used to block outbound services from internal network
# Default to allow all
$IPT -N tcp_outbound
Le regole definiscono una serie di "parametri" che per permettono di identificare il traffico che arriva al nostro firewall, principalmente sulla base delle caratteristiche del pacchetto stesso. Non sono in grado di disquisire sulla correttezza o meno delle regole utilizzate, semplicemente mi limito a leggerle e modificare quelle che voglio agiscano diversamente.
Ad esempio, mi interessa che il mio sistema risponda ai ping, almeno dalla rete locale. Identifico quindi la sezione relativa al traffico ICMP e modifico le regole relative (elimino parte dei commenti per questioni di spazio).

codice:
# icmp_packets chain
...
# Echo - uncomment to allow your system to be pinged.
#Uncomment the LOG command if you also want to log PING attempts
#
# $IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j LOG \
# --log-prefix "Ping detected: "
# $IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j ACCEPT
$IPT -A icmp_packets -p ICMP -i !$INET_IFACE --icmp-type 8 -j ACCEPT
# Bydefault, however, drop pings without logging. Blaster
# and other worms have infected systems blasting pings.
# Comment the line below if you want pings logged, but it
# will likely fill your logs.
#$IPT -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j DROP
$IPT -A icmp_packets -p ICMP -i $INET_IFACE --icmp-type 8 -j DROP
Ho commentato le righe presenti di default e le ho sostituite con comandi personalizzati: "$IPT -A icmp_packets -p ICMP -i !$INET_IFACE --icmp-type 8 -j ACCEPT" accetta tutto il traffico ICMP type 8 (il classico ping) che non viene dall'interfaccia $INET_IFACE che abbiamo deciso essere quella esterna. Il "!" davanti ad un valore ne inverte il senso quindi questo comando si legge: "Nella chain "icmp_packets" se identifichi un pacchetto nel protocollo ICMP di tipo 8 che non proviene dall'interfaccia $INET_IFACE accettalo". Lo stesso risultato si poteva ottenere indicando "-i $LOCAL_IFACE" al posto di "-i !$INET_IFACE" ma il metodo che ho utilizzato è più generale e dovrebbe funzionare bene anche in presenza di più di una interfaccia LAN.
Analogamente, dobbiamo droppare il solo traffico ICMP type 8 proveniente dalla interfaccia $INET_IFACE ed è quello che fà la regola "$IPT -A icmp_packets -p ICMP -i $INET_IFACE --icmp-type 8 -j DROP" definita subito dopo.
Le sezioni successive iniziano finalmente a lavorare sul filtering vero e proprio dei servizi. Dopo aver sostanzialmente abilitato tutto il traffico in uscita, inizia a popolare le chain relative ai pacchetti in INPUT.

codice:
# INPUT Chain #
echo "Process INPUT chain ..."
# Allow all on localhost interface
$IPT -A INPUT -p ALL -i $LO_IFACE -j ACCEPT
# Drop bad packets
$IPT -A INPUT -p ALL -j bad_packets
...
# Rules for the private network (accessing gateway system itself)
$IPT -A INPUT -p ALL -i $LOCAL_IFACE -s $LOCAL_NET -j ACCEPT
$IPT -A INPUT -p ALL -i $LOCAL_IFACE -d $LOCAL_BCAST -j ACCEPT
# Inbound Internet Packet Rules
# Accept Established Connections
$IPT -A INPUT -p ALL -i $INET_IFACE -m state --state ESTABLISHED,RELATED \
-j ACCEPT
...
La prima regola abilita tutto il traffico "INPUT" sul loopback, come avevamo deciso in precedenza e subito dopo ci mostra il primo utilizzo delle chain personalizzate definite prima: per droppare i bad packet non si limita a istruire il firewall per dropparli ma li reindirizza alla chain denominata "bad_packet" e quindi il pacchetto seguirà dal quel momento uscità dalla chain "INPUT" per seguire le regole definite nella chain bad_packet.
Le successive regole sostanzialmente accettano tutto il traffico proveniente dall'interfaccia sulla lan proveniente dalla range di IP della rete locale e il traffico proveniente dalla stessa rete e destinato al braodcast della stessa rete locale.
Interessante la prossima regola: Viene accettato tutto il traffico proveniente dall'interfaccia di rete esterna (internet) ma solo se relativo a connessioni esistenti o già stabilite. Questo blocca tutto il traffico in ingresso che non sia stato richiesto dall'interno della nostra rete. E' una regola fondamentale, perchè, per capirci, in assenza di questo trust un eventuale browser sul firewall non sarebbe in grado di visualizzare alcuna pagina web in quanto il traffico di risposta proveniente dal server esterno verrebbe droppato.

codice:
#
# FORWARD Chain
# Drop bad packets
$IPT -A FORWARD -p ALL -j bad_packets
# Accept TCP packets we want to forward from internal sources
$IPT -A FORWARD -p tcp -i $LOCAL_IFACE -j tcp_outbound
# Accept UDP packets we want to forward from internal sources
$IPT -A FORWARD -p udp -i $LOCAL_IFACE -j udp_outbound
# If not blocked, accept any other packets from the internal interface
$IPT -A FORWARD -p ALL -i $LOCAL_IFACE -j ACCEPT
# Deal with responses from the internet
$IPT -A FORWARD -i $INET_IFACE -m state --state ESTABLISHED,RELATED \
-j ACCEPT
Questa chain invece processa il traffico relativo che transita sul nostro firewall ma che è invece relativo ai client della nostra rete. Anche qui i bad_packet vengono girati alla chain competente (e quindi non vengono inoltrati ai client della nostra rete) ed i pacchetti validi vengono instradati attraverso le chain competenti ad analizzarli. Anche qui viene accettato il traffico in ingresso se relativo a connessioni esistenti o richieste dall'interno della rete.
codice:
# OUTPUT Chain #
echo "Process OUTPUT chain ..."
# Generally trust the firewall on output
# However, invalid icmp packets need to be dropped
# to prevent a possible exploit.
$IPT -A OUTPUT -m state -p icmp --state INVALID -j DROP
# Localhost
$IPT -A OUTPUT -p ALL -s $LO_IP -j ACCEPT $IPT -A OUTPUT -p ALL -o $LO_IFACE -j ACCEPT
# To internal network
$IPT -A OUTPUT -p ALL -s $LOCAL_IP -j ACCEPT $IPT -A OUTPUT -p ALL -o $LOCAL_IFACE -j ACCEPT
# To internet
$IPT -A OUTPUT -p ALL -o $INET_IFACE -j ACCEPT
# Log packets that still don't match
$IPT -A OUTPUT -m limit --limit 3/minute --limit-burst 3 -j LOG \
--log-prefix "OUTPUT packet died: "
Questa chain chiude la tabella "FILTER" e riguarda il traffico in uscita.
In questo caso stiamo autorizzando tutto il traffico valido proveniente dal nostro sistema e diretto all'esterno. Come dice il commento alla prima regola, il traffico non valido viene comunque droppato per evitare che possa essere veicolo di eventuali attacchi, magari verso altri sistemi.

Il contenuto delle tabelle "NAT" e "MANGLE" è davvero scarno ma per ora è tutto quello che ci serve, si limita ad abilitare il masquerading nella tabella NAT e lascia vuota la tabella MANGLE.
Lo script utilizza una sintassi diversa per abilitare il masquerading rispetto a quella che avevo indicato in precedenza. A quanto ho capito cercando informazioni, questa sintassi è preferibile nel caso l'interfaccia esterna abbia un IP statico, mentre quelle che avevo indicato è preferibile in presenza di un IP dinamico. Io ho IP statico quindi la adotto ma me lo segno lo stesso:
codice:
###############################################################################
# # nat table
# ###############################################################################
echo "Load rules for nat table ..."
###############################################################################
# # PREROUTING chain
# ###############################################################################
# # POSTROUTING chain
#
# Sintassi per abilitare il masquerading da preferire in presenza di un IP
# statico sull'interfaccia esterna:

$IPT -t nat -A POSTROUTING -o $INET_IFACE \
-j SNAT --to-source $INET_ADDRESS

# Sintassi per abilitare il masquerading da preferire in presenza di un IP
# dinamico sull'interfaccia esterna:
# $IPT -t nat -A POSTROUTING -o $INET_IFACE -j MASQUERADE

###############################################################################
# # mangle table
# ###############################################################################
# The mangle table is used to alter packets. It can alter or mangle them in
# several ways. For the purposes of this generator, we only use its ability
# to alter the TTL in packets. However, it can be used to set netfilter
# mark values on specific packets. Those marks could then be used in another
# table like filter, to limit activities associated with a specific host, for
# instance. The TOS target can be used to set the Type of Service field in
# the IP header. Note that the TTL target might not be included in the
# distribution on your system. If it is not and you require it, you will
# have to add it. That may require that you build from source.
echo "Load rules for mangle table ..."
Questi due screen sono dei port-scan effettuati sulle due interfacce di rete del server di test appena avviato (e quindi senza firewall configurato) e dopo aver attivato il firewall ottenuto con lo script generato dal wizard:



Per ottenere i primi due screen, con l'elenco completo dei servizi disponibili sul server, sono stati sufficienti meno di 10 secondi ciascuno. Per gli ultimi due, ho invece fermato a mano lo scanner dopo circa 2 ore ciascuno.
Notiamo innanzitutto due cose: Nei primi due screen i servizi ssh e webmin (spostato dalla 10000 alla 15000) sono presenti solo per la rete interna (192.168.100.150) quindi la configurazione che avevo impostato per restringere l'accesso ssh e webmin effettivamente funziona, negli ultimi due screen invece non ci sono porte aperte quindi il nostro sistema, pur effettuando correttamente il forwarding ed il routing per i client della rete, è assolutamente irraggiungibile via rete quindi neppure via ssh o webmin per l'amministrazione.

Dobbiamo aggiungere allo script i servizi che vogliamo poter utilizzare sul nostro server: Nello specifico io voglio che la macchina svolga i compiti di server di posta (quindi SMTP, POP3, IMAP con SSL), server web (con e senza SSL), server DNS e DHCP per la rete locale. Devo inoltre ricordarmi di abilitare la porta che ho assegnato a webmin, in questo caso solo protocollo TCP.
Sfruttando il solito wizard, otteniamo un nuovo script che aggiunge le seguenti regole alla tabella FILTER, sotto varie chain:

codice:
# DNS Server
# Configure the server to use port 53 as the source port for requests
# Note, if you run a caching-only name server that only accepts queries
# from the private network or localhost, you can comment out this line.

$IPT -A udp_inbound -p UDP -s 0/0 --destination-port 53 -j ACCEPT

# If you don't query-source the server to port 53 and you have problems,
# uncomment this rule. It specifically allows responses to queries
# initiated to another server from a high UDP port. The stateful
# connection rules should handle this situation, though.
# $IPT -A udp_inbound -p UDP -s 0/0 --source-port 53 -j ACCEPT
...
# DNS Server - Allow TCP connections (zone transfers and large requests)
# This is disabled by default. DNS Zone transfers occur via TCP.
# If you need to allow transfers over the net you need to uncomment this line.
# If you allow queries from the 'net, you also need to be aware that although
# DNS queries use UDP by default, a truncated UDP query can legally be
# submitted via TCP instead. You probably will never need it, but should
# be aware of the fact.
# $IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 53 -j ACCEPT

# Web Server

# HTTP
#$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 80 -j ACCEPT

# HTTPS (Secure Web Server)
#$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 443 -j ACCEPT

# Email Server (SMTP)
#$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 25 -j ACCEPT

# Email Server (POP3)
#$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 110 -j ACCEPT

# Email Server (IMAP4)
#$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 143 -j ACCEPT

# SSL Email Server (POP3)
#$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 995 -j ACCEPT

# SSL Email Server (IMAP4)
#$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 993 -j ACCEPT

# sshd
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 22 -j ACCEPT

# User specified allowed TCP protocol
$IPT -A tcp_inbound -p TCP -s 0/0 --destination-port 15000 -j ACCEPT
...
# Allow DHCP client request packets inbound from internal network
$IPT -A INPUT -p UDP -i $LOCAL_IFACE --source-port 68 --destination-port 67 \
-j ACCEPT
Ho evidenziato in rosso quello che non mi convince o devo cambiare.
Il mio DNS sarà solo locale, quindi posso commentare la regola "$IPT -A udp_inbound -p UDP -s 0/0 --destination-port 53 -j ACCEPT", come da istruzioni dell'autore del wizard. Inoltre, se mi va bene che web e mail siano fruibili da qualunque rete (-s 0/0), non mi va assolutamente bene che lo siano il servizio ssh e la porta su cui è in ascolto webmin quindi cambio le due regole relative come segue:

codice:
# sshd $IPT -A tcp_inbound -p TCP -i $LOCAL_IFACE --destination-port 22 -j ACCEPT # User specified allowed TCP protocol $IPT -A tcp_inbound -p TCP -i $LOCAL_IFACE --destination-port 15000 -j ACCEPT
In questo modo, il servizio ssh e webmin dovrebbero essere raggiungibili solo dall'interfaccia di rete interna.
E' ora necessario impostare il sistema per caricare lo script del firewall automaticamente:
Ci sono diversi modi per farlo ma personalmente preferisco aggiungere una riga ad hoc al file di configurazione delle interfacce di rete, in modo che ad ogni reload della scheda di rete pubblica lo script venga rieseguito ed il firewall riapplicato.

Per copiare lo script sul firewall, è sufficiente ricorrere alla solita connessione ssh, e con i diritti di root (devo scrivere in una directory per cui servono i privilegi) lancio i comandi:
codice:
touch /etc/firewall
chmod +x /etc/firewall
nano /etc/firewall
per creare il file "firewall" nella directory "/etc", renderlo eseguibile ed infine aprirlo con nano. A questo punto è sufficiente un copia-incolla e salvare il nuovo file con "Ctrl + x". Il file così creato sarà leggibile ed eseguibile da tutti gli utenti del server quindi aggiungo anche i comandi:
codice:
chmod g= /etc/firewall
chmod o= /etc/firewall
per togliere a tutti gli utenti tutti i permessi sul file. Solo "root" o un utente abilitato sudo possono così lanciare e/o modificare/consultare lo script di configurazione del firewall.
Per aggiungere il firewall allo script di avvio dell'interfaccia di rete edito il file "/etc/network/interfaces" con il comando
codice:
nano /etc/network/interfaces
per aggiungervi il comando di pre-up
codice:
pre-up /etc/firewall
Il mio file /etc/network/interfaces diventa quindi:
codice:
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface - Outside -
auto eth0
allow-hotplug eth0
iface eth0 inet static
address 192.168.50.2
netmask 255.255.255.0
gateway 192.168.50.1
dns-nameservers 208.57.222.222 151.99.125.2 8.8.8.8
broadcast 192.168.50.255
pre-up /etc/firewall

# Scheda di rete secondaria - Inside -
auto eth1
allow-hotplug eth1
iface eth1 inet static
address 192.168.100.150
netmask 255.255.255.0
dns-search fracassetti.lan
broadcast 192.168.100.255
Lo script era lungi dall'essere perfetto o non migliorabile e col tempo vi ho apportato alcune modifiche e correzioni.

Come prima cosa, ho rimosso il servizio "iptables" dal caricamento al boot: Nell'installazione base non mi dà alcun fastidio ma installando altri servizi che si aspettavano che il firewall venisse gestito con questo daemon, mi sono trovato le regole impostate dal pre-up sovrascritte dalla configurazione prevista da queste applicazioni. Non ho disinstallato iptables, ho semplicemente rimosso il caricamento del servizio al boot. Il firewall viene quindi configurato esclusivamente dal mio script "/etc/firewall" richiamato dal automaticamente quando viene attivata la scheda di rete eth0.
Il metodo più "elegante" sarebbe forse quello di modificare la configurazione iniziale di iptables per caricare le mie regole ma anche procedere in questo modo non è necessariamente sbagliato e modificare la configurazione di iptables chiederebbe un pò di adattamento e revisione della sintassi di questo script che non ho ne tempo ne voglia di fare, per ora.

Per togliere il servizio dall'esecuzione al boot ho usato, come root, il comando:
# update-rc.d iptables remove
Ricordo che nel caso si volesse arrestare il firewall la procedura corretta non è cancellare tutte le regole usando il comando "iptables -F" (lo script imposta la policy di default in "DROP" quindi il flush delle regole isola completamente il server dalla rete!!) ma aggiungere il parametro "stop" allo script, usando quindi il comando "sh /etc/firewall stop" (sempre come root o usando sudo). In questo modo l'esecuzione dello script si ferma alla sezione "Flush Any Existing Rules or Chains".


Ho notato un errore/mancato aggiornamento nella sezione iniziale dello script: Il modulo che permette di indicare più porte per ogni comando in realtà ora dovrebbe chiamarsi ipt_multiport quindi il comando:
codice:
# /sbin/modprobe multiport
in realtà dovrebbe essere:
codice:
# /sbin/modprobe ipt_multiport