[OpenBSD]

[Precedente: Tabelle] [Indice] [Successivo: Network Address Translation]

PF: Packet Filtering


Table of Contents


Introduzione

Il Packet filtering è il selettivo passaggio o blocco di pacchetti attraverso un'interfaccia di rete. I criteri utilizzati da pf(4) nell'analisi dei pacchetti sono basati sugli headers dei Layer 3 (IPv4 e IPv6) e Layer 4 (TCP, UDP, ICMP, e ICMPv6). I criteri più spesso utilizzati sono l'indirizzo sorgente e destinazione, porta sorgente e destinazione e protocollo.

Le regole di filtraggio specificano il criterio da utilizzare per analizzare un pacchetto deve essere confrontato e l'azione da intraprendere, blocco o passaggio, quando vi è una corrispondenza. Le regole di filtraggio sono valutate in ordine sequenziale, dalla prima alll'ultima. A meno che non si abbia una corrispondenza con una regola contenente la keyword quick, il pacchetto sarà controllato con tutte le regole di filtraggio prima di intraprendere l'azione finale. L'ultima regola di confronto è la "vincente" e decide quale azione eseguire sul pacchetto. Esiste una regola implicita del pass all all'inizio delle regole di configurazione, questo significa che se non vi dovesse essere alcuna corrispondenza con le regole del filtro, il pacchetto sarà fatto passare.

Sintassi della regola

La sintassi semplificata per la regola di filtraggio è:
action [direction] [log] [quick] [on interface] [af] [proto protocol] \
   [from src_addr [port src_port]] [to dst_addr [port dst_port]] \
   [flags tcp_flags] [state]
action
L'azione da intraprendere per i pacchetti per i quali c'è una corrispondenza sarà pass o block. L'azione di pass porta il pacchetto al kernel per processarlo ulteriormente mentre l'azione di block sarà dettata dalle opzioni stabilite nella block-policy. L'azione di default può essere esclusa specificando block drop o block return.
direction
La direzione con la quale il pacchetto si muove attraverso l'interfaccia, in oppure out.
log
Specifica che il pacchetto dovrebbe essere loggato con pflogd(8). Se la regola crea uno stato solo il pacchetto che stabilisce lo stato viene loggato. Per erffettuare il log di tutti i pacchetti usare log-all.
quick
Se un pacchetto ha una corrispondenza con una regola che specifica quick, quella regola viene considerata l'ultima regola di confronto e viene intrapresa quella specifica azione.
interface
Il nome o il gruppo di interfaccia di rete attraverso la quale il pacchetto si sta muovendo. Le interfaccie possono essere aggiunte a gruppi arbitrari usando il comando: ifconfig(8). Alcuni gruppi vengono creati automaticamente anche dal kernel: Questo permetterebbe di avere regole con corrispondenza a ogni pacchetto che attraversa qualsiasi interfaccia ppp o carp.
af
La famiglia di indirizzi del pacchetto, inet per IPv4 oppure inet6 per IPv6. Di solito PF è in grado di determinare questo parametro basandosi sull'indirizzo sorgente e/o destinazione.
protocol
Il protocollo del Layer 4 del pacchetto:
src_addr, dst_addr
L'indirizzo sorgente/destinazione dell'header IP. Gli indirizzi possono essere specificati come:
src_port, dst_port
La porta sorgente/destinazione nell'header dell'Layer 4. Le porte possono essere specificate come:
tcp_flags
Specifica i flag che devono essere settati nel TCP header quando si usa un protocollo tcp. I flag sono definiti come flags check/mask. Per esempio:flags S/SA - questo istruisce PF a cercare solo pacchetti con flag S e A (SYN e ACK) e a considerare una corrispondenza solo quando il flag SYN è "on". In OpenBSD 4.1 e successivi, i flag di default S/SA sono applicati a tutte le regole di filtraggio.
state
Specifica se informazioni di stato sono conservate per pacchetti che hanno corrispondenza con questa regola.

Default Deny

Quando si configura un firewall è raccomandato un approcio "default deny". Cioè negare il transito a tutto e consentire selettivamente che determinati pacchetti passino attraverso il firewall. Questo approcio è raccomandato perchè è cautelativo e consente di scrivere più facilmente le regole di configurazione.

Per creare un firewall con una policy default deny le prime due regole dovrebbero essere:

block in  all
block out all

Queste bloccano tutto il traffico su tutte le interfaccie in ogni direzione da ovunque a ovunque.

Traffico passante

Ora il traffico deve essere fatto passare esplicitamente attraverso il firewall altrimenti verrà gettato dalla policy deny di default. Qui entrano in gioco i criteri di confronto come porta sorgente/destinazione, indirizzo sorgente/destinazione e protocollo. Le regole che consentono al traffico di passare attraverso il firewall dovrebbero comunque essere scritte per essere le più restrittive possibili. Questo affinchè sia consentito il passaggio al solo traffico desiderato.

Qualche esempio:

# Passaggio del traffico in ingresso alla dc0 dalla rete locale 192.168.0.0/24,
# alla macchina OpenBSD con indirizzo IP 192.168.0.1. Inoltre consentire il traffico
# di ritorno in uscita sulla dc0.
pass in  on dc0 from 192.168.0.0/24 to 192.168.0.1
pass out on dc0 from 192.168.0.1 to 192.168.0.0/24


# Consentire il passaggio del traffico TCP in ingresso all'interfaccia fxp0 al
# web server in esecuzione sulla macchina OpenBSD. Il nome dell'interfaccia, fxp0,
# è usata come indirizzo di destinazione, così i pacchetti soddisferanno solo questa
# regola per essere destinati alla macchina OpenBSD pass in on fxp0 proto tcp from any
# to fxp0 port www

La keyword quick

Come desritto precedentemente, ogni pacchetto è valutato dalla prima all'ultima regola di configurazione. Di default, il pacchetto viene segnato nel passaggio, questo può essere cambiato a ogni regola, potrebbe essere cambiato diverse volte prima della fine delle regole di filtraggio.L'ultima regola che ha una corrispondenza "vince". C'è un'eccezione a tutto questo. L'opzione quick su una regola di filtraggio ha l'effetto di cancellare ogni ulteriore regola e di intraprendere la specifica azione. Diamo un'occhiata ad un paio di esempi:

Sbagliato:

block in on fxp0 proto tcp from any to any port ssh
pass  in all

In questo caso la regola di blocco viene considerata ma non avrà mai alcun effetto perchè è seguita da una linea che consente il passaggio di tutto il traffico.

Meglio:

block in quick on fxp0 proto tcp from any to any port ssh
pass  in all

Queste regole vengono valutate un pò differentemente. Se la regola di blocco ha una corrispondenza, a causa dell'opzione quick, il pacchetto sarà bloccato e le regole successive ignorate.

Keeping State

Una proprietà importante di Packet Filter è il "keeping state" o "stateful inspection". Stateful inspection è la capacità di PF di tracciare lo stato o il progresso di una connessione di rete. Conservando in una tabella di stato le informazioni relative a ogni connessione, PF è in grado, velocemente, di determinare se un pacchetto che attraversa il firewall appartiene a una connessione già esistente. Se vi appartiene il pacchetto attraversa il firewall senza essere verificato da tutte le regole di configurazione.

Keeping state ha molti vantaggi tra i quali regole di configurazione più semplici e migliori performance di packet filtering. PF è in grado di effettuare un confronto con pacchetti che si muovono in entrambe le direzioni rispetto ai dati conservati nella tabella di stato, questo significa che le regole di filtaggio per i pacchetti in risposta su una connessione tracciata non devono essere scritte. E dato che i pacchetti che appartengono a una stateful connection non devono passare attraverso la valutazione delle regole di configurazione, il tempo impiegato da PF per processare questi pacchetti è notevolmente inferiore.

Quando una regola permette di creare uno stato, il primo pacchetto che soddisfa la regola crea uno "stato" tra client e server. Ora, non solo i pacchetti che vanno dal client al server soddisfano l'opzione keep state e attraversano il firewall senza ulteriori controlli ma così fanno anche i pacchetti di risposta tra server e client.

A partire da OpenBSD 4.1, tutte le regole di filtraggio creano automaticamente uno stato nella tabella quando vi è una corrispondenza con una reagola. Nelle precedenti versioni occorreva specificare l'opzione keep state.

Esempio che utilizza OpenBSD 4.1 e successive versioni:

pass out on fxp0 proto tcp from any to any

Esempio che utilizza OpenBSD 4.0 e versioni precedenti:

pass out on fxp0 proto tcp from any to any keep state

Questo consente a tutto il traffico TCP in uscita dall'interfaccia fxp0 e al corrispondente traffico di risposta di oltrepassare le regole del firewall. L'uso del keep state migliora in modo significativo le performance del firewall e i confronti con la tabella di stato sono incredibilmente più veloci rispetto al passaggio attraverso tutte le regole del firewall.

L'opzione modulate state funziona come il keep state ma si applica solo ai pacchetti TCP. Con l'opzione modulate state il numero di sequenza iniziale (Initial Sequence Number - ISN) delle connessioni in uscita è random. Questo è utile per proteggere le connessioni iniziali con alcuni sistemi operativi che hanno un prevedibile ISN. A partire da OpenBSD 3.5, l'opzione modulate state può essere usata in regole che utilizzano altri protocolli oltre il TCP.

Keep state sul traffico in uscita TCP, UDP, ICMP e modulate su TCP ISN:

pass out on fxp0 proto { tcp, udp, icmp } from any \
    to any modulate state

Un altro vantaggio del keeping state è che il corrispondente traffico ICMP oltrepassa il firewall. Per esempio se in una connessione TCP tracciata arriva un messaggio ICMP source-quench riferito a questa connessione, verrà verificata la corrispondenza con la tabella di stato e il messaggio ICMP oltrepasserà le regole del firewall.

Il controllo dei dati della tabella di stato è ottenuta globalmente dalle opzioni runtime state-policy e sulla base di ogni regola dalle opzioni keyword if-bound, group-bound, e floating state. Queste keyword hanno lo stesso significato anche se usate con l'opzione state-policy. Esempio:

pass out on fxp0 proto { tcp, udp, icmp } from any \
    to any modulate state (if-bound)

Questa regola imporrebbe che affinchè i pacchetti abbiano una corrispondenza con la tabella di stato devono transitare attraverso l'interfaccia fxp0.

Da notare che le regole nat, binat, e rdr implicitamente creano stati che restano tali fin quando la connessione attraversa le regole di configurazione.

Keeping State per l'UDP

Si sentirà dire che "Non si può creare uno stato con l'UDP perchè l'UDP è un protocollo stateless!" cioè senza stato. E' vero che una sessione di comunicazione UDP non ha nessun stato (un esplicito inizio e una esplicita fine della comunicazione), ma PF è in grado di crearne una per ogni session UDP. Nel caso di protocolli con pacchetti senza "inizio" e "fine", PF semplicemente tiene traccia del tempo trascorso dall'ultima corrispondenza con un pacchetto. Se si è raggiunto un tempo limite, lo stato viene cancellato. Il valore di questo intervallo di tempo può essere settato options nella sezione del file pf.conf.

Opzioni di Stateful Tracking

Quando una regola crea un nuovo stato nella tabella di stato possono essere specificate alcune opzioni che controllano come deve essere creato lo stato. Sono disponibili le seguenti opzioni:
max number
Limita il numero massimo di stati creati dalla regola a number. Se il numero massimo viene raggiunto, il pacchetto che creerebbe lo stato viene gettato finchè non decresce il numero di stati esistenti.
no state
Blocca la creazione automatica di uno stato.
source-track
Questa opzione abilita il tracciamento del numero di stati creati per indirizzo IP sorgente. Questa opzione ha due formati: Il numero totale di indirizzi IP sorgente tracciati globalmente può essere controllata con src-nodes runtime option.
max-src-nodes number
Quando l'opzione source-track è usata, max-src-nodes limiterà il numero di indirizzi IP sorgente che simultaneamente possono creare stati. Questa opzione può essere usata solo con la regola source-track.
max-src-states number
Quando l'opzione source-track è usata, max-src-states limiterà il numero di stati simultanei che possono essere creati per indirizzo IP sorgente. Lo scopo di questo limite (per esempio, stati creati solo da questa regola o stati creati da tutte le regole che usano source-track) è in funzione dell'opzione source-track specificata.

Le opzioni sono specificate all'interno delle parentesi e immediatamente dopo una delle keyword di stato (keep state, modulate state, o synproxy state). Opzioni multiple sono separate da virgole. In OpenBSD 4.1 e versioni successive l'opzione keep state diviene implicita in tutte le regole. Comunque, quando si specificano le opzioni una delle keyword di stato deve essere specificata prima delle opzioni.

Un esempio di regola:

pass in on $ext_if proto tcp to $web_server \
    port www keep state \
    (max 200, source-track rule, max-src-nodes 100, max-src-states 3)

La regola precedente si comporta così:

Un set separato di restrizioni può essere usato su connessioni TCP che hanno completato l'handshake a 3 vie.

max-src-conn number
Limita il numero massimo di connessioni simultanee TCP che hanno completato l'handshake a tre vie che un singolo host può effettuare.
max-src-conn-rate number / interval
Limita il numero di nuove connessioni ad un certo valore per intervallo di tempo.

Entrambe queste opzioni automaticamente invocano l'opzione source-track e sono incompatibili con il source-track global.

Dato che questi limiti sono utilizzati solo su connessioni TCP che hanno completato l'handshake 3-way, possono essere intraprese azioni più tenaci per tutti gli indirizzi IP con comportamenti scorretti.

overload <table>
Mette un indirizzo IP scorretto nella tabella
flush [global]
Elimina ogni altro stato che ha una corrispondenza con questa regola ed è creato da questo IP sorgente. Quando viene specificato global, elimina tutti gli stati corrispondenti a questo indirizzo IP sorgente senza tener conto di quale regola ha creato lo stato.

Un esempio:

table <abusive_hosts> persist
block in quick from <abusive_hosts>

pass in on $ext_if proto tcp to $web_server \
    port www flags S/SA keep state \
    (max-src-conn 100, max-src-conn-rate 15/5, overload <abusive_hosts> flush)

This does the following:

TCP Flags

La verifica di pacchetti TCP basato sui flag è il più delle volte usato per filtrare pacchetti TCP che tentano di effettuare una nuova connessione. I flag TCP e il loro significato sono riportati di seguito:

Per fare in modo che PF controlli i flag TCP in una regola, deve essere usata la keyword flags con la seguente sintassi:

flags check/mask
flags any

La mask dice a PF che ispezioni solo quei flag specifici mentre check specifica quale di quei flag devono essere "on" nell'header affinchè si abbia una corrispondenza. L'utilizzo della keyword any permette di settare nell'header ogni combinazione di flag.

pass in on fxp0 proto tcp from any to any port ssh flags S/SA

La regola precedente consente il passaggio di traffico TCP con il flag SYN settato analizzando i flag SYN e ACK. Un pacchetto con flag SYN e ECE settati soddisferebbe la regola precedente mentre un pacchetto con SYN e ACK o solo ACK no.

In OpenBSD 4.1 e versioni successive, i flag di default applicati alle regole TCP sono flags S/SA. Insieme al keep state di default in OpenBSD 4.1 e versioni successive queste due regole sono equivalenti:

pass out on fxp0 proto tcp all flags S/SA keep state
pass out on fxp0 proto tcp all

Ogni regola avrà corrispondenza con il flag SYN settato e il flag ACK non settato ed ognuna creerà uno stato. I flag di default possono essere sostituiti usando l'opzione flags come visto precedentemente.

In OpenBSD 4.0 e versioni precedenti non vi erano flag di default applicati a nessuna regola. Ogni regola doveva specificare quale flag controllare e specificare l'opzione keep state.

pass out on fxp0 proto tcp all flags S/SA keep state

Si dovrebbe fare attenzione all'uso dei flag, capire quello che si sta facendo e il perchè, fare attenzione ai consigli dati dalle persone perchè la maggior parte sono sbagliati. Alcune persone suggeriscono di creare uno stato solo nel caso in cui il solo flag SYN è settato. Una regola di questo tipo appare così:

     . . . flags S/FSRPAUEW  bad idea!!

La teoria è di creare stati solo all'inizio di una sessione TCP e la sessione dovrebbe iniziare solo con il flag SYN. Il problema è che alcuni siti stanno iniziando a usare il flag ECN, e con il flag ECN settato ogni tentavivo di connettersi a un host con una regola di firewall come quella precedente sarebbe vano. Una regola migliore sarebbe di non specificare alcun flag e lasciare a PF l'utilizzo dei flag di default. Se si dovesse decidere di utilizzare i flag, questa combinazione dovrebbe essere la più appropriata:

. . . flags S/SAFR

Pur essendo pratico e sicuro non è necessario controllare i flag SYN e RST se il traffico è anche sottoposto allo scrub. Il processo di scrubbing porterà PF a rifiutare ogni pacchetto in arrivo con combinazioni illegali di flag TCP (come SYN e RST) e a normalizzare potenziali combinazioni ambigue (come SYN and FIN).

TCP SYN Proxy

Di solito quando un client inizia una connessione TCP a un server, PF farà passare i pacchetti dell' handshake così come sono tra i due host. PF è comunque in grado di effettuare il proxy dell'handshake. Con il proxy dell'handshake, PF completa l'handshake con il client, inizia un handshake con il server, e quindi passa i pacchetti tra i due. Il beneficio di questo processo è che nessun pacchetto è inviato al server prima che il client completi l'handshake. Questo elimina la minaccia al server di flood TCP SYN da client scorretti che non desiderano completare l'handshake.

Il TCP SYN proxy viene abilitato usando la keyword synproxy state nelle regole di filtraggio. Esempio:

pass in on $ext_if proto tcp from any to $web_server port www \
   flags S/SA synproxy state

Qui la connessione al server web sarà soggetta al TCP proxy di PF.

Per il modo con cui il synproxy state funziona, include anche le funzionalità del keep state e modulate state.

Il SYN proxy non funzionerà se PF è in esecuzione su un bridge(4).

Bloccare pacchetti contraffatti

La contraffazione dell'indirizzo si ha quando un utente con cattive intenzioni falsifica l'indirizzo IP dei pacchetti che invia per nascondere il suo vero indirizzo o per farsi passare per qualcun altro sulla rete. Una volta che l'utente ha contraffatto il suo indirizzo può eseguire un attacco a una rete senza rivelare l'indirizzo sorgente da dove parte l'attacco o tentare di guadagnare l'accesso ai servizi di rete che sono ristretti a un certo numero di indirizzi IP.

PF offre alcune protezioni contro la contraffazione dell'indirizzo IP attraverso la keyword antispoof:

antispoof [log] [quick] for interface [af]
log
Specifica i tipi di pacchetti che dovrebbero essere sottoposti a log con pflogd(8).
quick
Se il pacchetto ha una corrispondenza con questa regola, questa sarà considerata la regola vincente e non verranno effettuate ulteriori valutazioni con le regole successive.
interface
L'interfaccia di rete sulla quale attivare la protezione di spoofing. Questa può essere anche una lista di interfaccie.
af
La famiglia di indirizzi per i quali attivare la protezione di spoofing, sia inet per IPv4 oppure inet6 per IPv6.

Esempio:

antispoof for fxp0 inet

Quando vengono caricate le regole di configurazione, ogni corrispondenza con la keyword antispoof viene espansa in due regole di filtraggio. Assumendo che l'interfaccia fxp0 abbia indirizzo IP 10.0.0.1 e una subnet mask di 255.255.255.0 (cioè /24), la regola antispoof si espenderebbe in:

block in on ! fxp0 inet from 10.0.0.0/24 to any
block in inet from 10.0.0.1 to any

Queste regole fanno due cose:

NOTA: Le regole di filtraggio che l'antispoof espande bloccheranno anche i pacchetti inviati sull'interfaccia di loopback agli indirizzi locali. E' buona regola non filtrare mai l'interfaccia di loopback e ciò diventa necessario nelle regole anti contraffazione:

set skip on lo0

antispoof for fxp0 inet

L'uso di antispoof dovrebbe essere ristretto alle interfaccie alle quali è assegnato un indirizzo IP. Usare antispoof su un'interfaccia senza un indirizzo IP comporterà avere delle regole del tipo:

block drop in on ! fxp0 inet all
block drop in inet all

Con queste regole c'è il rischio di bloccare tutto il traffico in arrivo su tutte le interfaccie.

Unicast Reverse Path Forwarding

A partire da OpenBSD 4.0, PF offre l'Unicast Reverse PAth Forwarding (uRPF). Quando un pacchetto viene controllato da uRPF, l'indirizzo sorgente viene agganciato alla tabella di routing. Se l'interfaccia esterna presente nella tabella di routing è la stessa interfaccia utilizzata come indirizzo sorgente dal pacchetto, il controllo uRPF ha esito positivo. Se non c'è corrispondenza tra gli indirizzi è possibile che l'indirizzo sorgente si contraffatto.

Il controllo uRPF sul pacchetto può essere ottenuto usando la keyword urpf-failed nelle regole di filtraggio:

block in quick from urpf-failed label uRPF

Da notare che il controllo uRPF ha senso in un sistema simmetrico di routing.

uRPF fornisce le stesse funzionalità delle regole antispoof.

Riconoscimento passivo di sistema operativo

Il riconoscimento passivo del sistema operativo (Passive OS Fingerprinting - OSFP) è un metodo utilizzato per riconoscere il sistema operativo di un host remoto in base a determinate caratteristiche dei pacchetti TCP SYN dell'host. Queste informazioni possono essere usate come criterio di confronto nelle regole di filtraggio.

PF determina il sistema operativo remoto comparando le caratteristiche TCP SYN del pacchetto con i dati del file fingerprints file, che per default è /etc/pf.os. Una volta che PF è abilitato, la lista corrente di fingerprint può essere vista con il seguente comando:

# pfctl -s osfp

Con una regola di filtraggio, un fingerprint può essere specificato a livello di classe OS, versione o patch. Ognuno di questi dati è visibile nell'output del comando precedente pfctl. Per specificare un fingerprint in una regola si usa la keyword os:

pass  in on $ext_if from any os OpenBSD keep state
block in on $ext_if from any os "Windows 2000"
block in on $ext_if from any os "Linux 2.4 ts"
block in on $ext_if from any os unknown

Tutti i pacchetti con fingerprint OS non noto appartengono alla classe speciale di sistema operativo unknown.

PRENDERE NOTA di ciò che segue:

Opzioni IP

Di default PF blocca pacchetti con opzioni IP settate. Questo può rendere difficoltoso il funzionamento di utility con "OS fingerprinting" come nmap. Se si dovesse avere un'applicazione che richiede il passaggio di questi pacchetti, come nel caso di pacchetti multicast o IGMP, si può utilizzare la direttiva allow-opts:
pass in quick on fxp0 all allow-opts

Esempio di regole di configurazione

Di seguito è riportato un esempio di regole di confogurazione di filtraggio. L'host che esegue PF si comporta da firewall tra una piccola LAN e Internet. Sono mostrate solo le regole di filtraggio; queueing, nat, rdr, ecc., sono lasciate fuori dall'esempio.

ext_if  = "fxp0"
int_if  = "dc0"
lan_net = "192.168.0.0/24"

# tabella contenente tutti gli indirizzi IP assegnati al firewall
table <firewall> const { self }

# non filtrare sull'interfaccia di loopback
set skip on lo0

# funzione di scrub per i pacchetti in arrivo
scrub in all

# setup a default deny policy
block all

# configura una policy deny di default
block in quick from urpf-failed

# consente le connessioni ssh dal computer affidabile 192.168.0.15 
# della network locale;
# usa "block return" così che un TCP RST è inviato a chiudere 
# connessioni bloccate; usa "quick" per evitare che questa regola sia 
# esclusa da eventuali regole "pass" successive 
block return in quick on $int_if proto tcp from ! 192.168.0.15 \
   to $int_if port ssh

# consente il traffico nella network locale
# queste regole creano stati per la presenza dell'opzione "keep state"
pass in  on $int_if from $lan_net to any
pass out on $int_if from any to $lan_net

# passaggio di tcp, udp, e icmp verso Internet sull'interfaccia esterna 
# keep state per l'udp e icmp; modulate state su tcp.
pass out on $ext_if proto { tcp udp icmp } all modulate state

# consente le connessioni ssh sull'interfaccia esterna se i pacchetti 
# NON sono destinati al firewall (sono destinati ad host sulla rete 
# locale); log del pacchetto iniziale per un eventuale riconoscimento 
# di tentativi di connessioni; uso di tcp syn proxy per ottenere il 
# proxy della connessione. Il flag di default "S/SA" sarà applicato
# automaticamente.
pass in log on $ext_if proto tcp from any to ! <firewall> \
   port ssh synproxy state

[Precedente: Tabelle] [Indice] [Successivo: Network Address Translation]


[back] www@openbsd.org
$OpenBSD: filter.html,v 1.2 2008/05/27 10:00:23 tobias Exp $