[OpenBSD]

[Předchozí: Tabulky] [Obsah] [Další: Network Address Translation]

PF: Filtrování paketů


Obsah


Úvod

Filtrování paketů je selektivní povolení nebo blokování datových paketů, které prochází přes síťové rozhraní. Kritéria, která pf(4) používá při inspekci paketů jsou založena na vrstvě 3 (IPv4 a IPv6) a vrstvě 4 (TCP, UDP, ICMP a ICMPv6) záhlaví. Nejčastěji používaná kritéria jsou zdrojová a cílová adresa, zdrojový a cílový port a protokol.

Filtrovací pravidla specifikují kritéria, která musí paket splnit a následnou akci, buď blokování nebo povolení, která se vykoná jakmile se najde shoda. Filtrovací pravidla jsou vykonávána sekvenčně od prvního k poslednímu. Pokud paket neodpovídá pravidlu obsahujícímu klíčové slovo quick, tak paket prochází všechna pravidla předtím než nastane finální akce. Poslední pravidlo, které odpovídá je "vítěz" a bude určovat, která akce se s paketem provede. Je zde defaultní pravidlo pass all na začátku seznamu, které znamená, že pokud paket neodpovídá žádnému filtrovacímu pravidlu, tak výsledná akce bude pass.

Syntaxe pravidel

Obecná, velmi zjednodušená syntaxe pro filtrovací pravidla je:
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
Akce, která se vykoná na odpovídajících paketech. Buď pass nebo block. Akce pass propustí paket dále do jádra k dalšímu zpracování, zatímco block akce reaguje podle nastavení block-policy volby. Defaultni reakce může být přepsána buď pomocí specifikování block drop nebo block return.
direction
Směr, kterým se paket pohybuje na rozhraní. Buď in nebo out.
log
Specifikuje, že paket by měl být zaznamenán pomocí pflogd(8). Pokud pravidlo vytváří stav, tak pouze paket, který tento stav vytváří je zaznamenán. K zaznamenání všech paketů bez rozdílu použijte log (all).
quick
Pokud paket odpovídá pravidlu specifikujícímu quick, tak je toto pravidlo považováno za poslední odpovídající a specifikovaná action je provedena.
interface
Název nebo skupina síťového rozhraní přes které se paket pohybuje. Rozhraní mohou být přidána do skupin pomocí ifconfig(8) příkazu. Některé skupiny jsou vytvořeny automaticky jádrem: Tohle zajistí, že pravidlo bude odpovídat jakémukoliv paketu, který jde přes jakékoliv ppp nebo carp rozhraní.
af
Rodina adres paketu. Buď inet pro IPv4 nebo inet6 pro IPv6. PF je obvykle schopné rozlišit tento parametr na základě zdrojové a/nebo cílové adresy(adres).
protocol
Protokol paketu z vrstvy 4:
src_addr, dst_addr
Zdrojová/cílová adresa v IP záhlaví. Adresy mohou být specifikovány jako:
src_port, dst_port
Zdrojový/cílový port v záhlaví paketu na vrstvě 4. Porty mohou být specifikovány jako:
tcp_flags
Specifikuje volby, které musí být nastaveny v TCP záhlaví při použítí proto tcp. Volby jsou specifikovány jako flags check/mask. Příklad: flags S/SA - toto PF říká, aby se podíval na S a A (SYN a ACK) volby a aplikoval toto pravidlo na paket jen pokud je SYN volba "zapnuta" (defaultně to platí pro všechna TCP pravidla). flags any instructs PF not to check flags.
state
Specifikuje, zda se ukládá informace o stavu spojení pro pakety, které odpovídají tomuto pravidlu.

Zakázání všeho

Doporučená praxe při nastavování firewallu je použít přístup "default deny". To znamená zakázat vše a poté selektivně povolovat určitý provoz přes firewall. Tento přístup je doporučený, protože zvyšuje bezpečnost a zjednodušuje psaní pravidel.

Pro vytvoření "default deny" filtrovací politiky stačí vytvořit tyto první dvě filtrovací pravidla:

block in  all
block out all

Toto bude blokovat všechen provoz na všech rozhraních v jakémkoliv směru a odkudkoliv kamkoliv.

Povolování provozu

Provoz nyní musí být explicitně povolen pro průchod firewallem, protože jinak bude zahozen díky politice "default deny". Tohle je oblast, kde kritéria paketů jako zdrojový/cílový port, zdrojová/cílová adresa a protokol přicházejí do hry. Kdykoliv je provoz povolen procházet přes firewall, tak pravidlo(pravidla) musí být maximálně restriktivní jak to jen je možné. Je to kvůli tomu, aby se zajistilo, že jen chtěný provoz a opravdu jen chtěný provoz bude povolen.

Některé příklady:

# Pass traffic in on dc0 from the local network, 192.168.0.0/24,
# to the OpenBSD machine's IP address 192.168.0.1. Also, pass the
# return traffic out on 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


# Pass TCP traffic in on fxp0 to the web server running on the
# OpenBSD machine. The interface name, fxp0, is used as the
# destination address so that packets will only match this rule if
# they're destined for the OpenBSD machine.
pass in on fxp0 proto tcp from any to fxp0 port www

Klíčové slovo quick

Jak již bylo zmíněno dříve, tak každý paket se vyhodnocuje v pravidlech od shora dolů. Defaultně je paket označen k propuštění což může být změněno kterýmkoliv pravidlem. Může to být změněno tam a zpět několikrát po sobě než se dorazí na konec filtrovacích pravidel. Poslední odpovídající pravidlo "vítězí". Je zde ale vyjímka z tohoto pravidla: quick volba ve filtrovacím pravidle má efekt zrušení jakéhokoliv následného zpracovávání a způsobí, že specifikovaná akce se provede. Pojďme se podívat na několik příkladů:

Špatně:

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

V tomto případě by block řádek měl být vykonán, ale nikdy nebude mít žádný efekt, protože je následován řádkem, který propustí vše.

Lépe:

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

Tyto pravidla jsou vykonány trochu odlišně. Pokud řádek block odpovídá, tak díky volbě quick bude paket zablokován a zbytek pravidel bude ignorován.

Udržování informace o spojení

Jedna z důležitých vlastností PF je udržování informace o spojení "keeping state" nebo kontrola stavu spojení "stateful inspection". Kontrola stavu spojení odkazuje na schopnost PF sledovat stav nebo vývoj síťového spojení. Pomocí ukládání informace o každém spojení do stavové tabulky je PF schopné rychle rozhodnout jestli paket procházející firewallem náleží do již existujícího navázaného spojení. Pokud ano, tak je puštěn skrze firewall aniž by procházel dalšími pravidly.

Udržování informace o spojení má mnoho výhod včetně lehčích pravidel a lepšího výkonu filtrování paketů. PF je schopné kontrolovat pakety pohybující se v jakémkoliv směru proti záznamům ve stavové tabulce což znamená, že filtrovací pravidla, která propuští vracející se provoz nemusí být vůbec napsaná. A díky tomu, že pakety odpovídající navázaným spojení neprochází vyhodnocením pomocí pravidel, tak čas, který PF stráví správou těchto paketů může být výrazně snížen.

Když pravidlo vytváří stav, tak první paket odpovídající pravidlu vytváří "stav" mezi odesílatelem a příjemcem. Nyní nejen pakety odcházející od odesílatele k příjemci odpovídají stavovému záznamu a přeskakují průchod pravidly, ale i pakety vracející se stejnou cestou se chovají stejně.

Všechny pass pravidla automaticky vytváří stavový záznam, když paket odpovídá pravidlu. Toto může být explicitně zrušeno použítím no state volby.

pass out on fxp0 proto tcp from any to any

Toto pravidlo umožňuje jakýkoliv odchozí TCP provoz na fxp0 rozhraní a také povoluje vracející se provoz k tomuto spojení skrze firewall. Udržování informace o spojení výrazně zlepšuje výkon vašeho firewallu, protože vyhledávání stavu jsou dramaticky rychlejší v porovnání s průchodem paketu filtrovacími pravidly.

Volba modulate state funguje stejně jako keep state mimo to, že je funkční pouze pro TCP pakety. S modulate state je Initial Sequence Number (ISN) odchozího spojení randomizováno. Toto je užitečné pro ochranu spojení navazovaných některými operačními systémy, které odvádí špatnou práci při výběru ISN. Pro umožnění lehčích pravidel může být volba modulate state použita v pravidlech, která specifikují protokol jiný než TCP; v těchto případech je vyhodnocována jako keep state.

Udržování informace o spojení pro odchozí TCP, UDP a ICMP pakety a modulování TCP ISN:

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

Další výhoda udržování informace o spojení je, že související ICMP provoz bude propuštěn skrze firewall. Například když TCP spojení prochází firewallem a je o něm udržována informace o spojení a zároveň přijde ICMP source-quench zpráva odkazující na toto TCP spojení, tak bude přiřazena odpovídajícímu záznamu o stavu a propuštěna skrze firewall.

Rozsah stavového záznamu je globálně kontrolován pomocí state-policy volby za běhu a v jednotlivých pravidlech pomocí if-bound a floating klíčových slov pro stavy. Tyto klíčová slova pro jednotlivá pravidla mají stejný význam jako když se použíjí s volbou state-policy . Příklad:

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

Toto pravidlo nařídí, že aby pakety souhlasili se stavovým záznamem, tak musí procházet přes fxp0 rozhraní.

Udržování informace o spojení pro UDP

Možná jste někdy slyšeli, jak se říká, že "Nejde vytvořit informace o stavu UDP, protože UDP je bezstavový protokol!". I když je pravda, že UDP komunikace nemá žádný koncept stavů (explicitně definovaný začátek a konec komunikace), tak to nemá žádný vliv na schopnost PF vytvořit informaci o stavu pro UDP spojení. V případě protokolů bez "počátečních" a "koncových" paketů PF prostě sleduje informaci o tom, kolik času uběhlo od okamžiku, kdy odpovídající paket procházel. Pokud čas vyprší(timeout), tak se informace o stavu smaže. Časové hodnoty je možné nastavit v options sekci pf.conf souboru.

Volby pro sledování stavů

Filtrovací pravidla, která vytvářejí stavové záznamy mohou specifikovat různé volby pro kontrolu chování výsledného stavového záznamu. Následující volby jsou dostupné:
max number
Omezuje maximální číslo stavových záznamů, které pravidlo může vytvořit na number. Pokud je maximum dosaženo, tak pakety, které by normálně vytvořili stavový záznam nebudou tomuto pravidlu vyhovovat, dokud se číslo existujích stavových záznamů nesníží pod uvedený limit.
no state
Zabraňuje pravidlu automaticky vytvářet stavový záznam.
source-track
Tato volba umožňuje sledování počtu stavových záznamů vytvořených pro každou zdrojovou IP adresu. Tato volba má dva formáty: Celkový počet zdrojových IP adres, které mohou být sledovány globálně může být kontrolován pomocí src-nodes on-line volby.
max-src-nodes number
Když se použije source-track volba, tak max-src-nodes omezí počet zdrojových IP adres, které mohou současně vytvořit stav. Tato volba může být použita pouze s source-track rule.
max-src-states number
Když se použije source-track volba, tak max-src-states omezí počet simultánních stavových záznamů, které mohou být vytvořeny pro každou IP adresu. Rozsah tohoto limitu (to značí, stavy vytvořené pouze tímto pravidlem nebo stavy vytvořené všemi pravidly, které používají source-track) je závislý na specifikaci source-track volby.

Volby jsou specifikovány uvnitř závorek a okamžitě po jednom z klíčových slov pro stavy (keep state, modulate state nebo synproxy state). Vícenásobné volby jsou odděleny čárkou. V OpenBSD 4.1 a pozdějších se keep state volba stala implicitně defaultní pro všechna filtrovací pravidla. Navzdory tomuto musí být při specifikaci voleb stavů jedno z klíčových slov pro stavy vždy použito ještě před těmito volbami.

Příklad pravidla:

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)

Pravidlo uvedené výše definuje následující chování:

Samostatná sada omezení může být uplatněna na stateful TCP spojení, která dokončila tzv. 3-cestný handshake.

max-src-conn number
Omezuje maximální počet simultánních TCP spojení, které může jednotlivý systém vytvořit a které dokončili 3-cestný handshake.
max-src-conn-rate number / interval
Omezuje rychlost nových spojení na určitý počet za danný časový interval.

Obě tyto volby automaticky používají source-track rule volbu a jsou nekompatibilní s source-track global.

Protože tyto limity jsou aplikovány pouze proti TCP spojením, které dokončili 3-cestný handshake, tak je možné použít mnohem agresivnější akce proti "útočícím" IP adresám.

overload <table>
Vloží "útočící" IP adresu systému do pojmenované tabulky.
flush [global]
Znič jakékoliv další stavy, které odpovídají tomuto pravidlu a které byly vytvořeny touto zdrojovou IP adresou. Když je global specifikováno, tak znič všechny stavy odpovídající zdrojové IP adrese bez ohledu na to, které pravidlo stav vytvořilo.

Příklad:

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)

Toto dělá následující:

Volby TCP paketu

Výběr TCP paketů podle jejich voleb je nejčastěji používáno k filtrování TCP paketů, které se pokoušejí otevřít nové spojení. TCP volby paketu a jejich významy jsou vypsány zde:

Aby PF kontroloval TCP volby paketu během vykonávání pravidla, tak se používá klíčové slovo flags s následující syntaxí:

flags check/mask
flags any

Část mask PF říká, aby kontroloval pouze specifikované volby a část check specifikuje, která volba(y) musí být "zapnuty" v záhlaví aby nastala shoda. Použití klíčového slova any umožňuje, aby jakákoliv kombinace voleb v záhlaví odpovídala.

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

Protože flags S/SA je nastaveno defaultně, tak výše uvedená pravidla jsou stejná; každé z těchto pravidel propouští TCP provoz s volbou SYN nastavenou, přičemž se dívá pouze na SYN a ACK volby. Paket s volbami SYN a ECE bude odpovídat výše uvedeným pravidlům, ale paket s SYN a ACK nebo pouze ACK už ne.

Defaultní volby mohou být přepsány pomocí volby flags jak je zmíněno výše.

Je důležité být opatrný při použítí voleb -- rozumějte tomu co děláte a proč a buďte opatrní s doporučeními od lidí, protože hodně z nich je špatných. Někteří lidé doporučovali vytvořit stav "jen pokud je SYN volba nastavena a žádné jiné". Takové pravidlo může končit nějak takhle:

     . . . flags S/FSRPAUEW  bad idea!!

Teorie je vytvořit stav jen na začátku TCP spojení a spojení by mělo začínat s SYN volbou a žádnou jinou. Problém je, že některé systémy začínají používat ECN volbu a tak jakýkoliv systém používající ECN, který se pokusí s vámi spojit bude tímto pravidlem odmítnut. Mnohem lepší přístup je nespecifikovat vůbec žádné volby a nechat PF aplikovat defaultní volby do vašich pravidel. Pokud opravdu sami pro sebe chcete volby specifikovat, tak následující kombinace by měla být bezpečná:

. . . flags S/SAFR

I když je toto praktické a bezpečné, tak je také zbytečné kontrolovat FIN a RST volby pokud je provoz také kontrolován pomocí scrub. Scrub proces způsobí, že PF zahodí jakékoliv příchozí pakety s nelegálními kombinacemi TCP voleb (jako SYN a RST) a normalizuje potencionálně nejasné kombinace (jako SYN a FIN).

TCP SYN Proxy

Normálně, když klient inicializuje TCP spojení k serveru, tak PF propustí handshake pakety mezi dvěma body spojení tak jak přicházejí. PF má ovšem schopnost aplikovat proxy na tyto handshake. Když handshake prochází přes tuto proxy, tak PF samotné dokončí handshake s klientem a poté vyvolá handshake se serverem a až poté propustí pakety mezi klientem a serverem. V případě TCP SYN flood útoku útočník nikdy nedokončí 3-cestný handshake, takže pakety útočníka nikdy nedojdou až na chráněný server, ale legitimní klienti handshake dokončit mohou a jsou propuštěni dále. Toto omezuje dopad podvržených TCP SYN floods na chráněnou službu, které jsou zpracovány přímo v PF. Běžné používání této volby není doporučeno, protože narušuje očekávané chování TCP protokolu v případě, kdy server nemůže zpracovat dotaz a v případech kdy je použit load balancer.

TCP SYN proxy se povoluje pomocí klíčového slova synproxy state ve filtrovacích pravidlech. Příklad:

pass in on $ext_if proto tcp to $web_server port www synproxy state

V tomto případě budou spojení k web serveru procházet přes TCP proxy v PF.

Díky tomu jak synproxy state funguje, tak také obsahuje stejnou funkcionalitu jako keep state a modulate state.

SYN proxy nebude fungovat pokud PF běží na bridge(4).

Blokování podvržených paketů

Podvržení adresy ("spoofing") je, když zlomyslný uživatel podvrhne zdrojovou IP adresu v paketech, které přenáší buď kvůli tomu, aby skryl svou reálnou adresu nebo aby se vydával za jiný systém na síti. Jakmile uživatel podvrhne svou adresu, tak může začít síťový útok aniž by odhalil pravý zdroj útoku nebo získat přístup k síťovým službám, které jsou jinak vyhrazené pro určité IP adresy.

PF nabízí určitou ochranu proti podvrženým adresám pomocí klíčového slova antispoof :

antispoof [log] [quick] for interface [af]
log
Určuje, že odpovídající pakety budou zaznamenány pomocí pflogd(8).
quick
Pokud paket odpovídá tomuto pravidlu, tak pravidlo bude považováno za "vítěze" a vyhodnocování pravidel se zde ukončí.
interface
Síťové rozhraní na kterém se má tato ochrana aktivovat. Může to také být seznam rozhraní.
af
Rodina adres pro kterou se má tato ochrana aktivovat. Buď inet pro IPv4 nebo inet6 pro IPv6.

Příklad:

antispoof for fxp0 inet

Když se pravidla nahrávají, tak jakýkoliv výskyt klíčového slova antispoof je rozšířen do dvou filtrovacích pravidel. Pokud má například rozhraní fxp0 IP adresu 10.0.0.1 a síťovou masku 255.255.255.0 (tzn., /24), tak antispoof pravidlo se rozšíří do:

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

Tyto pravidla splňují dvě věci:

POZNÁMKA: Filtrovací pravidla, do kterých se antispoof pravidlo také rozšíři, blokují i pakety posílané přes lokální rozhraní na lokální adresy. Je proto dobrou praxí vynechat filtrování na lokálních rozhraních. Což je dobré i tak, ale stává se to nutností při použítí antispoof pravidel:

set skip on lo0

antispoof for fxp0 inet

Použití antispoof by mělo být omezeno na rozhraní, která mají přiřazenu IP adresu. Použití antispoof na rozhraní bez IP adresy skončí filtrovacím pravidlem, které vypadá nějak takto:

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

S těmito pravidly je zde riziko, že bude blokován všechen příchozí provoz na všech rozhraních.

Unicast Reverse Path Forwarding

PF nabízí vlastnost - Unicast Reverse Path Forwarding (uRPF). Když paket prochází uRPF kontrolou, tak zdrojová IP adresa paketu projde kontrolou v směrovací tabulce. Pokud odchozí rozhraní nalezené v záznamu směrovací tabulky je stejné jako rozhraní přes které paket přišel, tak uRPF kontrola proběhne v pořádku. Pokud ale rozhraní nejsou stejná, tak je možné, že paket má svoji zdrojovou adresu podvrženu.

Kontrola uRPF může být na paketech vykonána pomocí klíčového slova urpf-failed ve filtrovacích pravidlech:

block in quick from urpf-failed label uRPF

Uvědomte si, že uRPF kontrola má smysl pouze v prostředí, kde se používá symetrické směrování.

uRPF poskytuje stejnou funkcionalitu jako antispoof pravidla.

Pasivní detekce operačních systémů

Pasivní detekce operačních systémů - Passive OS Fingerprinting (OSFP) je metoda pro pasivní detekci operačního systému vzdáleného systému (počítače, serveru,...), která je založena na určitých charakteristikách, které tento systém promítne do TCP SYN paketů. Tato informace pak může být použita jako kritérium ve filtrovacích pravidlech.

PF určuje vzdálený operační systém porovnáním charakteristik TCP SYN paketu proti souboru otisků, kterým je defaultně /etc/pf.os. Jakmile je PF povolen, tak aktuální seznam otisků může být zobrazen pomocí tohoto příkazu:

# pfctl -s osfp

V rámci filtrovacího pravidla může být otisk specifikován pomocí třídy OS, verze nebo subtypu/patch verze. Každá z těchto položek je vypsána ve výstupu příkazu pfctl použitého výše. Ke specifikováni otisku ve filtrovacím pravidle je třeba použít kličové slovo os :

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

Speciální třída operačního systému unknown umožňuje nastavit shodu u paketů, kde otisk OS není znám.

NEZAPOMEŇTE na následující:

Volby protokolu IP

Defaultně PF blokuje pakety s použitými volbami IP. Tohle může ztížit práci pro "OS detekční" utility jako nmap. Pokud máte aplikaci, která vyžaduje propuštění těchto paketů, jako například multicast nebo IGMP, tak můžete použít allow-opts direktivu:
pass in quick on fxp0 all allow-opts

Příklad filtrovacích pravidel

Níže je příklad filtrovacích pravidel. Stroj na kterém běží PF vystupuje jako firewall mezi malou interní sítí a Internetem. Ukázány jsou pouze filtrovací pravidla; queueing, nat, rdr, atp., byla v tomto příkladu vynechána.

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

# table containing all IP addresses assigned to the firewall
table <firewall> const { self }

# don't filter on the loopback interface
set skip on lo0

# scrub incoming packets
match in all scrub (no-df)

# setup a default deny policy
block all

# activate spoofing protection for all interfaces
block in quick from urpf-failed

# only allow ssh connections from the local network if it's from the
# trusted computer, 192.168.0.15. use "block return" so that a TCP RST is
# sent to close blocked connections right away. use "quick" so that this
# rule is not overridden by the "pass" rules below.
block return in quick on $int_if proto tcp from ! 192.168.0.15 \
   to $int_if port ssh

# pass all traffic to and from the local network.
# these rules will create state entries due to the default
# "keep state" option which will automatically be applied.
pass in  on $int_if from $lan_net
pass out on $int_if to $lan_net

# pass tcp, udp, and icmp out on the external (Internet) interface. 
# tcp connections will be modulated, udp/icmp will be tracked
# statefully.
pass out on $ext_if proto { tcp udp icmp } all modulate state

# allow ssh connections in on the external interface as long as they're
# NOT destined for the firewall (i.e., they're destined for a machine on
# the local network). log the initial packet so that we can later tell
# who is trying to connect. 
# Uncomment last part to use the tcp syn proxy to proxy the connection.
pass in log on $ext_if proto tcp to ! <firewall> \
   port ssh # synproxy state

[Předchozí: Tabulky] [Obsah] [Další: Network Address Translation]


[back] www@openbsd.org
$OpenBSD: filter.html,v 1.15 2012/11/08 08:49:02 ajacoutot Exp $