[Wstecz: Tabele] [Spis treści] [Dalej: Translacje adresów (NAT)]
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.
action direction [log] [quick] on interface [af] [proto protocol] \
from src_addr [port src_port] to dst_addr [port dst_port] \
[tcp_flags] [state]
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.
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
Ź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 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łę.
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
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)).
PF oferuje pewną ochronę przed podszywaniem się pod inne adresy poprzez słowo kluczowe antispoof:
antispoof [log] [quick] for interface [af]
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.
pass in quick on fxp0 all allow-opts
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)]