58
OBRONA
HAKIN9 3/2010
Z
decydowanie nie będzie to kolejny tekst
o cudownych i niezawodnych metodach
zabezpieczania systemów przed atakami,
ponieważ uniwersalne sposoby nie istnieją,
a stereotypowe receptury nie zawsze zdają
egzamin. Co w takim razie ze skutecznością
ochrony prywatności? W dużej mierze zależy
ona od rozwagi administratora, świadomości
zagrożeń i umiejętności sprytnego wykorzystania
oferowanych przez systemy operacyjne
narzędzi. Nie zawsze kosztowne i przesadnie
rozbudowane systemy antywłamaniowe
gwarantują bezpieczeństwo. Jeżeli nie
potrafimy ich odpowiednio skonfigurować lub
nie znamy czyhających niebezpieczeństw,
nawet najdoskonalsze oprogramowanie nie
udaremni włamania. Warto zastanowić się
także, czy zawsze konieczny jest zakup drogiego
oprogramowania, gdyż w większości przypadków
darmowe narzędzia w zupełności zaspokajają
potrzeby administratorów i użytkowników dając
im poczucie bezpieczeństwa. Oprogramowanie
zaprezentowane w artykule jest darmowe i
dostępne w większości dystrybucji opartych na
BSD. Sprawia to, że bez ponoszenia zbędnych
kosztów każdy może samodzielnie uodpornić
system operacyjny i stworzyć własnego
firewalla – wystarczą chęci i wiedza. Nie
oznacza to jednak, że wykonane w ten sposób
zabezpieczenie będzie gorsze niż komercyjne.
Wręcz przeciwnie, poprawna konfiguracja
sprawia, że oprogramowanie przedstawione
ŁUKASZ CIESIELSKI
Z ARTYKUŁU
DOWIESZ SIĘ
jak chronić system przed
niepożądanym dostępem do
danych,
jakie darmowe
oprogramowanie skutecznie
uniemożliwia włamanie,
w jaki sposób filtrować pakiety
NAT,
co zrobić aby system *BSD stał
się małą twierdzą.
CO POWINIENEŚ
WIEDZIEĆ
znać podstawowe pojęcia z
zakresu sieci komputerowych,
posiadać minimalną wiedzę na
temat pakietów sieciowych,
znać podstawy dowolnego
systemu z rodziny BSD.
w dalszej części tekstu w niczym nie ustępuje
produktom oferowanym przez potentatów rynku
komputerowego.
Jednym z najpożyteczniejszych narzędzi,
którym będziemy zajmować się w artykule, jest
tzw. Packet Filter, nazywany skrótowo PF. W języku
polskim oznacza to po prostu Filtr Pakietów.
Choć brzmi to banalnie, manipulacje możliwe
przy jego wykorzystaniu są ogromne. Ponieważ
oprogramowanie to powstało pierwotnie
dla systemu BSD, a dokładniej OpenBSD
(następnie przeportowano go także na FreeBSD
i NetBSD), właśnie na jego przykładzie będzie
przeprowadzona konfiguracja (choć fragmenty
mogą ukazywać wersję dla FreeBSD, która
czasem nieco się różni). Rzecz najważniejsza
– Filtra Pakietów nie trzeba instalować! Czyż nie
jest to wspaniałe, kiedy system operacyjny sam
oferuje nam firewall? PF znajduje się domyślnie w
systemie od wersji OpenBSD 3.0 i co ważniejsze
jest częścią jądra. Przed użyciem należy go
jednak aktywować. W tym celu wystarczy
odpowiednio zmodyfikować plik /etc/rc.conf.local
poprzez dodanie do niego polecenia pf=YES (w
przypadku FreeBSD będzie to pf_enable=”YES” ).
Spowoduje to, że program zostanie uruchomiony
wraz z systemem operacyjnym. Alternatywnym
sposobem jest manualne załadowanie
odpowiedniego modułu za pomocą polecenia
kldload pf.ko. Niezwykle wygodnym narzędziem
wspomagającym zarządzanie tym pakietem jest
pfctl. Dzięki niemu w prosty sposób możemy w
Stopień trudności
Rozsądek to
bezpieczeństwo
Czy na Twoich barkach spoczywa odpowiedzialność za
bezpieczeństwo danych firmy? A może jesteś administratorem
sieci? Jeżeli nie, to z całą pewnością jesteś użytkownikiem,
ceniącym sobie prywatność i starającym się chronić własny
system przed włamaniem. Niektórzy twierdzą, że posiadają dobry
program antywirusowy. Niestety to za mało – sprawdź sam.
59
ROZSĄDEK TO BEZPIECZEŃSTWO
HAKIN9
3/2010
pełni zarządzać Filtrem Pakietów. Zanim
jednak przejdziemy do obsługi PF, wróćmy
do konfiguracji, gdyż to właśnie ona jest
najistotniejsza i decyduje o skuteczności
zabezpieczeń.
PF ma charakter zapory ogniowej,
a co za tym idzie większość ustawień
wprowadzana jest właśnie pod kątem
firewalla. Na początek warto wiedzieć czym
jest ściana ogniowa, jak działa i jakie funkcje
spełnia, aby uzmysłowić sobie skąd może
pochodzić zagrożenie. Jej prymarnym
zadaniem jest oczywiście ochrona sieci
przed potencjalnymi atakami. Skąd mogą
one pochodzić? Przede wszystkim z
innej sieci. W tym celu jest śledzony ruch
pakietów pomiędzy siecią potencjalnej
ofiary i agresora. Badania prowadzone nad
tymi pakietami – ich typami, zawartością itp.
– umożliwiają wprowadzenie stosownych
odpowiednich ograniczeń. W ten sposób
przyjmowane są jedynie pożądane pakiety,
natomiast reszta jest automatycznie
odrzucana. Dzięki takim zabiegom
można w dużej mierze ograniczyć ryzyko
włamania. PF wykorzystuje tzw. zestawy
reguł, które są w rzeczywistości zestawami
poleceń wykonywanych w odniesieniu
do poszczególnych pakietów. Działanie
Filtra Pakietów jest dosyć specyficzne,
ponieważ – co warto zauważyć – nie
przerywa on działania po napotkaniu
odpowiedniej reguły. Jeśli nie jest ona
ostatnią, następuje dalsze sprawdzanie.
Kiedy nie ma już kolejnej reguły, następuje
wykonanie ostatniej zapamiętanej.
Doskonałym przykładem jest tu podanie
dwóch reguł: pass in all oraz block in all.
Pierwszej umożliwia nieograniczony ruch
sieciowy, natomiast druga wręcz przeciwnie,
całkowicie go blokuje. Co zrobi PF
napotkawszy takie polecenia? Oczywiście
na początku sprawdzi je kolejno docierając
do ostatniej linii, która nakazuje wszystko
zablokować. Pomimo że pierwsze polecenie
umożliwia ruch sieciowy, jest ono w pewnym
sensie ignorowane przez program Packet
Filter. Dzieje się tak dlatego, że w przykładzie
nie ma podanych szczegółowych
warunków do spełnienia przez pakiety, więc
teoretycznie obydwa spełniają oczekiwania
programu. Tworząc skomplikowane
schematy działania tego filtra rozwiązanie
takie okazuje się logiczne i sensowne. Tego
typu schemat działania przypomina nieco
sięganie po kolejne pasujące elementy, aż
do momentu, kiedy następny okazuje się
niepoprawny.
Domyślnym plikiem konfiguracyjnym
jest /etc/pf.conf (wskazujemy go także
podczas konfiguracji /etc/rc.conf poprzez
dodanie wpisu pf_rules="/etc/pf.conf" ). To w
nim znajdują się instrukcje dla programu
Packet Filter. Z założenia program ten
miał odbiegać od sztywnych norm
ograniczających użytkownika. Dowodem
tego jest aż siedem możliwych rodzajów,
czy też form wprowadzanych instrukcji. Daje
to możliwość uczynienia reguł prostymi i
zarazem przejrzystymi.
Na początku warto przyjrzeć się tzw.
makrom. Czym są makra? Najprościej
można je określić jako zbiór zmiennych
zdefiniowanych przez użytkownika. Zmienne
takie wykorzystuje się zazwyczaj do
przechowywania adresów IP i numerów
portów. Niekiedy za pomocą zmiennych
określa się także interfejsy sieciowe. Aby
stworzyć makro, wystarczy zdefiniować
jego nazwę i przypisać do niej określoną
wartość, np. host = „111.111.111.111”. W nazwie
makra wolno używać zarówno liter, cyfr oraz
podkreślenia. Jedynym zastrzeżeniem jest
fakt, że nazwy nie mogą stanowić słowa
zarezerwowane, takie jak out czy pass. Aby
wykorzystać makro należy jego nazwę
poprzedzić znakiem $ (dolar), np. $host.
Dzięki takim udogodnieniom praca z Filtrem
Pakietów staje się znacznie przyjemniejsza.
Warto także dodać, że z makrami
doskonałą symbiozę tworzą tzw. listy.
Dzięki nim można pogrupować podobne
parametry i stworzyć z nich jedną regułę.
Kiedy najczęściej są wykorzystywane listy?
Otóż załóżmy, że musimy zablokować
kilka adresów IP. Oczywiście można
zdefiniować je pojedynczo i określić kryteria
dla każdego z osobna. Sposób ten jest
jednak mniej czytelny i znacznie bardziej
uciążliwy, kiedy takich adresów jest duża
ilość. Prościej jest stworzyć z nich listę.
Poszczególne elementy definiowane są
przy użyciu nawiasów klamrowych. I choć
można użyć takiej listy bezpośrednio w
głównej części naszej reguły, najczęściej
przypisuje się je do zmiennych tworząc
makra. Zastanawiający może być fakt, w jaki
sposób program – choćby pfctl – widzi taką
listę. Otóż dla niego oznacza to konieczność
stworzenia wielu reguł, tak jak byśmy dla
każdego elementu listy pisali osobną regułę.
Dokładnie to musi zrobić program, kiedy
podczas analizy pliku pf.conf natknie się na
listę. Co istotne, dana reguła nie musi się
ograniczać do jednej listy, ponieważ może
zawierać definicję kilku, np. jedną zwierającą
adresy IP i drugą opisującą określone
porty. Należy zwrócić uwagę na dwa istotne
detale. Po pierwsze przecinki oddzielające
kolejne elementy nie są konieczne, jednakże
zazwyczaj są wstawiane dla zachowania
przejrzystości. Kolejny aspekt, to sposób
deklarowania i tworzenia listy. Jeśli
chcielibyśmy określić kilka adresów, które
mają być akceptowane, lista taka mogłaby
mieć postać, np. poprawne=”{xxx.xxx.xxx.xxx,
yyy.yyy.yyy.yyy}”. Niekiedy przydatną funkcją
oferowaną przez PF jest możliwość
zagnieżdżania kolejnych list. Deklaracja
odbywa się z użyciem wspomnianego już
znaku $ (dolar). Operacją, której należy się
wystrzegać, jest wykluczanie pojedynczych
elementów. Jeśli zachodzi taka konieczność
trzeba zachować ostrożność i dokładnie
przeanalizować składnię. Zdarza się, że
wprowadzone polecenia wykluczają się
wzajemnie, co w efekcie rozbija całkowicie
zaporę ogniową. Połączenie możliwości
jakie oferują zarówno makra, jak i listy
sprawia, że zaprogramowanie filtra staje
się czynnością mniej skomplikowaną.
Przykład praktycznego wykorzystania
tych metod pokazuje Listing 1. Jak łatwo
zauważyć, znacznie upraszcza to nie tylko
dalsze korzystanie, ale także postać pliku
konfiguracyjnego.
W przypadku dużej ilości adresów
Ipv4 i Ipv6, lepszym rozwiązaniem są
tzw. tabele. Zaprojektowano je właśnie
w celu przechowywania adresów i
umożliwiania szybkiego dostępu do nich.
Największą zaletą takiego rozwiązania jest
stosunkowo niskie obciążenie procesora
oraz niewielkie zużycie pamięci. Tabele
świetnie nadają się do przechowywania
rozmaitych reguł przekierowujących,
filtrujących i NAT. Powszechnie wykorzystane
tabele zawdzięczają przede wszystkim
niezwykłej wydajności. Aby stworzyć tabele,
należy posłużyć się programem pfctl lub
korzystając z dyrektywy table zrobić to
bezpośrednio w pliku pf.conf. Zanim jednak
do tego przejdziemy, należy wspomnieć o
dwóch towarzyszących tabelom atrybutach,
a mianowicie const i persist. Pierwszy
OBRONA
60
HAKIN9
3/2010
ROZSĄDEK TO BEZPIECZEŃSTWO
61
HAKIN9
3/2010
z nich sprawia, że zawartość danej
tabeli nie może zostać zmieniona. Jest
to skuteczna metoda w przypadku, gdy
zależy nam na zablokowaniu możliwości
manipulacji adresami. Drugi parametr jest
swego rodzaju zabezpieczeniem tabeli
przed działalnością systemowego jądra.
Oznacza to, że nawet w przypadku kiedy nie
odwołują się do niej reguły, nie może zostać
usunięta. Domyślnie zadaniem jądra byłoby
automatyczne zlikwidowanie bezużytecznej
tabeli. Korzystanie z tabel umożliwia szybką
manipulację regułami. Prosty przykład
ich wykorzystania przedstawia Listing
2. Zostały w nim stworzone dwie tabele,
zawierające pożądane i niepożądane
adresy IP. Następnie zostały zadeklarowane
dwie reguły. Pierwsza z nich zakazuje
dostępu niechcianym adresom, natomiast
druga umożliwia ruch tym, które zostały
wymienione jako niestwarzające zagrożenia
dla naszego systemu. W przykładzie zostały
dla uproszczenia określone jedynie reguły
odpowiadające za ruch przychodzący.
Można oczywiście dodatkowo przypisać
reguły dla ruchu w drugą stronę, czyli
wychodzącego (ang. out). Tabele są
doskonałym rozwiązaniem, jeżeli zależy
nam na wykluczeniu określonego adresu.
O ile w odniesieniu do list, negacje są
raczej odradzane – ponieważ są często
źródłem niedopatrzeń, a co za tym
idzie błędów i luk w zabezpieczeniach
– w przypadku tabel ryzyko pomyłki jest
znacznie mniejsze. Aby skorzystać ze
wspomnianej negacji, wystarczy poprzedzić
dany adres znakiem wykrzyknika. Może
zdarzyć się sytuacja, że blokując określoną
sieć (xxx.xxx.xxx.xxx/yy), będziemy chcieli
umożliwić połączenie jednemu, wybranemu
adresowi (zzz.zzz.zzz.zzz). W takim wypadku
wystarczy stworzyć tabelę np. table
<blokowane_adresy> {xxx.xxx.xxx.xxx/yy,
!zzz.zzz.zzz.zzz}. Widzimy zatem, że tabele
są znacznie oszczędniejszą i wygodniejszą
formą przekazywania danych do reguł. Nie
oznacza to jednak końca udogodnień ze
programu. Niekiedy z rozmaitych względów
praktyczniejszym, a przede wszystkim
wygodniejszym sposobem udostępniania
adresów IP jest wykorzystanie plików.
Implementacja jest prosta i ogranicza się
do zasady mówiącej, że każdy adres musi
znajdować się w nowej linii. Załóżmy, że
posiadamy taki plik, np. /etc/przyjazne_
adresy_IP. Zapis umożliwiający wczytanie
jego zawartości może mieć postać: table
<przyjazne_adresy_IP> persist file „/etc/
przyjazne_adresy_IP”. Trudno określić, która z
metod jest najlepsza, ponieważ każda z nich
ma zarówno wady, jak i zalety. Warto jednak
wiedzieć jakie mamy możliwości, ponieważ
dzięki temu zawsze będziemy mieli wybór, a
nasze działanie nie będzie wymuszone. W
odniesieniu do zabezpieczeń elastyczność
jest niezwykle istotnym przywilejem.
Prostym sposobem na wprowadzanie
wszelakich zmian jest wykorzystanie
wspomnianego już programu pfctl.
Udostępnia on opcje umożliwiające
tworzenie i zarządzanie grupami adresów.
Aby stworzyć nową grupę, np. niechciane_
adresy_IP, wystarczy w konsoli wpisać
polecenie pfctl -t niechciane_adresy_IP -
Tadd xxx.xxx.xxx.xxx, a następnie zatwierdzić
klawiszem Enter. W tym momencie
program sprawdzi, czy tabela o podanej
nazwie istnieje. Jeśli tak, to dopisze do niej
podany adres. Gdyby okazało się, że nie
ma takiej tabeli zostanie ona utworzona
i uzupełniona odpowiednim wpisem. Dla
pewności warto każdorazowo sprawdzić,
czy operacja przebiegła zgodnie z
planem. Służy do tego parametr -Tshow
(pfctl -t nazwa_tabeli -Tshow). Niekiedy
zdarza się, że wprowadzony adres IP
jest nieprawidłowy i trzeba go usunąć.
Podobnie jak w powyższych przykładach,
wystarczy wywołać program pfctl, wskazać
na konkretną tabelę, w której znajduje się
niechciany wpis i uzupełnić polecenie
o parametr -Tdelete i numer IP. Program
posiada znacznie więcej przydatnych
funkcji, ale do nich wrócimy w dalszej części
artykułu.
Zapewne zastanawiacie się w jaki
– najprostszy – sposób można wykorzystać
możliwości Filtra Pakietów. Przykład
podstawowego użycia tego programu
przedstawia Listing 3. Znajdują się tam dwa
pozornie wykluczające się polecenia. Jedno
nakazuje zablokowanie wszystkich pakietów
przychodzących, a kolejne każe przepuścić
pakiety od określonych grup. Nonsens?
Niezupełnie, ponieważ wspomnieliśmy już,
że Packet Filter sprawdza polecenia, dopóki
nie dotrze do ostatniego. Ma to ogromny
plus, ponieważ umożliwia tworzenie
takich konstrukcji jak zaprezentowana w
przykładzie. Reguła odrzucająca wszystkie
pakiety przychodzące daje nam pewność,
że żaden z nich się nie przedostanie. Ale
sytuacja taka kompletnie sparaliżowałaby
normalny ruch sieciowy. Należy zatem
określić te adresy, którym ufamy. Tak
też postępuje PF, kiedy odnajdzie drugą
regułę. Jest to banalnie prosty przykład,
jednak niezwykle skuteczny. Aby był on
całkowicie zrozumiały przeanalizujmy w
skrócie poszczególne elementy. Słowa
block i pass nakazują, aby PF zablokował
i przepuścił pakiety. Na drugim miejscu
znajduje się słowo in oznaczające, że reguła
dotyczy ruchu przychodzącego. Oczywiście
program wymaga również określenia
interfejsu (tu: sis0). Na koniec należy określić
skąd dokąd, a właściwie od kogo do kogo
ma zostać umożliwiony ruch, a na kogo
zostanie nałożona blokada.
Znając takie zagadnienia jak listy, makra
i tabele nadszedł czas na prezentacje
konkretnych zastosowań. Na początku
zajmiemy się składnią obowiązującą
tworzone reguły, a następnie przejdziemy
do przykładów. Wspomnieliśmy już na
czym polega filtrowanie pakietów. Jest
to w pewnym sensie zarządzanie losem
każdego pakietu i określanie, który
ma zostać przyjęty, a który odrzucony.
Aby jednak zapora funkcjonowała
tak jak powinna, czyli zabezpieczała
daną maszynę, a nie utrudniała pracę
użytkownikom konieczna jest skrupulatna
konfiguracja. W przypadku większych
sieci nie można jej zrobić w krótkim
czasie, ponieważ każda reguła musi być
dobrze przemyślana. Wyeliminuje to w
przyszłości żmudne poszukiwanie miejsca
zawierającego błąd. Prześledźmy zatem
jakimi elementami dysponuje każda
reguła. Oczywiście nie musimy korzystać z
wszystkich, a jedynie z tych, których realnie
potrzebujemy. Każdą regułę zaczynamy od
określenia, czy będzie ona miała na celu
blokowanie określonych pakietów (słowo
kluczowe block), czy ich przepuszczanie
(słowo kluczowe pass). W tym momencie
warto wspomnieć o parametrze quick.
Jeśli dana reguła zawiera taką deklarację,
a sprawdzany pakiet spełnia jej założenia,
to automatycznie program przyjmuje, że
jest to ostatnia pasująca reguła i wykonuje
określone działania. Dzięki tej metodzie
można określić pewne wyjątki od naszych
reguł.
OBRONA
60
HAKIN9
3/2010
ROZSĄDEK TO BEZPIECZEŃSTWO
61
HAKIN9
3/2010
Kiedy wiadomo już jaki rodzaj
akcji Packet Filter będzie podejmował,
należy wskazać kierunek połączeń.
Jak łatwo się domyśleć będzie to ruch
przychodzący (słowo in) lub wychodzący
(słowo out). Najczęściej kontroluje się
ruch przychodzący, ponieważ zazwyczaj
to właśnie z zewnątrz pochodzą
zmodyfikowane pakiety. Aby jednak PF
wiedział gdzie powinien w ogóle szukać
ruchu sieciowego, koniecznie należy
wskazać odpowiedni interfejs. Nie będziemy
wymieniać tu wszystkich możliwości,
ponieważ mijałoby się to z celem. Może
to być, np. dc0, sis0 w zależności od
posiadanego sprzętu. Najlepiej sprawdzić
to za pomocą polecenia ifconfig. Deklaracja
urządzenia odbywa się przez wpisanie
słowa kluczowego on, a następnie podanie
nazwy interfejsu. Ponieważ funkcjonowanie
Filtra Pakietów jest związane z tzw. Warstwą
3 i 4, istnieje możliwość zdefiniowania
protokołu. Może to być chociażby tcp, icmp,
czy udp. Lista jest bardzo długa, jednak
w prosty sposób możemy ją prześledzić.
Wystarczy wpisać w konsoli polecenie
cat /etc/protocols. Jeżeli zależy nam na
podaniu konkretnego protokołu, koniecznie
należy poprzedzić jego nazwę słowem
proto. Najważniejszą częścią każdej reguły
jest jednak miejsce, w którym definiuje się
adresy docelowe oraz źródłowe. I to właśnie
na nich należy skupić uwagę, ponieważ
drobna pomyłka może zepsuć całą regułę.
Na początku podajemy tzw. adresy
źródłowe poprzedzając je znacznikiem
from. Program oferuje tu bogatą gamę
możliwości. W zależności od potrzeby
wpisujemy tu pojedynczy adres IP,
przypisujemy stworzoną wcześniej grupę,
zestaw w postaci listy, tabeli lub nawet
blok sieci (CIDR, np. xxx.xxx.xxx.xxx/yy).
Dla określenia wszystkich adresów
– np. aby wszystkie były wpuszczone
– stosuje się słowo kluczowe any. Niekiedy
można spotkać także formę all, będącą
odpowiednikiem formy from any to any. Po
określeniu źródła należy także wskazać
miejsce przeznaczenia. Deklaracja
taka odbywa się poprzez parametr to i
podanie adresu lub określonej grupy. Tak
jak w powyższym przypadku, określenie
adresu docelowego może mieć postać
pojedynczego adresu, tabeli itd. Obsługuje
także opcję any, czyli przeznaczeniem
będzie każdy adres z pewnego zakresu.
W tym momencie można także przypisać
negacje, jednak jak już omówiliśmy
to wcześniej, zdecydowanie lepszym
rozwiązaniem dla tego typu działań są
tabele.
Podobnie jak w przypadku adresów,
możemy określić porty źródłowe i docelowe.
Także w tym przypadku istnieje kilka
sposobów na deklarację. Pierwszy to po
prostu podanie odpowiedniego numeru
portu (zakres od 1 do 65535). Niestety
czasem zapis taki może przysporzyć nieco
kłopotów z odróżnieniem poszczególnych
usług po ich numerycznym opisie.
Znacznie wygodniejsze – zwłaszcza
dla mniej doświadczonych użytkowników
– jest korzystanie z możliwości zapisu
nazwy usługi. Kompletna lista znajduje
się w pliku /etc/services (np. ftp, www, link,
kazaa itd.). Kiedy zachodzi jednak potrzeba
wyodrębnienia wielu portów, warto stworzyć
ich listę i to właśnie ją przypisać. Załóżmy
jednak, że zależy nam na przedziale
pomiędzy portem o numerze sto, a portem
o numerze tysiąc. Wypisywanie każdego
po kolei byłoby kompletnym nonsensem.
W takiej sytuacji korzystamy oczywiście
z operatorów określających przedziały.
Do dyspozycji mamy chociażby znaki
mniejszości i większości, ale także mniejszy
lub równy (<=) oraz większy lub równy (>=). To
jednak nie wszystko! Packet Filter rozpoznaje
także takie operatory jak: różny (!=), przedział
(><) i wartości poza danym przedziałem
(<>). Dzięki temu w stosunkowo prosty i
precyzyjny zarazem sposób możemy
określić konkretną grupę interesujących nas
portów.
PF oferuje również określenie tzw.
stanów i flag, jednak w aktualnych wersjach
zostały zdefiniowane jako domyślne
najlepsze rozwiązania. W odniesieniu
do stanów jest to opcja keep state, czyli
polecenie przechowywania informacji o
połączeniu w momencie, kiedy okazuje
się, że dany pakiet odpowiada regułom.
Aktualnie jest ona dołączana domyślnie,
a informacje zapisywane są w tzw.
tabeli stanów. Ma to znaczący wpływ na
dokładność filtrowania oraz wydajność
filtra. Tabela ta pozwala na błyskawiczne
sprawdzenie, czy pakiet jest elementem
nawiązanego wcześniej połączenia.
Kiedy okaże się, że tak jest zostaje on
przepuszczony bez sprawdzania zgodności
z regułami. Oczywiście zawartość
tabeli można kontrolować, np. określać
maksymalną liczbę stanów (max liczba_
stanów) a także śledzić liczbę utworzonych
stanów dla danego IP (source-track). Jeśli
uznamy, że określone wpisy są zbędne,
można za pomocą parametru no state
zapobiec ich utworzeniu.
Flagi – a konkretnie ACK (potwierdzenie)
i SYN (zapytanie o możliwość rozpoczęcia
nowej sesji) – związane są z protokołem
TCP (proto tcp). Wykorzystuje się je
najczęściej właśnie podczas rozpoczynania
nowej sesji (SYN). Zapis ustalający w regule
odpowiednie cechy sprawdzanych flag
może mieć postać np. flags S/SA. Na
początku znajduje się słowo kluczowe, a
następnie określenie rodzaju flagi, która
powinna znajdować się w nagłówku,
aby dalsze sprawdzanie w ogóle miało
miejsce (tu: S, czyli SYN). Jeżeli założenia
są spełnione, ostatnia część zapisu
wskazuje, jakie flagi mają być sprawdzane
w dalszej kolejności (tu: SA, czyli SYN i
ACK). Począwszy od OpenBSD w wersji
4.1 flagi są automatycznie dołączane do
Listing 1.
Przykład użycia makr i list
interfejs
= „
sis0
"
host1
= „
111.111.111.111
"
host2
= „
222.222.222.222
"
host3
= „
333.333.333.333
"
wszystkie_hosty
= „
{
" $host1 $host2 $host3 „}"
block
in
on
$
interfejs
from
$
wszystkie_hosty
to
any
Listing 2.
Przykład wykorzystania tabel przez Packet Filter
table
<
niechciane_adresy_IP
>
{
xxx
.
xxx
.
xxx
.
xxx
,
yyy
.
yyy
.
yyy
.
yyy
,
zzz
.
zzz
.
zzz
.
zzz
}
table
<
przyjazne_adresy_IP
>
{
aaa
.
aaa
.
aaa
.
aaa
,
bbb
.
bbb
.
bbb
.
bbb
}
block
in
on
sis0
from
<
niechciane_adresy_IP
>
to
any
pass
in
on
sis0
from
<
przyjazne_adresy_IP
>
to
any
OBRONA
62
HAKIN9
3/2010
ROZSĄDEK TO BEZPIECZEŃSTWO
63
HAKIN9
3/2010
każdej reguły TCP. Zazwyczaj zaleca się,
aby to Packet Filter wybrał i zastosował
najkorzystniejszą kombinację flag. Trudno
sprecyzować jaka będzie najbardziej
uniwersalna i najkorzystniejsza. Raczej
odradza się wskazywania w jednej regule
wszystkich flag. Znacznie korzystniejszym
rozwiązaniem jest z całą pewnością
sprawdzenie SYN, ACK, RST i ewentualnie
FIN. Reguła ta będzie znacznie
bezpieczniejsza i skuteczniejsza.
Obecnie najczęściej blokuje się
całkowicie ruch sieciowy ustawiając reguły
block in all oraz block out all. Następnie
wprowadza się kolejno poszczególne
wyjątki. Jest to jedna z najbezpieczniejszych
technik, ponieważ daje możliwość
prawdopodobnie największej kontroli nad
tym, które pakiety mają zostać odrzucone,
a które przyjęte. W tym celu należy
dokładnie przemyśleć, które porty, protokoły
i adresy są pożądane. Pamiętajmy, że
skrupulatność i rozsądek są tu kluczowymi
atutami, gdyż budujemy zaporę ogniową.
Jej szczelność decyduje o bezpieczeństwie,
a przecież na tym najbardziej nam zależy.
Ponadto przydatnym może okazać się
wspomniany już parametr quick. Kiedy
najlepiej jej użyć? Otóż załóżmy, że w
jednej linii przepuszczamy wszystkie
pakiety spełniające pewne założenia
reguły, natomiast w kolejnej linii blokujemy
cały ruch. Jaki będzie efekt? Nietrudno
przewidzieć, że zakończy się blokadą
sieci. Jest to częsty błąd popełniany przez
pośpiech. Można takiej sytuacji uniknąć
korzystając z argumentu quick. Kiedy pakiet
spełni oczekiwania danej reguły, dalsze
sprawdzanie się nie odbędzie.
Dobrą praktyką jest tworzenie
szablonów, czy też schematów konfiguracji
PF tak, aby były one elastyczne i w
pewnym sensie uniwersalne. Oczywiście
trudno stworzyć coś takiego w krótkim
czasie, ponieważ niezbędna okaże się
szczegółowa wiedza na temat ruchu
sieciowego w miejscu, gdzie ma zostać
ustawiona zapora ogniowa. Doskonałym
początkiem jest jednak wspomniane
zatrzymanie zarówno ruchu wychodzącego
jak i przychodzącego. Daje to bazę
dla dalszych prac. W przypadku mniej
skomplikowanego firewalla, można w
tym momencie przepuścić pakiety na
określonych portach, aby np. umożliwić
przeglądanie stron internetowych (Listing
4). Wystarczy odblokować protokoły tcp
i udp oraz odpowiednie porty, z których
korzystają. Tu zostały podane domyślne
wartości, a mianowicie port o numerze 53
związany z tcp i udp. Dodatkowo niezbędne
będzie także umożliwienie ruchu na porcie
80, który jest przypisany do protokołu www.
Mówiąc o kontrolowaniu pakietów,
trudno nie wspomnieć o tzw. spoofingu.
Ujmując zagadnienie najogólniej
można określić to jako fałszowanie
pakietów, polegające na zmianie ich
adresów źródłowych. Dzięki tej technice
zostaje ukryta prawdziwa tożsamość i
pochodzenie pakietów. Atakujący jest w
stanie przejąć kontrolę nad usługami
przeznaczonymi jedynie dla użytku ściśle
określonych numerów IP i doskonale
maskować miejsce, z którego dokonano
włamania. Tworząc Filter Pakietów
pomyślano również o takiej ewentualności.
Rozwiązanie oferowane przez program
nie jest doskonałe, ale byłoby nadużyciem
określenie go jako nieskuteczne. Aby
ustawić ochronę przeciwko sfałszowanym
pakietom, należy użyć słowa kluczowego
antispoof. Polecenie to posiada niewiele
dodatkowych parametrów, dzięki czemu
jego ustawienie jest niezwykle proste. Jeżeli
zależy nam na zakończeniu porównywania z
kolejnymi regułami, gdy dany pakiet będzie
spełniał założenia antispoof, koniecznie
należy dodać opcję quick. Następnie
określamy dla jakich interfejsów ma
zostać włączona ochrona zapobiegająca
spoofingowi. Na końcu określamy jeszcze
rodzinę adresów: inet lub inet6. Polecenie
może mieć postać, np. antispoof for sis0
inet. Sposób działanie tego polecenia
jest pozornie skomplikowany, jednak
w rzeczywistości składa się z dwóch
logicznych reguł. Pierwsza sprawdza, czy
dany pakiet przechodzi przez dany interfejs.
Jeśli nie oznacza to, że pochodzi z innego
interfejsu sieciowego i natychmiast jest
blokowany. Druga bada, czy przypadkiem
host nie wysłał pakietu samemu sobie.
Sytuacja taka raczej nie powinna mieć
miejsca, więc PF uznaje ją automatycznie
za podejrzaną. Stosowanie antispoof ma
miejsce zazwyczaj w przypadkach, kiedy
interfejs posiada adres IP. W przeciwnym
razie może nastąpić zablokowanie
ruchu przychodzącego. Ochrona przed
sfałszowanymi pakietami jest jednocześnie
kolejnym etapem budowy prostego firewalla.
Właściwie, jeśli nie będzie wykorzystywany
dodatkowy serwer, taka ochrona w wielu
przypadkach okaże się wystarczająca. Być
może ktoś zarzuci takiemu rozwiązaniu
zbytnie uproszczenie. Pamiętajmy jednak,
że nie jest to kompletna zapora – choć
jest niezwykle skuteczna w warunkach
domowych – a jedynie przykład.
Często można spotkać się z
zabezpieczaniem ruchu sieciowego
poprzez tworzenie tzw. mostów pomiędzy
kilkoma segmentami sieci. Mostem
najczęściej jest komputer użyty w
charakterze pośrednika. To właśnie
przez niego zostaje przepuszczony ruch
sieciowy. Co daje takie rozwiązanie?
Jednym z ogromnych plusów jest fakt, że
pakiety mogą być wielokrotnie filtrowane.
Załóżmy, że mamy do czynienia z sieciami
podpiętymi do interfejsów xl0 i xl1. W
takim przypadku każdy pakiet zostanie
poddany sprawdzeniu dwukrotnie, podczas
wejścia i wyjścia z mostu. W takim
wypadku odmiennie niż w poprzednich
przykładach warto rozpocząć zestaw reguł
od umożliwienia ruchu w obu kierunkach
(pass in oraz pass out) dla każdego
interfejsu korzystającego z mostu. Nie
jest to jednak regułą, ponieważ jeżeli
któryś interfejs jest mniej wiarygodny
niż inny, można oczywiście zablokować
na nim ruch sieciowy. Tak naprawdę
jedynym ograniczeniem Filtra Pakietów
jest wyobraźnia administratora. Im więcej
możliwych nadużyć potrafimy wyodrębnić,
tym skuteczniej zabezpieczymy sieć.
Pozostałe reguły tworzymy korzystając
z zaprezentowanych wyżej poleceń
składowych.
Aby uprościć proces tworzenia
szkieletu pliku konfiguracyjnego można
podzielić go na mniejsze partie. Pozwala
to na wyrobienie określonych nawyków,
dzięki którym zwiększa się szansa na
wyeliminowanie ewentualnych błędów.
No dobrze, tylko jak podzielić taki plik?
Konfigurując Packet Filter któryś raz z
rzędu, nietrudno zauważyć, że pewne
elementy – a konkretnie ich umiejscowienie
– się powtarzają. W ten oto sposób
łatwo zauważyć, że na początku zawsze
deklarujemy listy, makra i zmienne. W sekcji
tej wskazujemy chociażby poszczególne
OBRONA
62
HAKIN9
3/2010
ROZSĄDEK TO BEZPIECZEŃSTWO
63
HAKIN9
3/2010
interfejsy. Następnie najczęściej znajdują
się zadeklarowane tabele, oczywiście
zakładając, że z nich korzystamy. Po
wstępnych przygotowaniach następują
właściwe reguły i ich opcje. Niekiedy
stosuje się wcześniej normalizację, lecz
ponieważ nie jest ona zalecana we
wszystkich przypadkach, nie będziemy
się nią zajmować w tym momencie. Nie
można także zapomnieć o translacji
adresów (czyli NAT), którą deklarujemy
przed regułami. W przypadku programu
PF kolejność ma znaczenie, ponieważ z
pliku konfiguracyjnego odczytywana jest
każda kolejna linia, zaczynając od pierwszej.
Niekiedy można natknąć się na opinię,
że plik konfiguracyjny dzieli się na siedem
części, jednak w rzeczywistości nie ma to
znaczenia, ponieważ ma on być przede
wszystkim czytelny dla administratora.
Edytowanie pliku za każdym razem,
kiedy konieczne jest wprowadzenie
zmian, może stać się nieco kłopotliwe.
Znacznie wygodniejszym rozwiązaniem
jest wykorzystanie narzędzia pfctl, które
umożliwia kontrolowanie Packet Filter. Dzięki
niemu bez problemu nie tylko uruchomimy
lub wyłączymy PF, ale także wprowadzimy
zmiany w tabelach i przeładujemy
ustawienia. Aby uruchomić program,
wystarczy wydać jedno, krótkie polecenie
pfctl -e. Podobnie jest w sytuacji, w której
zachodzi potrzeba natychmiastowego
wyłączenia zapory ogniowej. Służy do tego
parametr -d. A co zrobić jeżeli koniecznie
musimy załadować plik konfiguracyjny
lub go przeładować? Oczywiście można
wykonać restart całego komputera wraz
z nowymi ustawieniami, jednak sami
przyznajcie – nie jest to komfortowe
rozwiązanie. Twórcy programu zadbali
także o umożliwienie użytkownikowi
manipulowanie zestawem reguł, tabelami
oraz samym Packet Filtrem. Jeśli
chcielibyśmy, aby został załadowany plik
konfiguracyjny, wydajemy polecenie pfctl -f
/etc/pf.conf.
Niekiedy jednak zachodzi potrzeba
załadowania jedynie określonych
fragmentów pliku, np. reguł filtrujących lub
reguł NAT. Wydawałoby się to trudne do
wykonania, gdyby nie opcje -Rf (reguły
filtrujące) i -Nf (reguły NAT). Pfctl oferuje też
bogaty wachlarz opcji pozwalających na
przeglądanie, a dokładniej na wyświetlanie
zarówno reguł, statystyk, liczników jak i
tabel stanów połączeń. Oczywiście jeśli
zachodzi taka potrzeba można wyświetlić
wszystkie informacje jednocześnie (-sa).
Zazwyczaj czytelniej i zdecydowanie
wygodniej jest dokonywać poszczególnych
odczytów oddzielnie. Aby sprawdzić
jakie mamy ustawione reguły filtrujące
lub NAT, uruchamiamy program pfctl z
parametrami -sr lub -sn. Jeśli natomiast
interesuje nas zawartość tabeli stanów
dodajemy parametr -ss. Nieco rzadziej
wykorzystywana, jednak równie użyteczna
jest opcja -si, dzięki której sprawdzimy
między innymi liczniki filtra. I choć
wydawałoby się, że zaprezentowane wyżej
dodatkowe parametry programu pfctl nie
mają dużego znaczenia dla administratora,
jest to błędny tok rozumowania, ponieważ to
właśnie one pozwalają na przejęcie pełnej
kontroli nad firewallem. Umożliwiają nie tylko
sprawdzenie jakie reguły zostały ustawione,
ale także co można dodać, aby uszczelnić
nasze zabezpieczenia.
Korzystając z pfctl z łatwością dodamy
także nowe wartości do tabeli, a nawet
utworzymy nowe tabele lub usuniemy
zbędne. Załóżmy, że chcemy wykreować
nową tabelę o nazwie danger_IP. Jak tego
dokonać bez edycji pliku pf.conf ? Wystarczy
wydać polecenie pfctl -t danger_IP -Tadd
xxx.xxx.xxx.xxx/yy. W tym przypadku nie
tylko zostanie utworzona nieistniejąca
wcześniej tabela, ale zostanie ona także
uzupełniona pierwszą wartością, którą w
naszym przypadku stanowiła by określona
grupa adresów IP. Jeśli zajdzie natomiast
potrzeba usunięcia określonego wpisu, nie
stanowi to problemu. W tym celu używamy
opcji Tdelete, zamiast Tadd. Poprawna
konfiguracja zapory ogniowej jest niezwykle
żmudnym zadaniem, jednak dzięki takim
narzędziom jak pfctl staje się nie tylko
łatwiejsza, ale też pochłania mniej czasu.
Po wprowadzeniu do tabeli odpowiednich
wartości zawsze warto przejrzeć
wprowadzone zmiany (-Tshow), ponieważ
czasem niewielki błąd może zaprzepaścić
większość naszej pracy. Oczywiście ogólna
składnia polecenia pozostaje identyczna jak
w przypadku dodawania nowych wartości,
co oznacza, że nie wolno zapomnieć o
wcześniejszym wskazaniu odpowiedniej
tabeli. Nie omówiliśmy tu wszystkich
dodatkowych opcji dla parametru -T. Oferuje
on też możliwość zabicia tabeli poprzez
rozszerzenie -Tkill, wyczyszczenie całej
zawartości (-Tflush), a nawet wyzerowanie
statystyk tabeli (-Tzero). Podobnie wygląda
sposób wprowadzania nowych makr. Służy
do tego polecenie parametr -D, który należy
uzupełnić odpowiednimi wartościami i co
najważniejsze podać nazwę dla tworzonego
makra. Mnogość opcji programu pfctl
sprawia, że trudno jest wyodrębnić, które
z nich są ważne, a które mniej istotne.
Zaleca się dokładne zapoznanie z jego
dokumentacją (man pfctl). Nie będziemy
analizować każdego możliwego polecenia,
opis ten mógłby zdominować całość tego
artykułu. Zaprezentowane powyżej przykłady
mają jedynie udowodnić, że wszystko
co potrafi Packet Filter może być w pełni
kontrolowane przez pfctl.
Zabezpieczenia dla ambitnych
Czy słyszeliście kiedykolwiek o tłumaczeniu
adresów sieciowych, nazywanym też
po prostu NAT (ang. Network Address
Translation)? Zapewne tak, a jeśli
ktokolwiek nie miał nigdy do czynienia
z tym zjawiskiem już tłumaczę. Otóż
polega to na umożliwieniu wielu hostom
znajdującym się w lokalnej sieci dostępu
do Internetu, wykorzystując w tym celu
jeden numer IP. No dobrze ale jak to
jest możliwe? Istnieją dwa sposoby, a
mianowicie poprzez router lub pojedynczy
komputer, przez który przechodzi cały ruch
sieciowy. NAT sprawia, że z Internetu może
korzystać większa liczba komputerów, przy
wykorzystaniu mniejszej liczby adresów IP.
Jest to możliwe, ponieważ prywatne adresy
osób z lokalnej sieci są dynamicznie
tłumaczone na adresy zewnętrzne. Choć
wydaje się to niezwykle skomplikowane,
efekt końcowy jest prosty do zrozumienia.
Wystarczy wyobrazić sobie sieć składającą
się z kilkunastu komputerów. Aby jednak
mogła ona łączyć się ze światem, czyli
wyjść poza pewien obręb (np. danego
budynku, jak ma to miejsce chociażby
w przypadku bloków mieszkalnych) i
połączyć się z Internetem, każdy host musi
być podłączony do głównego komputera.
Choć każdy z komputerów posiada swój
własny numer IP, tak naprawdę jest on
tłumaczony na adres zewnętrzny przez
NAT. Oczywiście rozwiązanie takie ma
określone zalety i wady. Do najważniejszych
OBRONA
64
HAKIN9
3/2010
ROZSĄDEK TO BEZPIECZEŃSTWO
65
HAKIN9
3/2010
plusów należy zaliczyć oszczędniejszą
gospodarkę publicznymi adresami IP,
których zasób nieustannie się kurczy.
Drugą ważną kwestią jest zwiększenie
prywatności i anonimowości. Największą
wadą wymienianą przez użytkowników są
występujące niekiedy problemy z usługami
peer to peer, czyli bezpośrednią wymianą
plików.
Packet Filter umożliwia również
filtrowanie pakietów NAT. Pamiętajmy
jednak, że sprawdzanie ich następuje
już po tłumaczeniu. Sprawia to, że
program nie dostrzega prawdziwego
adresu i portu danego pakietu, lecz
wersję przetłumaczoną. Ponieważ system
translacji jest powszechnie stosowany,
zagadnienie wzmacniania zapory oraz
nakładanie szczególnego nacisku na
tłumaczenie adresów sieciowych jest
niezwykle istotne. Gdzie jest wykorzystywany
NAT? Przede wszystkim wszędzie tam,
gdzie mamy do czynienia z routerami i
bramami sieciowymi. Mogą to być sieci
na blokowiskach, w firmach, na uczelniach
itp. Aby jednak rozpocząć kontrolowanie
pakietów, należy najpierw uruchomić
usługę przekazywania numerów IP. Możemy
to zrobić na dwa sposoby. Pierwsza
metoda włącza IP forwarding dynamicznie
podczas działania systemu. W tym celu
w konsoli wydajemy polecenie sysctl
net.inet.ip.forwarding=1. Oczywiście jeżeli
korzystamy z IPv4, ponieważ dla IPv6
wpisujemy sysctl net.inet6.ip6.forwarding=1.
Przypisana na końcu polecenia cyfra jeden
oznacza uruchomienie usługi. Jeżeli istnieje
konieczność uruchomienia przekazywania
pakietów IP wraz ze startem systemu,
należy powyższe polecenia dodać do pliku
/etc/sysctl.conf (pomijając początkowe
polecenie sysctl). Najprawdopodobniej
zmienne te będą znajdowały się już w
tym pliku, jednak w postaci komentarza. W
takiej sytuacji wystarczy usunąć z początku
odpowiedniej linii znak #. Teraz możemy
przystąpić do właściwej konfiguracji.
Na początku jednak zwróćmy uwagę
na umiejscowienie reguł NAT. Jest to szósta
sekcja w pliku konfiguracyjnym /etc/pf.conf,
co oznacza, że znajdują się one przed
właściwymi regułami. A teraz czas na
składnię. Każda reguła NAT zaczyna się
od deklaracji, która nadaje jej taki właśnie
charakter, czyli jak łatwo się domyśleć nat.
Następnie może znajdować się parametr
pass oznaczający, że tłumaczone pakiety
mają być przepuszczane przez filter oraz
log, czyli logowanie za pomocą pflogd.
Domyślnie logowany jest pierwszy pakiet,
jednak jeśli zależy nam, aby dotyczyło
to wszystkich wpisujemy log (all). Nie
wolno także zapomnieć o wskazaniu
odpowiedniego interfejsu, na którym pakiety
są tłumaczone (on nazwa_interfejsu). Teraz
podobnie jak w regułach omawianych we
wcześniejszej części tekstu określamy adres
źródłowy oraz port źródłowy, poprzedzając
go słowem from. To my decydujemy czy
będzie to pojedynczy adres IP, blok sieci,
domena, tabela lub lista. Następny etap to
wskazanie adresu docelowego pakietu oraz
port (poprzedzając je słowem kluczowym
to). To właśnie ten adres będzie poddany
translacji. W tym miejscu można wpisać np.
to any. Po określeniu źródła i celu, należy
uzupełnić regułę dodając -> i określając typ
tzw. puli adresów. W tym miejscu możemy
wstawić konkretny adres IP, jednak znacznie
wygodniejsze jest wskazanie zewnętrznego
interfejsu, ponieważ dzięki temu nie
będziemy musieli modyfikować reguły, gdy
zmieni się adres IP danego interfejsu. Gdyby
okazało się, że koniecznie musimy ustawić
wyjątek, wystarczy poprzedzić (lub wpisać
nową) regułę słowem no. Do sprawdzania
stanu NAT wykorzystujemy znane już
narzędzie pfctl z parametrami -s state.
Poruszając temat routera lub
komputera pełniącego taką funkcję,
warto poświęcić odrobinę czasu na
odpowiednie ustawienie kolejkowania,
ponieważ niepoprawna konfiguracja
może negatywnie wpłynąć na osiągi i
wydajność sieci. Choć działania związane
z kolejkowaniem pozornie wydają się
przynosić rezultaty jedynie dla pakietów
wychodzących nie jest to do końca prawdą.
Możemy także odpowiednio kontrolować
kolejki na wewnętrznym interfejsie routera.
Pewnie zastanawiacie się co podejmuje
decyzje dotyczące tego, która kolejka
będzie aktualnie przetwarzana. Otóż
odpowiada za to tzw. scheduler, czyli
algorytm kolejkowania. Domyślnie w
przypadku np. OpenBSD stosowana
jest zasada pierwszy wszedł, pierwszy
wyjdzie (FIFO). A co jeśli kolejka jest zbyt
długa? Zastosowane rozwiązanie jest
banalnie proste, ostatnie pakiety zostają
odrzucone. Zasadniczo wyróżniamy dwa
modele kolejkowania. Pierwszy bazuje
na klasach (ang. Class Based Queueing
– CBQ), natomiast drugi na priorytetach
(ang. Priority Queueing – PRIQ). Pytanie
zasadnicze brzmi: Jaka jest między nimi
różnica? Otóż pierwszy algorytm dzieli
dostępne pasmo na kilka kolejek (klas),
a następnie dokonuje segregacji na
podstawie chociażby adresów i portów.
Obserwujemy tu pewną hierarchię, czego
dowodem jest kolejka główna (Root, z
pasmem np. 4 Mbps) i podrzędne (pasmo
jest rozdzielone, np. queue 1 Mbps, queue
2 Mbps, queue 512 Kbps oraz queue 512
Kbps). Oczywiście jest to jedynie przykład
rozdzielenia pasma. Ciekawą właściwością
takiego rozwiązania jest możliwość
przydzielenia określonych wartości dla
poszczególnych usług danych kolejek, np.
ssh. Oczywiście suma przepustowości
poszczególnych subkolejek nie może
przekraczać pasma przydzielonego kolejce
nadrzędnej.
Nieco inaczej odbywa się dzielenie
łącza w przypadku kolejkowania
priorytetowego. Tutaj także mamy główną
kolejkę oraz podrzędne, jednak ma
przypisaną określony stopień ważności.
Wyższe priorytety zawsze są przetwarzane
jako pierwsze. Należy bardzo rozważnie
nadawać poszczególnym kolejkom
priorytety, ponieważ zły przydział może
spowodować zachwianie stabilności. W
tym wypadku dopiero kiedy w kolejce o
wyższym priorytecie skończą się pakiety
(albo będzie zakończona jej obsługa),
zostaną rozpoczęte działania na kolejnej,
która będzie miała najwyższy priorytet z
pozostałych do obsłużenia kolejek. Również
w tym wypadku obowiązuje reguła FIFO
(w danej kolejce). Wciąż można spotkać w
Internecie sprzeczne opinie na temat tego,
który ze sposobów kolejkowania jest lepszy.
Warto przetestować każdy z nich i sprawdzić
czy nasze oczekiwania bardziej spełnia
CBQ czy PRIQ. Implementacja kolejkowania,
czyli ALTQ (ang. Alternate Queueing)
została włączona do podstawowego
systemu OpenBSD, FreeBSD itd. ALTQ poza
wspomnianymi już metodami CBQ i PRIQ
obsługuje także dwa niezwykle przydatne
mechanizmy, a dokładnie RED (ang.
Random Early Detection) i ECN (ang. Explicit
Congestion Notification). Pierwszy z nich
OBRONA
64
HAKIN9
3/2010
ROZSĄDEK TO BEZPIECZEŃSTWO
65
HAKIN9
3/2010
to rodzaj systemu wczesnego wykrywania,
dzięki któremu można uniknąć przeciążenia.
Jeśli kolejka zaczyna się przepełniać
(RED nieustannie wywołuje obliczanie
różnicy pomiędzy minimum a maksimum
jej wielkości), zostają porzucone pakiety
z losowo wybranych połączeń. Jest to
znacznie lepsze rozwiązanie niż chociażby
FIFO, gdyż w tym wypadku zostałyby
porzucone wszystkie pakiety. Musimy
jednak pamiętać, że RED nie może być
używany w przypadku kolejkowania ruchu
UDP i ICMP. Powiadomienia o przeciążeniu
są natomiast zasługą wspomnianego
ECN. To on informuje poszczególne hosty o
istnieniu takiej sytuacji.
Ale jak obsłużyć kolejkowanie w
przypadku Packet Filtra? Jego konfiguracja
znajduje się w pliku /etc/pf.conf. Aby w
ogóle zaistniała implementacja, należy ją
uruchomić za pomocą opcji altq on. Po
niej należy określić na jakim interfejsie ma
nastąpić proces kolejkowania. Ponadto
dyrektywa ta umożliwia sprecyzowanie
typu algorytmu szeregowania (cbq lub
priq), maksymalną przepustowość oraz
liczbę pakietów w kolejce. Na końcu reguły,
w nawiasach klamrowych znajduje się
lista podrzędnych kolejek (np. ssh, ftp).
Przykładowe polecenie może wyglądać
tak: altq on sis0 cbq bandwidth 4Mb
qlimit 1024 queue { host1, host2, host3 }.
Kiedy mamy już włączone kolejkowanie
na danym interfejsie, należy odpowiednio
skonfigurować poszczególne kolejki
pochodne. Umożliwia to dyrektywa queue.
Po dyrektywie koniecznie należy podać
nazwę kolejki, której będzie ona dotyczyć. W
tym momencie możemy też określić nazwę
interfejsu, jednak nie jest to obowiązkowe.
Jeśli tego nie zrobimy, zapis będzie dotyczył
każdego z dostępnych interfejsów. Kolejne
parametry określają kolejno całkowitą
przepustowość (np. bandwidth 4Mb)
oraz limit pakietów w kolejce. Jeśli nie
sprecyzujemy ile pakietów może znaleźć
się w kolejce, zostanie przypisana wartość
domyślna (najczęściej pięćdziesiąt). Co
ciekawe wartość ta może mieć postać
procentową, co znacznie ułatwia dzielenie
zasobów. Oczywiście podobnie jak w
przypadku głównej kolejki, również tu
określamy algorytm szeregowania. Musi on
być taki, jak w przypadku nadrzędnej kolejki.
Jakie może przyjąć wartości? Takie jak
podane przy poleceniu altq, czyli cbq lub
priq. Nie są to jednak wszystkie możliwości,
a jedynie przykłady. Powszechnie używa
się też takich algorytmów jak htb czy
hfsc. Stosowanie HFSC jest doskonałym
rozwiązaniem, jednak należy zachować
szczególną ostrożność, podczas
klasyfikowania ruchu (konieczne jest
stworzenie domyślnej kolejki). Dlaczego
akurat ta metoda? Najważniejszą zaletą
tego algorytmu jest jego uniwersalność.
Nie tylko pozwala na dzielenie danego
pasma, ale też ustawianie priorytetów
i minimalizowanie opóźnień. Właściwie
HFSC jest poprawionym algorytmem
HTB, a dokładniej rzecz ujmując
wyeliminowano największą wadę HTB, czyli
sposób zarządzania niesklasyfikowanymi
pakietami (również ARP). Tworzył on ukrytą
kolejkę i przepuszczał takie pakiety, w
przeciwieństwie do HFSC, który może je
odrzucać. Przykładowe polecenie może
mieć postać, np. queue host1 bandwidth
256Kb hfsc(upperlimit 4Mb). W przykładzie
pojawił się host1. Aby umożliwić mu ruch
sieciowy należy odpowiednio przypisać
reguły Packet Filtra. W odniesieniu do tego
przypadku na końcu reguły dla pakietów
przychodzących i wychodzących dodajemy
queue host1. W tekście celowo nie został
zaprezentowany kompletny kod firewalla,
ponieważ najważniejszą rzeczą jest
wykonanie ściany ogniowej samodzielnie.
Jedynie wtedy możemy być pewni, że
wszystko zostało idealnie dopasowane do
siebie. Ponadto tylko w sytuacji, w której
tworzymy własny kod, wiemy dokładnie
dlaczego zastosowaliśmy akurat takie
rozwiązanie i w razie późniejszych innowacji
nie będziemy mieli z tym problemów.
Podstawą sukcesu w przypadku
zabezpieczeń jest wiedza i znajomość
oprogramowania, z którym pracujemy.
Wiele przykładów i ustawień różnych osób
znajduje się w Internecie. Zachęcam
także do lektury dokumentacji FreeBSD,
OpenBSD i NetBSD (większość można
znaleźć w języku polskim).
Podsumowanie
W artykule omówiliśmy podstawowe
aspekty zabezpieczania systemu z
rodziny BSD. W podobny sposób można
chronić systemy Linux korzystając
chociażby z iptables (omówionego
na łamach magazynu hakin9). Celem
zaprezentowanego tekstu absolutnie nie
było tworzenie kolejnej, szczegółowej
dokumentacji, a jedynie nakreślenia
zagadnienia i omówienie pewnych nie
do końca rozwiniętych w niej wątków.
Wybór Packet Filtra nie był przypadkowy,
ponieważ systemy BSD niezwykle często
wykorzystywane są w sytuacjach, kiedy
zachodzi potrzeba kontrolowania ruchu
sieciowego. Pamiętajmy jednak, że
nawet najdoskonalsze oprogramowanie
nie będzie w stanie zaoferować
bezpieczeństwa naszym danym, jeśli sami
o to nie zadbamy. Najważniejszą bronią
administratora jest jego rozwaga podczas
zabezpieczania sieci, natomiast oferowane
przez twórców dystrybucji rozwiązania z
całą pewnością nie ustępują produktom
komercyjnym. Wszystko zależy od
naszych chęci i umiejętności. Czy zapora
sieciowa musi być skomplikowana? Wręcz
przeciwnie, może składać się z kilku linii
reguł i działać bez zarzutu. Jak osiągnąć
doskonałość? Testy, eksperymenty i
nieuniknione porażki, na których najwięcej
się uczymy. A teraz włącz komputer i zbuduj
firewall doskonały!
Listing 3.
Przykład nieskomplikowanego użycia Filtra Pakietów
table
<
przyjazne_adresy_IP
>
{
aaa
.
aaa
.
aaa
.
aaa
/
yy
,
bbb
.
bbb
.
bbb
.
bbb
/
yy
}
block
in
on
sis0
all
pass
in
on
sis0
from
<
przyjazne_adresy_IP
>
to
any
Listing 4.
Umożliwienie przeglądania stron internetowych
pass
in
quick
on
sis0
proto
udp
from
xxx
.
xxx
.
xxx
.
xxx
/
yy
to
any
port
=
53
keep
state
pass
in
quick
on
sis0
proto
tcp
from
xxx
.
xxx
.
xxx
.
xxx
/
yy
to
any
port
=
80
fl ags
S
/
SA
keep
state
Łukasz Ciesielski
Autor z wykształcenia jest dziennikarzem, a także
pasjonatem programowania w C/C++, Javie i PHP. W
wolnych chwilach zgłębia tajniki systemów z rodziny Linux
i BSD. Kontakt z autorem: lucas.ciesielski@gmail.com