Vediamo come configurare il firewall (iptables) sulla nostra Debian.
Disclaimer:
Scrivo questo articolo più che altro per me, per ricordarmi come ho fatto. Poi se torna utile a qualcun altro tanto di guadagnato.
Non credo sia opportuno disquisire su quali porte aprire e quali no: ognuno conosce le proprie esigenze; qui ci concentriamo invece su tutto il resto. Non sto a dirvi neanche come si installa iptables perché spero sia ovvio. Se non lo è, probabilmente dovreste leggere altre guide prima di questa.
Innanzitutto decidiamo dove mettere lo script, per esempio io uso /usr/local/bin/Iptables.
Creeremo varie funzioni, tra le quali:
- ipt_list() per stampare le regole di iptables
- ipt_load() per caricare le regole
- ipt_clear() per cancellare le regole
Cominciamo con la più semplice:
ipt_list() {
iptables --list --verbose --numeric --line-numbers
}
Credo che il significato sia chiaro: stampa la lista di regole di iptables. Anche le opzioni sono abbastanza chiare: l’unica, forse, non immediata ma la più importante è ‘–numeric’: mostra gli IP invece che i domini. Cosa fondamentale se non volete che iptables faccia una richiesta DNS per ogni IP che deve stampare (e possono essere taaanti).
Procediamo:
ipt_clear() {
iptables --policy INPUT ACCEPT
iptables --policy FORWARD ACCEPT
iptables --policy OUTPUT ACCEPT
iptables --flush
iptables --delete-chain
}
Questa funzione:
- ripristina le policy delle catene (chain) al default “ACCEPT” (noi le imposteremo a “DROP”)
- cancella (flush) tutte le regole da tutte le catene
- cancella tutte le catene create da noi
Aggiungiamo anche un case che gestisca i parametri dello script. Dovreste avere qualcosa di simile:
#!/bin/bash
eth=wired
ipt_list() {
iptables --list --verbose --numeric --line-numbers
}
ipt_load() {
echo 'Ciao bello!!'
}
ipt_clear() {
iptables --policy INPUT ACCEPT
iptables --policy FORWARD ACCEPT
iptables --policy OUTPUT ACCEPT
iptables --flush
iptables --delete-chain
}
case $1 in
t|test)
ipt_clear
ipt_load
ipt_list
sleep 10
ipt_clear
ipt_list
exit 0
;;
l|load)
ipt_clear
ipt_load
ipt_list
exit 0
;;
c|clear)
ipt_clear
ipt_list
exit 0
;;
esac
ipt_list
Il case controlla il primo parametro (e solo quello!) e con ‘l’ carica le regole (al momento nessuna) e con ‘c’ le cancella. Senza argomenti stampa le regole. Notare anche ‘t’: carica le regole, aspetta 10 secondi e poi le cancella. Utile quando lavoro su un server remoto per evitare che regole troppo aggressive mi blocchino fuori da un server (che brutta esperienza… :/)
eth=wired è l’interfaccia di rete; cambiate wired con la vostra: la trovate con ifconfig.
Bene, ora configuriamo ipt_load, che è la funzione dove c’è più ciccia:
ipt_load() {
# Impostiamo le policy a DROP (si, siamo paranoici)
iptables --policy INPUT DROP
iptables --policy FORWARD DROP
iptables --policy OUTPUT DROP
# lasciamo che l'interfaccia loopback sia libera
iptables --append INPUT --in-interface lo --jump ACCEPT
iptables --append OUTPUT --out-interface lo --jump ACCEPT
# Catena custom (ci serve per dopo)
iptables --new-chain LOGGING
# Apriamo qualche porta
# outbound ssh, smtp, whois, http, imap2, https, submission, imaps, vnc, tor
iptables --append OUTPUT --out-interface $eth --protocol tcp --match multiport --destination-port 22,25,43,80,143,443,587,993,5901:5920,9001 --match state --state NEW,ESTABLISHED --jump ACCEPT
iptables --append INPUT --in-interface $eth --protocol tcp --match multiport --source-port 22,25,43,80,143,443,587,993,5901:5920,9001 --match state --state ESTABLISHED --jump ACCEPT
# inbound ping
iptables --append INPUT --protocol icmp --icmp-type echo-request --jump ACCEPT
iptables --append OUTPUT --protocol icmp --icmp-type echo-reply --jump ACCEPT
# outbound ping
iptables --append OUTPUT --protocol icmp --icmp-type echo-request --jump ACCEPT
iptables --append INPUT --protocol icmp --icmp-type echo-reply --jump ACCEPT
# outbound DNS, ntp, fwknop-client
iptables --append OUTPUT --protocol udp --out-interface $eth --match multiport --destination-port 53,123,62201 --match state --state NEW,ESTABLISHED --jump ACCEPT
iptables --append INPUT --protocol udp --in-interface $eth --match multiport --source-port 53,123 --match state --state ESTABLISHED --jump ACCEPT
# logghiamo tutti i pacchetti che scartiamo
# levels: emerg, alert, crit, error, warning, notice, info or debug
iptables --append INPUT --jump LOGGING
iptables --append OUTPUT --jump LOGGING
iptables --append LOGGING --match limit --limit 2/min --jump LOG --log-prefix "[iptables] " --log-level info
iptables --append LOGGING --jump DROP
}
Notare il prefisso “[iptables] “. Queste sono solo alcune porte che uso io. Va naturalmente adattato alle proprie esigenze. Solo un paio di commenti:
- le policy di tutte le catene sono impostate a DROP, quindi se non aprite qualche porta la rete risulterà bloccata
- abbiamo creato una catena LOGGING dove passano tutti i pacchetti non gestiti dalle nostre regole
- ogni pacchetto che arriva nella catena LOGGING viene prima “loggato” in syslog e poi scartato
Bene, ora caricate le regole con “Iptables l” e dovreste ottenere qualcosa di simile:
Chain INPUT (policy DROP 3183 packets, 1788K bytes)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
2 0 0 ACCEPT tcp -- wired * 0.0.0.0/0 0.0.0.0/0 multiport sports 22,25,43,80,143,443,587,993,5901:5920,9001 state ESTABLISHED
3 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 8
4 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 0
5 0 0 ACCEPT udp -- wired * 0.0.0.0/0 0.0.0.0/0 multiport sports 53,123 state ESTABLISHED
6 0 0 LOGGING all -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
Chain OUTPUT (policy DROP 2980 packets, 557K bytes)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0
2 0 0 ACCEPT tcp -- * wired 0.0.0.0/0 0.0.0.0/0 multiport dports 22,25,43,80,143,443,587,993,5901:5920,9001 state NEW,ESTABLISHED
3 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 0
4 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0 icmptype 8
5 0 0 ACCEPT udp -- * wired 0.0.0.0/0 0.0.0.0/0 multiport dports 53,123,62201 state NEW,ESTABLISHED
6 0 0 LOGGING all -- * * 0.0.0.0/0 0.0.0.0/0
Chain LOGGING (2 references)
num pkts bytes target prot opt in out source destination
1 0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 2/min burst 5 LOG flags 0 level 6 prefix "[iptables] "
Affinché le regole sopravvivano al riavvio lanciate netfilter-persistent save: saranno salvate in /etc/iptables/rules.v4.
Adesso vogliamo che i pacchetti scartati vengano loggati in un file a parte, /var/log/iptables.log. Create il file /etc/rsyslog.d/iptables.conf ed inserite:
if $msg contains '[iptables]' then /var/log/iptables.log
&stop
e riavviate rsyslog. Ora il file si popolerà di messaggi imperscrutabili. 🙂 Col tempo però diventerà sempre più grande, quindi dobbiamo farlo “ruotare” da logrotate. Create il file /etc/logrotate.d/iptables ed inserite:
/var/log/iptables.log {
olddir /var/log/old/iptables
createolddir 750 root adm
size 5M
dateext
dateformat -%Y-%m-%d.log
compress
delaycompress
missingok
rotate 19
create 0640 root adm
postrotate
/usr/bin/killall -HUP rsyslogd
endscript
}
Ecco cosa gli stiamo dicendo di fare: sposta il file di log vecchio in /var/log/old/iptables. Se necessario, crea la cartella con quei permessi. Sposta il log quando raggiunge i 5MB, aggiungi la data di oggi come suffisso, comprimilo alla prossima rotazione, se non esiste è uguale, tieni 19 file, avvisa rsyslog della rotazione.
Non capirò mai perché i log vengano ruotati giornalmente o settimanalmente… Ma perché? Credo che una configurazione come quella sopra dovrebbe essere il default considerando che 1. lo spazio su disco non è più un problema al giorno d’oggi e 2. i log sono qualcosa di preziosissimo quando qualcosa va male, quindi più se ne hanno e meglio è.