Debian e iptables

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 è.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *