background image

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.

background image

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 

background image

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ł.

background image

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. dc0sis0 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

background image

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 
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 

background image

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 

background image

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 

background image

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