Product SiteDocumentation Site

14.2. Firewall o filtraggio dei pacchetti

Il firewall è un punto di filtraggio di rete ed è efficace solo per i pacchetti che devono transitare da esso. Perciò può essere efficace solo se il passaggio attraverso il firewall è l'unico instradamento possibile per quei pacchetti.
Il kernel Linux incorpora il firewall netfilter. Può essere controllato nello spazio utente con i comandi iptables, ip6tables, arptables e ebtables.
Tuttavia, i comandi iptables di Netfilter sono stati sostituiti da nftables, che evita molti dei suoi problemi. Il suo design comporta una minore duplicazione di codice e può essere gestito con il solo comando nft. A partire da Debian Buster, il framework nftables è utilizzato in modo predefinito. I comandi menzionati in precedenza sono forniti dalle versioni che utilizzano, per impostazione predefinito, l'API del kernel di nftables. Se si preferiscono i comandi "classici", i relativi binari possono essere attivati usando update-alternatives.
Per abilitare un firewall predefinito in Debian eseguire:
# apt install -y nftables
Reading package lists... Done
...
# systemctl enable nftables.service
Created symlink /etc/systemd/system/sysinit.target.wants/nftables.service → /lib/systemd/system/nftables.service.

14.2.1. Comportamento di nftables

Non appena il kernel sta processando un pacchetto di rete, si mette in pausa e ci permette di ispezionare il pacchetto per decidere cosa fare con esso. Per esempio si potrebbe volere buttare o scartare determinati pacchetti in arrivo, modificare altri pacchetti in diversi modi, bloccare alcuni pacchetti in uscita per controllare la presenza di malware o ridirigere alcuni pacchetto il prima possibile per collegare le interfacce di rete o per suddividere il carico dei pacchetti in arrivo tra più sistemi.
Una buona comprensione dei livelli 3,4 e 5 del modello OSI (Open Systems Interconnection) è essenziale per ricavare il massimo da netfilter.
Il firewall è configurato con tabelle, composte di regole contenute in catene. A differenza di iptables, nftables non ha nessuna tabella predefinita. L'utente decide quali e come le tabelle devono essere create. Ogni tabella deve avere assegnata soltanto una delle seguenti cinque famiglie: ip, ip6, inet, arp e bridge. ip è utilizzato se la famiglia non è stata speficificata.
Sono presenti due tipologie di catene: catene di base e catene regolari. Una catena di base è un punto d'ingresso per pacchetti dallo stack di rete. Le catene di base sono registrate negli hook di Netfilter, ad esempio essi vedono i pacchetti che passano nello stack TCP/IP. D'altro canto, una catena regolare non è attaccata a nessun hook e per questo non vede nessun traffico. Tuttavia può essere utilizzata come una destinazione di un salto per una migliore organizzazione dei ruoli.
Le regole sono composte da istruzioni, che includono alcune espressioni che devono essere confrontate il cui risultato determina l'istruzione da eseguire, come accept, drop, queue, continue, return, jump chain e goto chain.

14.2.2. Passare da iptables a nftables

I comandi iptables-translate e ip6tables-translate possono essere utilizzati per tradurre vecchi comandi iptables nella nuova sintassi di nftables. Possono essere tradotti anche interi insiemi di ruoli, in questo caso vengono migrati i ruoli configurati su un computer che ha Docker installato:
# iptables-save > iptables-ruleset.txt
# iptables-restore-translate -f iptables-ruleset.txt

# Translated by iptables-restore-translate v1.8.7 on Wed Mar 16 22:06:32 2022
add table ip filter
add chain ip filter INPUT { type filter hook input priority 0; policy accept; }
add chain ip filter FORWARD { type filter hook forward priority 0; policy drop; }
add chain ip filter OUTPUT { type filter hook output priority 0; policy accept; }
add chain ip filter DOCKER
add chain ip filter DOCKER-ISOLATION-STAGE-1
add chain ip filter DOCKER-ISOLATION-STAGE-2
add chain ip filter DOCKER-USER
add rule ip filter FORWARD counter jump DOCKER-USER
add rule ip filter FORWARD counter jump DOCKER-ISOLATION-STAGE-1
add rule ip filter FORWARD oifname "docker0" ct state related,established counter accept
add rule ip filter FORWARD oifname "docker0" counter jump DOCKER
add rule ip filter FORWARD iifname "docker0" oifname != "docker0" counter accept
add rule ip filter FORWARD iifname "docker0" oifname "docker0" counter accept
add rule ip filter DOCKER-ISOLATION-STAGE-1 iifname "docker0" oifname != "docker0" counter jump DOCKER-ISOLATION-STAGE-2
add rule ip filter DOCKER-ISOLATION-STAGE-1 counter return
add rule ip filter DOCKER-ISOLATION-STAGE-2 oifname "docker0" counter drop
add rule ip filter DOCKER-ISOLATION-STAGE-2 counter return
add rule ip filter DOCKER-USER counter return
add table ip nat
add chain ip nat PREROUTING { type nat hook prerouting priority -100; policy accept; }
add chain ip nat INPUT { type nat hook input priority 100; policy accept; }
add chain ip nat OUTPUT { type nat hook output priority -100; policy accept; }
add chain ip nat POSTROUTING { type nat hook postrouting priority 100; policy accept; }
add chain ip nat DOCKER
add rule ip nat PREROUTING fib daddr type local counter jump DOCKER
add rule ip nat OUTPUT ip daddr != 127.0.0.0/8 fib daddr type local counter jump DOCKER
add rule ip nat POSTROUTING oifname != "docker0" ip saddr 172.17.0.0/16 counter masquerade
add rule ip nat DOCKER iifname "docker0" counter return
# Completed on Wed Mar 16 22:06:32 2022
# iptables-restore-translate -f iptables-ruleset.txt > ruleset.nft
# nft -f ruleset.nft
# nft list ruleset
table inet filter {
	chain input {
		type filter hook input priority filter; policy accept;
	}

	chain forward {
		type filter hook forward priority filter; policy accept;
	}

	chain output {
		type filter hook output priority filter; policy accept;
	}
}
table ip nat {
	chain DOCKER {
		iifname "docker0" counter packets 0 bytes 0 return
		iifname "docker0" counter packets 0 bytes 0 return
	}

	chain POSTROUTING {
		type nat hook postrouting priority srcnat; policy accept;
		oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 0 bytes 0 masquerade
		oifname != "docker0" ip saddr 172.17.0.0/16 counter packets 0 bytes 0 masquerade
	}

	chain PREROUTING {
		type nat hook prerouting priority dstnat; policy accept;
		fib daddr type local counter packets 1 bytes 60 jump DOCKER
		fib daddr type local counter packets 0 bytes 0 jump DOCKER
	}

	chain OUTPUT {
		type nat hook output priority -100; policy accept;
		ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
		ip daddr != 127.0.0.0/8 fib daddr type local counter packets 0 bytes 0 jump DOCKER
	}

	chain INPUT {
		type nat hook input priority 100; policy accept;
	}
}
table ip filter {
	chain DOCKER {
	}

	chain DOCKER-ISOLATION-STAGE-1 {
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
		counter packets 0 bytes 0 return
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-2
		counter packets 0 bytes 0 return
	}

	chain DOCKER-ISOLATION-STAGE-2 {
		oifname "docker0" counter packets 0 bytes 0 drop
		counter packets 0 bytes 0 return
		oifname "docker0" counter packets 0 bytes 0 drop
		counter packets 0 bytes 0 return
	}

	chain FORWARD {
		type filter hook forward priority filter; policy drop;
		counter packets 0 bytes 0 jump DOCKER-USER
		counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-1
		oifname "docker0" ct state related,established counter packets 0 bytes 0 accept
		oifname "docker0" counter packets 0 bytes 0 jump DOCKER
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
		iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
		counter packets 0 bytes 0 jump DOCKER-USER
		counter packets 0 bytes 0 jump DOCKER-ISOLATION-STAGE-1
		oifname "docker0" ct state established,related counter packets 0 bytes 0 accept
		oifname "docker0" counter packets 0 bytes 0 jump DOCKER
		iifname "docker0" oifname != "docker0" counter packets 0 bytes 0 accept
		iifname "docker0" oifname "docker0" counter packets 0 bytes 0 accept
	}

	chain DOCKER-USER {
		counter packets 0 bytes 0 return
		counter packets 0 bytes 0 return
	}

	chain INPUT {
		type filter hook input priority filter; policy accept;
	}

	chain OUTPUT {
		type filter hook output priority filter; policy accept;
	}
}
Gli strumenti iptables-nft, ip6tables-nft, arptables-nft e ebtables-nft sono versioni di iptables che utilizzano le API di nftables, in modo che l'utente possa continuare ad utilizzare la vecchia sintassi di iptables, anche se non è raccomandato; questi strumenti dovrebbero essere usati soltanto per retrocompatibilità.

14.2.3. Sintassi di nft

Il comando nft permette la manipolazione di tabelle, catene e regole. L'opzione table supporta diverse operazioni: add, create, delete, list e flush. nft add table ip6 mangle aggiunge una nuova tabella della famiglia ip6.
Per inserire una nuova catena di base nella tabella filter, si può utilizzare il seguente comando (notare che il punto e virgola è protetto da un backslash quando si usa Bash):
# nft add chain filter input { type filter hook input priority 0 \; }
Le regole sono normalmente aggiunte con la seguente sintassi: nft add rule [family] table chain handle handle statement.
Il comando insert è simile al comando add, ma la regola data viene anteposta all'inizio della catena o prima della regola con il gestore dato invece che alla fine o dopo quella regola. Ad esempio, il comando seguente inserisce una regola prima di quella con il gestore numero 8:
# nft insert rule filter output position 8 ip daddr 127.0.0.8 drop
L'esecuzione dei comandi nft non genera modifiche permanenti alla configurazione, quindi vengono perse se non sono salvate. I regole del firewall sono presenti in /etc/nftables.conf. Un modo semplice per salvare in modo permanente la configurazione attuale del firewall è di eseguire come root nft list ruleset > /etc/nftables.conf.
nft permette molte altre operazioni, per maggiori informazioni fare riferimento alla sua pagina del manuale nft(8).

14.2.4. Installare le regole ad ogni avvio

Per abilitare un firewall predefinito in Debain è necessario registrare i ruoli in /etc/nftables.conf ed eseguire come root systemctl enable nftables. È possibile fermare il firewall eseguendo come root nft flush ruleset.
Negli altri casi, si consiglia di posizionare lo script di configurazione nella direttiva up del file /etc/network/interfaces. Nel seguente esempio, lo script è memorizzato in /usr/local/etc/arrakis.fw.

Esempio 14.1. File interfaces che richiama lo script del firewall

auto eth0
iface eth0 inet static
    address 192.168.0.1
    network 192.168.0.0
    netmask 255.255.255.0
    broadcast 192.168.0.255
    up /usr/local/etc/arrakis.fw
Questo presuppone ovviamente che si stia usando ifupdown per configurare le interfacce di rete. Se si sta utilizzando qualcos'altro (come NetworkManager o systemd-networkd), bisogna invece fare riferimento alla loro documentazione per trovare il modo di eseguire lo script dopo che l'interfaccia di rete è stata abilitata.