[OpenBSD]

[Wstecz: Tabele] [Spis treści] [Dalej: Translacje adresów (NAT)]

PF: Filtrowanie pakietów


Spis treści


Wstęp

Filtrowanie pakietów to selektywne przepuszczanie lub blokowanie pakietów przechodzących przez interfejs sieciowy. Kryteria, którymi kieruje się pf(4) podczas sprawdzania pakietów, są oparte o Warstwę 3 (IPv4 i IPv6) oraz Warstwę 4 nagłówków (TCP, UDP, ICMP, i ICMPv6). Najczęstrzym kryterium jest źródłowy i docelowy adres, źródłowy i docelowy port, oraz protokół.

Reguły filtrujące określają kryteria według których podejmowana jest konkretna akcja wobec pakietu: przepuść (ang. pass) lub blokuj (ang. block). Reguły filtrujące są porównywane sekwencyjnie, od pierwszej do ostatniej. Z wyjątkiem sytuacji, gdy pakiet pasuje do reguły ze słowem kluczowym quick, pakiety są porównywane z wszystkimi regułami filtrującymi zanim ostateczna akcja zostanie podjęta. Ostatnia pasująca reguła jest "zwycięzcą" i decyduje o podjętej akcji. Jeśli na początku zestawu reguł znajduje się bezwarunkowe pass all, wówczas wszystkie pakiety, które nie pasowały do żadnej z reguł, będą przepuszczane.

Składnia reguł

Ogólna, bardzo uproszczona składnia reguł filtrujących wygląda następująco:
action direction [log] [quick] on interface [af] [proto protocol] \
   from src_addr [port src_port] to dst_addr [port dst_port] \
   [tcp_flags] [state]
action
Akcja, która ma być podejmowana wobec pasujących pakietów, albo pass albo block. Akcja pass przepuszcza pakiety z powrotem do jądra systemu do dalszego przetwarzania, podczas gdy akcja block podejmuje działanie zgodne z ustawieniem opcji domyślnej polityki blokowania block-policy. Domyślna akcja może być jawnie zmieniona poprzez podanie słów kluczowych block drop lub block return.
direction
Kierunek, w którym dany pakiet zmierza na interfejsie sieciowym, albo in (pol. wchodzi) albo out (pol. wychodzi).
log
Określa, że dany pakiet powinien być zapisany w logach przez pflogd(8). Jeśli reguła zawiera opcję keep state, modulate state lub synproxy state, wówczas jedynie pakiet, który rozpoczyna połączenie stanowe jest odnotowywany w logach. Aby bezwzględnie zapisywać w logach wszystkie pakiety, należy zastosować opcję log-all.
quick
Jeśli pakiet pasuje do reguły ze słowem kluczowym quick, wówczas reguła ta jest uznawana za ostatnią pasującą i podejmowana jest odpowiednia akcja action.
interface
Nazwa interfejsu sieciowego, przez który pakiet przechodzi.
af
Rodzina adresów: inet dla IPv4 lub inet6 dla IPv6. PF jest zwykle w stanie zidentyfikować ten parametr na podstawie adresu(ów) źródłowych/przeznaczenia.
protocol
Protokół Warstwy 4 pakietu:
src_addr, dst_addr
Adres źródłowy/docelowy w nagłówku IP. Adres może być podany jako:
src_port, dst_port
Port źródłowy/docelowy w Warstwie 4 nagłówka pakietu. Porty mogą być podawane jako:
tcp_flags
Określa flagi, które muszą być ustawione w nagłówku TCP gdy używamy proto tcp. Są one podawane w postaci: flags check/mask. Na przykład: flags S/SA - PF bierze pod uwagę jedynie flagi S i A (SYN i ACK), a reguła jest dopasowana jeśli tylko flaga SYN jest ustawiona.
state
Określa, czy informacje o stanie połączenia są przechowywane, gdy pakiety są dopasowywane do tej reguły.

Domyślne blokowanie

Zaleca się przyjmować domyślną politykę blokowania wszystkiego, a jedynie selektywne przepuszczanie konkretnego ruchu. Takie podejście zalecane jest nie tylko ze względów bezpieczeństwa, ale sprawia, że pisanie zestawu reguł jest znacznie prostsze.

Aby wprowadzić domyślną politykę blokowania pierwsze dwie reguły powinny wyglądać tak:

block in  all
block out all

Spowoduje to blokowanie całego ruchu na wszystkich interfejsach w obu kierunkach.

Przepuszczanie ruchu

Ruch musi być precyzyjnie przepuszczany przez firewall albo będzie zatrzymany przez domyślną politykę blokowania. W tym miejscu do akcji wchodzą kryteria takie jak port źródłowy/docelowy, adres źródłowy/docelowy oraz protokół. Jeśli jakiś ruch ma być przepuszczany przez firewall, to reguły powinny być napisane tak restrykcyjnie jak to tylko możliwe. Ma to zapewnić przepuszczanie pożądanego i tylko pożądanego ruchu.

Kilka przykładów:

# Przepuść ruch na dc0 z sieci lokalnej, 192.168.0.0/24,
# zmierzający do 192.168.0.1. Przepuść także cały powracający
# do tej sieci ruch na 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


# Przepuść ruch TCP z zewnątrz na fxp0 zmierzający do serwera www.
# Nazwa interfejsu, fxp0, jest użyta jako adres docelowy, więc
# pakiety będą pasować do tej reguły jedynie jeśli są przeznaczone
# dla tej maszyny OpenBSD.
pass in on fxp0 proto tcp from any to fxp0 port www

Słowo kluczowe quick

Jak już zostało to wcześniej napisane, każdy pakiet jest porównywany z całym zestawem reguł od góry do dołu. Domyślnie, pakiet po porównaniu jest przekazywany do kolejnej reguły, i dlatego może być wielokrotnie zmieniany jego status, zanim osiągnie koniec zestawu reguł. "Ostatnia pasująca reguła wygrywa". Jest od tego wyjątek: opcja quick w regule filtrującej skutkuje przerwaniem dalszego porównania i powoduje podjęcie akcji z obecnej reguły. Oto kilka przykładów:

Źle:

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

W tym przypadku, linia block może być porównywana, ale nigdy nie będzie miała żadnego efektu, ponieważ po niej znajduje się linia, która wszystko przepuszcza.

Lepiej:

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

Te reguły są porównywane nieco inaczej. Jeśli linia block pasuje, dzięki opcji quick, pakiet będzie zablokowany, a reszta reguł tego zestawu będzie zignorowana.

Śledzenie stanów

Jedną z ważnych możliwości Packet Filter jest śledzenie stanów (ang. keeping state) lub kontrolowanie stanów (ang. stateful inspection). Kontrolowanie stanów odnosi się do zdolności PF do śledzenia stanu, lub przebiegu, połączenia sieciowego. Poprzez przechowywanie informacji o każdym połączeniu w tabeli stanów, PF jest w stanie szybko określić, czy pakiet przechodzący przez firewall należy do już nawiązanego połączenia. Jeśli tak jest, pakiet jest przepuszczany bez przechodzenia zestawu reguł.

Śledzenie stanów ma wiele zalet, włączając uproszczenie zestawu reguł i lepszą wydajność filtrowania pakietów. PF jest w stanie dopasowywać pakiety poruszające się w obu kierunkach danego połączenia, co oznacza, że reguły przepuszczające powracający ruch nie są potrzebne. A ponieważ pakiety pasujące do połączenia stanowego nie przechodzą zestawu reguł, czas zużywany na przetwarzanie tych pakietów może być bardzo znacząco zmniejszony.

Gdy reguła posiada opcje keep state, pierwszy pakiet pasujący do niej tworzy "połączenie stanowe" pomiędzy nadawcą i odbiorcą. Wówczas, nie tylko pakiety pochodzące od nadawcy, zmierzające do odbiorcy, ale i pakiety odbiorcy, skierowane do nadawcy pasują do reguły i nie są sprawdzane przez zestaw reguł filtrujących. Na przykład:

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

Zezwala to na wychodzenie ruchu TCP na interfejsie fxp0 i przepuszcza ruch powrotny pakietów stanowiących odpowiedź. Śledzenie stanów poza swoją funkcjonalnością, zapewnia także znaczący wzrost wydajności firewalla, ponieważ wyszukania stanów są dużo szybsze niż porównywanie pakietu z zestawem reguł filtrujących.

Opcja modulate state działa podobnie do keep state z tą różnicą, że odnosi się jedynie do pakietów TCP. Przy użyciu modulate state, Inicjujący Numer Sekwencyjny (ISN) wychodzącego połączenia jest losowy. Jest to przydatne do ochrony połączeń nawiązanych przez różne systemy operacyjne, które nie najlepiej radzą sobie z generowaniem numeru ISN.

Śledź stan wychodzących pakietów TCP, UDP i ICMP oraz generuj ISN dla TCP:

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

Inną zaletą śledzenia stanów jest to, iż odpowiedni ruch ICMP będzie przepuszczany przez firewall. Na przykład, jeśli keep state jest zdefiniowane dla połączenia TCP i nadejdzie komunikat ICMP "gaszący źródło" (ang. source-quench) odwołujący się do tego połączenia, będzie on dopasowany do odpowiedniego wpisu stanowego i przepuszczony przez firewall.

Warto zwrócić uwagę, że połączenia stanowe są ograniczone do interfejsu na którym są tworzone. Jest to istotne w przypadku ruterów i firewalli korzystającymi z PF, zwłaszcza przy polityce "domyślnego blokowania" zaimplementowanej tak, jak wcześniej zostało to opisane. Jeśli firewall realizuje śledzenie stanów dla wszystkich wychodzących połączeń na zewnętrznym urządzeniu, pakiety te wciąż musza być formalnie przepuszczone na wewnętrznym interfejsie.

Proszę zwrócić uwagę, że reguły nat, binat, i rdr bezwarunkowo tworzą stan dla pasujących połączeń tak długo, jak dane połączenie jest przekierowywane przez daną regułę.

Śledzenie stanów dla UDP

Każdy od czasu do czasu słyszy opinie, że "nie można utworzyć stanu z UDP, ponieważ UDP jest bezstanowym protokołem!". Mimo iż jest prawdą, że sesje komunikacyjne UDP nie mają żadnej formy stanu (formalnego rozpoczęcia i zakończenia komunikacji), nie ma to żadnego wpływu na zdolność PF do tworzenia stanowych sesji UDP. W tym przypadku protokołów bez pakietów "inicjujących" i "kończących" połączenie, PF po prostu śledzi ile czasu upłynęło odkąd pasujący pakiet został przepuszczony. Jeśli czas przekroczy ustaloną wartość, wpis stanu jest usuwany. Czas "przeterminowania" może być dostrojony w sekcji opcje pliku pf.conf.

Flagi TCP

Dopasowanie pakietów TCP na podstawie flag jest najczęściej wykorzystywane przy filtrowaniu pakietów, które otwierają nowe połączenie. Flagi TCP i ich nazwy są podane poniżej:

Aby PF sprawdzał flagi TCP podczas przetwarzania reguł filtrujących wykorzystywane jest słowo kluczowe flags. Jego składnia jest następująca:

flags check/mask

Część mask mówi PF aby sprawdzać jedynie podane flagi, a część check określa która flaga(i) powinny być "ustawione" w nagłówku, aby dopasowanie miało miejsce.

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

Powyższa reguła przepuszcza ruch TCP z ustawioną jedynie flagą SYN, biorąc pod uwagę tylko flagi SYN i ACK. Pakiet z flagami SYN i ECE będzie pasował do powyższej reguły, jednak pakiet z SYN i ACK lub tylko ACK już nie.

Uwaga: we wcześniejszych wersjach OpenBSD, następująca zapis był poprawny:

. . . flags S

Nie jest to już prawdziwe. Maska zawsze musi być podana.

Flagi często są stosowane w połączeniu z regułami keep state, aby wspomóc kontrolę tworzenia stanów:

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

Reguła ta zezwala na tworzenie stanu dla wychodzących pakietów TCP z ustawioną jedynie flagą SYN i przy braniu pod uwagę SYN oraz ACK.

Przy korzystaniu z flag trzeba być bardzo ostrożnym - należy rozumieć co się robi i dlaczego, no i należy uważać na rady innych, ponieważ często są one błędne. Niektórzy np. sugerują tworzenie stanu "jedynie gdy flaga SYN jest ustawiona, i żadna inna". Taka reguł wyglądała by tak:

     . . . flags S/FSRPAUEW  to zły pomysł!!

W teorii, tworzy się połączenie stanowe na początku sesji TCP, a sesja powinna rozpocząć się od flagi SYN, i żadnej innej. Problem polega na tym, że niektórzy użytkownicy rozpoczynają połączenia wraz z flagą ECN, i będą odrzuceni przez taką regułę. Dużo lepszą praktyką jest:

. . . flags S/SAFR

Jest to praktyczne i bezpieczne. Jeśli ruch podlega normalizacji scrub, wówczas można nie sprawdzać także flag FIN i RST. Normalizacja powoduje, że PF porzuca nadchodzące pakiety z nieprawidłową kombinacja flag (taką jak SYN i FIN lub SYN i RST). Zaleca się zawsze normalizować przychodzący ruch przy pomocy scrub:

scrub in on fxp0
.
.
.
pass in on fxp0 proto tcp from any to any port ssh flags S/SA \
   keep state

TCP SYN Proxy

Domyślnie gdy klient nawiązuje połączenie TCP z serwerem PF przepuszcza pakiety związane z "potrójnym uzgodnieniem" (ang. handshake) pomiędzy dwoma uczestniczącymi końcami gdy tylko nadejdą. PF ma jednak zdolność do pośredniczenia (ang. to proxy) w "potrójnym uzgodnieniu". Dzięki temu, to PF dokona poprawnego "potrójnego uzgodnienia" z klientem, następnie zainicjuje "potrójne uzgodnienie" z serwerem i dopiero wtedy zacznie przekazywać pakiety pomiędzy oboma węzłami. Najważniejszą korzyścią tego procesu jest to, iż żaden pakiet nie zostanie wysłany do serwera zanim klient nie dokona poprawnego "potrójnego uzgodnienia". Eliminuje to zagrożenie ataków typu "TCP SYN flood" na serwer danej usługi.

"TCP SYN proxy" jest uruchamiane przy pomocy słów kluczowych synproxy state w regułach filtrujących. Na przykład:

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

Tu, połączenia do serwera WWW będą nawiązywanie za pośrednictwem "TCP SYN proxy" przez PF.

Ze względu na swoje działanie, synproxy state zawiera funkcjonalności keep state i modulate state.

"SYN proxy" nie będzie działać jeśli PF jest uruchomiony na moście (ang. bridge(4)).

Blokowanie zfałszowanych pakietów

Fałszowaniem (ang. spoof) określa się zmianę adresu źródłowego pakietu mające na celu albo ukrycie prawdziwego pochodzenia datagramu, albo podszycie się pod inny segment sieci. Gdy użytkownik pomyślnie podszyje się pod inny adres, może rozpocząć sieciowy atak bez ujawniania jego prawdziwego źródła lub spróbować uzyskać kontrolę nad usługami, które są dostępne dla wąskiej grupy konkretnych adresów IP.

PF oferuje pewną ochronę przed podszywaniem się pod inne adresy poprzez słowo kluczowe antispoof:

antispoof [log] [quick] for interface [af]
log
Określa, że pasujące pakiety powinny być logowane przez pflogd(8).
quick
Jeśli pakiet pasuje do tej reguły, wówczas jest jest ona uznawana za regułę "zwycięską" i nie następuje dalsze porównywanie reguł z zestawu.
interface
Nazwa interfejsu sieciowego na którym ma być aktywowana ochrona przed spoofing-iem. Może to być także lista interfejsów sieciowych.
af
Rodzina adresów dla której ma być aktywowana ochrona przed spoofing-iem. inet dla IPv4 lub inet6 dla IPv6.

Przykład:

antispoof for fxp0 inet

Gdy zestaw reguł jest ładowany, każde wystąpienie antispoof jest rozszerzane do dwóch reguł filtrujących. Zakładając, że interfejs fxp0 ma adres IP 10.0.0.1 i maskę podsieci 255.255.255.0 (np, /24), powyższa reguła antispoof byłaby przekształcona w:

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

Reguły te realizują dwa zadania:

UWAGA: Reguły filtrujące, w które antispoof się przekształca będą blokować także pakiety wysyłane przez interfejs zwrotny (ang. loopback) na lokalny adres. Te adresy powinny być formalnie przepuszczane. Przykład:

pass in quick on lo0 all

antispoof for fxp0 inet

Korzystanie z antispoof powinno być ograniczone do interfejsów, które mają przypisany adres IP. Użycie antispoof na interfejsie bez adresu IP spowoduje powstanie następujących reguł filtrujących:

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

Przy takich regułach istnieje ryzyko blokowania całego nadchodzącego ruchu na wszystkich interfejsach.

Opcje IP

Domyślnie PF blokuje wszystkie pakiety IP z ustawionymi opcjami. Może to utrudnić rozpoznanie systemu operacyjnego przez takie narzędzia jak nmap. Jeśli zachodzi konieczność uruchomienia aplikacji, która wymaga przepuszczania tych pakietów, jak np. wysyłanych do grupy odbiorców (ang. multicast) lub IGMP, można skorzystać z dyrektywy allow-opts:
pass in quick on fxp0 all allow-opts

Przykład zestawu reguł filtrujących

Poniżej znajduje się przykład zestawu reguł filtrujących. Maszyna z PF pełni rolę firewalla oddzielającego małą sieć wewnętrzną od Internetu. Poniżej przedstawiona jest tylko konfiguracj odpowiadająca za filtrowanie, regułki kolejkowanie, nat, rdr itp zostały pominięte.

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

# normalizacja przychodzących pakietów
scrub in all

# ustawienie polityki domyślnego blokowania
block in  all
block out all

# przepuść ruch na interfejsie zwrotnym w obu kierunkach
pass quick on lo0 all

# aktywuj ochronę przed spoofing-iem dla interfejsu wewnętrznego.
antispoof quick for $int_if inet

# zezwalaj na połączenia ssh z sieci lokalnej jedynie z zaufanego
# hosta - 192.168.0.15. korzystaj z "block return", aby TCP RST
# od razu było wysyłane w odpowiedzi na blokowane połączenia
# korzystaj z "quick", aby reguła nie była nadpisana przez
# znajdujące się poniżej reguły "pass"
block return in quick on $int_if proto tcp from ! 192.168.0.15 \
   to $int_if port ssh flags S/SA

# przepuszczaj cały ruch z i do lokalnej sieci przeznaczony dla 
# maszyny będącej firewall-em
pass in  on $int_if from $lan_net to $int_if 
pass out on $int_if from $int_if to $lan_net

# wypuszczaj tcp, udp i icmp na interfejsie zewnętrznym (Internet). 
# śledź stan dla udp i icmp oraz moduluj stan dla tcp.
pass out on $ext_if proto tcp all modulate state flags S/SA
pass out on $ext_if proto { udp, icmp } all keep state

# zezwalaj na połączenia ssh na interfejsie zewnętrznym pod warunkiem,
# że NIE są one skierowane do firewall-a (np, nie są skierowane
# do maszyny z sieci lokalnej). twórz logi dla pakietów inicjujących
# połączenia, aby można było potem stwierdzić, kto próbował się
# połączyć. używamy "tcp syn proxy" dla nadchodzących połączeń.
pass in log on $ext_if proto tcp from any to { !$ext_if, !$int_if } \
   port ssh flags S/SA synproxy state

[Wstecz: Tabele] [Spis treści] [Dalej: Translacje adresów (NAT)]


[wstecz] www@openbsd.org
Originally [OpenBSD: filter.html,v 1.17 ]
$Translation: filter.html,v 1.6 2004/02/07 16:51:33 pl-team Exp $
$OpenBSD: filter.html,v 1.6 2004/02/08 12:16:38 jufi Exp $