Uwaga:
Oryginalna wersja tego artykułu została opublikowana w IBM developerWorks i
jest własnością Westtech Information Services. Poniższy dokument jest
poprawioną przez zespół GDP wersją oryginalnego tekstu i nie jest już
aktualizowany.
|
Projektowanie pełnostanowanego firewalla dla Linuksa 2.4
1.
O tym dokumencie
Czego dotyczy ten artykuł?
W artykule omówimy użycie netfilter do skonfigurowania skutecznego firewalla
pełnostanowego pod Linuksem. Wystarczy działający system Linux z jądrem 2.4.
Może to być laptop, stacja robocza, router lub serwer z Linuksem 2.4.
Niezbędna jest pewna znajomość standardowej terminologii dotyczącej
problematyki sieciowej, takiej jak adresy IP, numery portów źródłowych i
docelowych, TCP, UDP, ICMP, etc. Po lekturze tego artykułu stanie się jasne,
jak są skonstruowane linuksowe pełnostanowe zapory sieciowe. Pokazane również
zostanie kilka przykładowych konfiguracji.
O autorze
Z technicznymi pytaniami dotyczącymi zawartości tego podręcznika należy się
zwracać do autora, Daniela Robbinsa, na adres drobbins@gentoo.org.
Mieszkający w Albuqerque, w Nowym Meksyku, Daniel Robbins był Głównym
Architektem Gentoo Technologies Inc., twórcą Gentoo Linux, zaawansowanej
dystrybucji dla komputerów PC. Stworzył również Portage, nowej generacji system
portów programowych dla Linuksa. Uczestniczył także w zespołach redakcyjnych
wydawnictwa Macmillan piszących książki takie jak "Caldera OpenLinux
Unleashed", "SuSE Linux Unleashed" oraz "Samba Unleashed". Daniel
zainteresował się komputerami w szkole średniej, kiedy los zetknął go z
językiem programowania Logo, jak również został wystawiony na potencjalnie
niebezpieczną dawkę PacMana. To prawdopodobnie wyjaśnia, czemu później
pracował jako grafik w SONY Electronic Publishing/Psygnosis. Daniel najbardziej
lubi spędzać czas ze swoją żoną Mary i nowo narodzoną córką Hadassah.
2.
Pierwsze kroki
Definiowanie celu
Ten artykuł ma pokazać, jak zainstalować firewall pełnostanowy (zwany inaczej
"stanowym") pod Linuksem. Powinien on działać na laptopie, stacji roboczej,
serwerze lub routerze. Jego podstawowym zadaniem ma być przepuszczenie
wyłącznie ruchu sieciowego konkretnego typu. Dla zwiększenia bezpieczeństwa,
firewall ten zostanie tak skonfigurowany, aby upuszczać (DROP) lub odrzucać
(REJECT) zarówno takie typy pakietów, które nie są potrzebne, jak również
takie, które mogą stanowić zagrożenie dla bezpieczeństwa.
Przygotowanie narzędzi
Zanim rozpoczniemy konstrukcję firewalla, będziemy potrzebować dwóch rzeczy. Po
pierwsze należy się upewnić, że możemy wywołać komendę iptables. Należy
napisać iptables jako root i zobaczyć czy program istnieje. Jeśli nie,
trzeba będzie go najpierw zainstalować w poniższy sposób:
Listing 2.1: Instalowanie potrzebnych narzędzi |
# emerge iptables
|
Konfiguracja jądra
Po zainstalowaniu, komenda iptables powinna być dostępna, utworzona
zostanie również strona manuala (man iptables). Teraz pozostaje się
upewnić, że mamy niezbędne funkcjonalności wbudowane w jądro. Ten
podręcznik wymaga umiejętności samodzielnej kompilacji kernela. Należy wejść do
/usr/src/linux i wpisać make menuconfig lub make
xconfig. Będziemy uruchamiać pewne funkcje sieciowe jądra.
W sekcji "Networking options" należy włączyć przynajmniej poniższe opcje:
Listing 2.2: Niezbędne opcje w kernelu |
<*> Packet socket
[*] Network packet filtering (replaces ipchains)
<*> Unix domain sockets
[*] TCP/IP networking
[*] IP: advanced router
[*] IP: policy routing
[*] IP: use netfilter MARK value as routing key
[*] IP: fast network address translation
[*] IP: use TOS value as routing key
|
Następnie pod menu "IP: Netfilter Configuration ->" należy włączyć wszystkie
opcje, co zapewni pełną funkcjonalność zapory. Nie wszystkich jej możliwości
użyjemy, ale dobrze mieć je włączone do przyszłych eksperymentów.
Jednej opcji w kategorii "Networking options" nie należy włączać:
explicit congestion notification. Należy pozostawić ją wyłączoną.
Listing 2.3: Opcja do wyłączenia |
[ ] IP: TCP Explicit Congestion Notification support
|
Gdyby ta opcja została włączona, komputer z Linuksem nie mógłby uzyskać dostępu
do 8% zasobów Internetu. Mając włączoną opcję ECN, Linux wysyła niektóre
pakiety z ustawionym bitem ECN, chociaż to powoduje kłopoty z niektórymi
internetowymi routerami. Zatem wyłączenie tej opcji bardzo ważne. Jeśli opcja
ta nie jest w ogóle dostępna w jądrze, można zignorować te ostrzeżenia.
Po wszystkim będziemy mieli kernel skonfigurowany zgodnie z naszymi potrzebami.
Teraz trzeba go skompilować, zainstalować i ponownie wystartować system. Pora
na zabawę z netfilter. :)
Podstawy tworzenia firewalla
Naszym najlepszym przyjacielem przy konstrukcji zapory jest komenda
iptables. Używa się jej do interakcji z regułami filtrowania pakietów w
jądrze systemu. Będziemy używać komendy iptables do tworzenia nowych
reguł, wyświetlania już istniejących, usuwania ich oraz ustawiania domyślnej
polityki zarządzania pakietami. Oznacza to, że aby zbudować firewall, trzeba
będzie wprowadzić serię komend iptables. I temu się przyjrzymy najpierw (na
razie nie radzimy wpisywać żadnych poleceń, tylko po prostu czytać dalej).
Listing 2.4: Zmiana polityki łańcuchów na DROP |
# iptables -P INPUT DROP
|
Powyżej widać prawie "doskonały" firewall. Jeżeli wprowadzimy tę komendę,
będziemy niewiarygodnie dobrze chronieni przed jakimkolwiek złośliwym atakiem.
A wszystko dlatego, że każe ona kernelowi odrzucić każdy przychodzący pakiet.
Chociaż taki firewall jest krańcowo bezpieczny, ale rozwiązanie to nie należy
do najmądrzejszych. Zanim przejdziemy dalej, popatrzmy jak dokładnie działa ta
komenda.
Ustanawienie polityki
Komendy iptables można użyć do ustawienia domyślnej polityki dla
łańcucha reguł filtrowania pakietów. W tym przykładzie, iptables
zostanie użyta do ustanowienia domyślnej polityki dla INPUT, wbudowanego
łańcucha reguł mających zastosowanie do każdego przychodzącego pakietu.
Ustawiając domyślną politykę na DROP, przekazujemy do jądra systemu, że każdy
pakiet, który osiągnie koniec łańcucha reguł INPUT, ma zostać upuszczony (czyli
zignorowany). Zatem, ponieważ nie dodaliśmy żadnej innej reguły do łańcucha
INPUT, wszystkie pakiety osiągają koniec łańcucha i zostają upuszczone.
Oczywiście ta komenda jako taka jest kompletnie bezużyteczna, aczkolwiek
ukazuje dobrą strategię tworzenia firewalla. Zaczynamy od domyślnego
upuszczania wszystkich pakietów, a następnie stopniowo otwieramy zaporę zgodnie
z naszymi potrzebami. To uczyni ją możliwie szczelną i bezpieczną.
3.
Definiowanie reguł
Poprawka (niewielka)
Na użytek tego przykładu przyjmiemy, że tworzymy firewall dla komputera
z dwoma interfejsami sieciowymi, eth0 i eth1. Karta eth0 jest podłączona do
naszej sieci LAN, natomiast karta eth1 do routera DSL, łącza z Internetem. W
związku z tym poprawimy naszą "najdoskonalszą zaporę ogniową" przez dodanie
jednej linii:
Listing 3.1: Poprawianie najdoskonalszej zapory ogniowej |
# iptables -P INPUT DROP
# iptables -A INPUT -i ! eth1 -j ACCEPT
|
Kolejna linia, iptables -A, dodała nową regułę filtrowania pakietów na
koniec łańcucha INPUT. Teraz nasz łańcuch INPUT zawiera pojedynczą regułę oraz
domyślną politykę "upuść". Popatrzmy zatem, co teraz potrafi nasz prawie
ukończony firewall.
Przegląd łańcucha INPUT
Kiedy do dowolnego interfejsu sieciowego (lo, eth0 lub eth1) przychodzi pakiet,
kod netfilter kieruje go do łańcucha INPUT i porównuje czy pasuje on do
pierwszej reguły. Jeżeli tak, pakiet zostaje zaakceptowany i nie podlega
dalszemu przetwarzaniu. Jeżeli nie, zostaje zastosowana domyślna polityka i
pakiet jest upuszczany (ignorowany).
Tak to wygląda ogólnie. Patrząc dokładniej, nasza pierwsza reguła pasuje do
wszystkich pakietów przychodzących poprzez kartę eth0 oraz interfejs lo i
niezwłocznie je przepuszcza. Natomiast każdy pakiet przychodzący poprzez eth1
zostanie odrzucony. Zatem jeżeli włączymy ten firewall na naszej maszynie,
będzie ona mogła komunikować się z naszą siecią LAN, ale zostanie całkowicie
odcięta od Internetu. Przyjrzyjmy się kilku metodom otwarcia ruchu
internetowego.
Tradycyjne firewalle
Oczywiście, aby nasza zapora stała się użyteczna, niektóre, wybrane pakiety z
Internetu powinny zostać dopuszczone do naszej maszyny. Istnieją dwa podejścia
do otwierania naszej zapory do użytecznego poziomu: jedna korzysta ze
statycznych reguł, druga używa reguł dynamicznych, pełnostanowych.
Przyjrzyjmy się na przykład pobieraniu strony internetowej. Jeżeli chcemy, aby
nasza maszyna pobierała strony internetowe, możemy dodać regułę statyczną,
która zawsze będzie pasować do każdego przychodzącego pakietu http,
niezależnie od źródła pochodzenia:
Listing 3.2: Akceptowanie wszystkich przychodzących pakietów http |
# iptables -A INPUT --sport 80 -j ACCEPT
|
Ponieważ cały standardowy ruch http przychodzi z portu 80, reguła ta efektywnie
dozwoli naszemu komputerowi pobierać strony www. Jednakowoż, takie tradycyjne
podejście, krańcowo akceptowalne, sprawia kilka problemów.
Tradycyjne bolączki firewalla
Oto problem: wprawdzie nie cały, ale większość ruchu internetowego pochodzi z
portu 80. Zatem, o ile powyższa reguła zadziała w większości prawidłowo, w
niektórych wypadkach zaszkodzi. Na przykład, zapewne widzieliście URL, który
wygląda tak: "http://www.foo.com:81". Ten adres wskazuje na stronę internetową
dostępną na porcie 81, zamiast domyślnego 80. Zatem będzie niedostępny spoza
naszej aktualnej zapory ogniowej. Uznawanie wszystkich takich nietypowych
wypadków szybko zamieni naszą superbezpieczną ścianę ogniową w ser szwajcarski
i wypełni łańcuch INPUT masą reguł do zastosowania dla bardzo rzadkich [oddball]
stron www.
Jednakowoż najważniejszy problem tej reguły związany jest z bezpieczeństwem.
Oczywiście to prawda, że tylko ruch z portu 80 zostanie przepuszczony przez
firewall, lecz port pochodzenia (source port) pakietu to nie to, co powinniśmy
kontrolować, jako że może on być łatwo zmieniany przez atakującego. Na
przykład jeżeli atakujący wie jak firewall został skonstruowany, może go
obejść, wystarczy, że wszystkie połączenia przychodzące z jego maszyny będą
pochodzić z portu 80! Skoro statyczna reguła zapory jest tak łatwa do
pokonania, potrzebujemy bezpieczniejszego, dynamicznego podejścia. Szczęśliwie,
iptables i kernel 2.4 zawierają wszystko, co potrzebne do uruchomienia
dynamicznego, pełnostanowego filtrowania.
4.
Firewalle pełnostanowe
Podstawy
Zamiast otwierać dziury w oparciu o statyczne charakterystyki protokołu w naszej
zaporze ogniowej, możemy użyć nowej linuksowej funkcjonalności śledzenia
(tracking), aby decyzje firewalla opierały się o dynamiczny stan połączenia
pakietów. Conntrack działa poprzez podłączanie każdego pakietu do
indywidualnego, dwukierunkowego kanału komunikacyjnego lub połączenia.
Sprawdźmy na przykład co się stanie, kiedy użyjemy telnetu lub ssh, aby
podłączyć się zdalnie do oddalonego komputera. Jeżeli spojrzymy na sieć z
poziomu pakietu, wszystko, co zobaczymy, to masa pakietów sunąca z jednej
maszyny do drugiej. Na wyższym poziomie tymczasem, ta wymiana pakietów stanie
się w rzeczywistości dwukierunkowym kanałem komunikacyjnym pomiędzy naszym
komputerem a drugim, znajdującym się gdzieś w sieci. Tradycyjne (przestarzałe i
niemodne) zapory sieciowe analizują każdy pakiet indywidualnie i nie
rozpoznają, że są one naprawdę częścią większej całości - połączenia.
Conntrack od środka
W tym miejscu wkracza technika śledzenia. Linuksowa funkcjonalność conntrack
może "widzieć" wyższy poziom odbywających się połączeń, gdyż rozpoznaje sesję
ssh jako pojedynczą jednostkę logiczną. Conntrack może nawet rozpoznać wymianę
pakietów ICMP i UDP jako logiczne "połączenia", nawet pomimo faktu, że UDP i
ICMP z natury swojej nie tworzą połączeń. Jest to bardzo użyteczne, jako że
pozwala zarządzać wymianą pakietów ICMP i UDP.
Jeżeli już zrestartowaliśmy i uruchomiliśmy jądro systemu z włączonym
netfilterem, możemy zobaczyć aktywne połączenia sieciowe na naszej maszynie
poprzez wpisanie cat /proc/net/ip_conntrack. Nawet bez skonfigurowanej
zapory, funkcja conntrack działa w tle, podejmując śledzenie połączeń w których
uczestniczy nasz komputer.
Stan połączenia NEW.
Conntrack nie tylko rozpoznaje połączenia, również klasyfikuje każdy widziany
pakiet do jednego z czterech stanów połączenia. Pierwszy stan, o którym
będziemy mówić, nazywany jest NOWYM (NEW). Jeżeli napisać ssh
remote.host.com, pakiet początkowy lub seria pakietów pochodzących z naszej
maszyny, a skierowanych do remote.host.com znajdą się w stanie NEW. Jednak
dopóki nie otrzymamy choćby pojedynczego pakietu odpowiedzi od remote.host.com,
żaden następny pakiet wysłany do remote.host.com jako część tego samego
połączenia nie zostanie zaliczony do stanu NEW. Zatem pakiety mogą zostać
uznane do stanu NEW tylko jeżeli służą do nawiązania nowego połączenia i żaden
ruch zwrotny ze zdalnego hosta (jako część danego połączenia oczywiście) jeszcze
się nie pojawił.
Opisane tu zostały pakiety wychodzące stanu NEW, ale przecież często się zdarzy
mieć w stanie NEW pakiety przychodzące. Takie pakiety generalnie pochodzą z
oddalonej maszyny i służą do nawiązania połączenia z nami. Pakiety początkowe,
które otrzymuje nasz Web serwer jako część zapytania HTTP, zostaną zaliczone do
stanu NEW. Jednak kiedy tylko odpowiemy na choćby jeden przychodzący
pakiet, żaden następny związany z tym konkretnym połączeniem nie zostanie
zaliczony do stanu NEW.
Stan ESTABLISHED
Kiedy tylko zostanie nawiązane połączenie w obu kierunkach, następne pakiety
związane z tym połączeniem zostaną zaliczone do stanu USTANOWIONE
(ESTABLISHED). Stany NEW i ESTABLISHED istotnie się różnią, jak za chwilę
zobaczymy.
Stan RELATED
Trzecia kategoria stanu połączenia nazywana jest POWIĄZANY (RELATED). Pakietami
POWIĄZANYMI nazywamy takie, które nawiązują nowe połączenie, ale w powiązaniu
do innego, już istniejącego. Stan RELATED może zostać użyty do regulowania
połączeń, które stanowią część protokołu wielopołączeniowego
(multi-connection) jak FTP lub dla pakietów błędów (error packets) powiązanych
z istniejącymi połączeniami (jak komunikaty błędów ICMP powiązane z istniejącym
połączeniem).
Stan INVALID
W końcu mamy również pakiety BŁĘDNE (INVALID) - takie, które nie mogą zostać
zaklasyfikowane do żadnej z trzech powyższych kategorii. Należy zwrócić uwagę,
że pakiety uznane za INVALID, nie zostają automatycznie usunięte. Właściwe
nimi operowanie zależy od twórcy firewalla, do którego należy ustanowienie
odpowiedniej reguły i łańcucha polityki.
Dodawanie reguły pełnostanowej.
W porządku, zatem rozumiemy już zasady śledzenia połączeń, czas na to, aby
przyjrzeć się jednej dodatkowej regule, która zamieni nasz
niefunkcjonalny firewall w coś całkiem użytecznego:
Listing 4.1: Dodawanie reguły pełnostanowej. |
# iptables -P INPUT DROP
# iptables -A INPUT -i ! eth1 -j ACCEPT
# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
Jak działa ta reguła.
Kiedy ta pojedyncza reguła zostanie dodana do istniejącego łańcucha INPUT,
przyzwoli na ustanawianie połączeń z oddalonymi maszynami. Działa następująco:
Powiedzmy, że chcemy połączyć się przy pomocy ssh do remote.host.com. Po
wpisaniu ssh remote.host.com, nasz komputer wysyła pakiet do
rozpoczęcia połączenia. Ten pojedynczy pakiet znajduje się w stanie NEW i nasz
firewall wypuści go, ponieważ blokujemy wyłącznie pakiety przychodzące do
naszego firewalla, a nie opuszczające go.
Kiedy przyjdzie pakiet z odpowiedzią od remote.host.com, przechodzi przez nasz
łańcuch INPUT. Nie pasuje do pierwszej reguły (skoro przychodzi na eth1), więc
idzie do naszej następnej, finalnej reguły. Jeżeli do niej pasuje, zostanie
zaakceptowany, jeżeli nie, spadnie na koniec łańcucha INPUT i zostanie
zastosowana domyślna polityka (DROP). Zatem czy ten pakiet z odpowiedzią
zostanie zaakceptowany czy upuszczony?
Odpowiedź: zostanie zaakceptowany. Kiedy kernel sprawdza przychodzący pakiet,
po pierwsze rozpoznaje czy jest częścią istniejącego połączenia. Następnie
musi zdecydować czy jest to pakiet stanu NEW czy ESTABLISHED. Skoro jest to
pakiet przychodzący, jądro sprawdza czy w tym połączeniu wystąpił już jakiś
ruch wychodzący i stwierdza, że tak było (nasz początkowy pakiet stanu NEW,
który został już wysłany). Zatem ten przychodzący pakiet zostanie zaliczony do
stanu ESTABLISHED, jak i wszystkie następne pakiety, które otrzymamy lub
wyślemy w ramach tego połączenia.
Przychodzące pakiety NEW
Rozważmy teraz co się stanie, jeżeli ktoś spróbuje połączyć się z nami przez
ssh ze zdalnego komputera. Otrzymany pakiet początkowy zostanie zakwalifikowany
jako NEW, nie pasuje do reguły 1, więc przechodzi do reguły 2. Ponieważ nie
znajduje się w stanie ESTABLISHED lub RELATED, spada na koniec łańcucha INPUT i
zostaje zastosowana domyślna polityka DROP. Przychodzące zapytanie o połączenie
ssh zostaje spuszczone po linie bez najmniejszej odpowiedzi z naszej strony
(lub komunikatu TCP reset).
Prawie doskonały firewall
Zatem jakiego firewalla już się dorobiliśmy? Doskonałego dla laptopa lub stacji
roboczej, gdzie nie życzymy sobie połączeń przychodzących z Internetu, ale
kiedy sami potrzebujemy połączyć się z jakąś stroną lub usługą, drzwi są
otwarte. Będziemy mogli używać Netscape, konquerora, ftp, ping, wykonywać DNS
lookups i wiele innych rzeczy. Każde połączenie zainicjowane przez nas
przedostanie się przez firewall. Natomiast jakiekolwiek połączenie samowolnie
dobijające się do nas z Internetu zostanie odrzucone, chyba że jest powiązane z
połączeniem zainicjowanym przez nas. Dopóki nie prowadzimy żadnych usług
sieciowych, mamy prawie że doskonałą zaporę ogniową.
Podstawowy skrypt zapory ogniowej
Poniżej znajduje się prosty skrypt, który może zostać użyty do ustanowienia lub
zburzenia naszego pierwszego, podstawowego firewalla dla stacji roboczej:
Listing 4.2: Podstawowy skrypt zapory ogniowej. |
#!/bin/bash
if [ "$1" = "start" ]
then
echo "Starting firewall..."
iptables -P INPUT DROP
iptables -A INPUT -i ! eth1 -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
elif [ "$1" = "stop" ]
then
echo "Stopping firewall..."
iptables -F INPUT
iptables -P INPUT ACCEPT
fi
|
Jak używać skryptu
Wyłączenie zapory nastąpi po wpisaniu ./firewall stop, ponowne włączenie
poprzez wpisanie ./firewall start. Aby wyłączyć firewalla opróżniamy
nasz łańcuch INPUT z reguł poprzez iptables -F INPUT, następnie
przełączamy domyślną politykę na ACCEPT poprzez komendę iptables -P INPUT
ACCEPT. Przyjrzyjmy się teraz garści ulepszeń, które możemy wprowadzić
do naszej już działającej zapory. Po tym, jak już objaśnimy każdą poprawkę,
zobaczymy pełny skrypt zapory i przystąpimy do rozwijania go pod kątem
zastosowań serwerowych.
5.
Poprawki pełnostanowe
Całkowicie wyłączyć ECN
Jak już zauważyliśmy wcześniej, należy koniecznie wyłączyć ECN (explicit
congestion notification), aby nasze połączenia internetowe działały poprawnie.
Chociaż może i wyłączyliście ECN w jądrze systemu po mojej sugestii, w
przyszłości możecie o tym zapomnieć. Możliwe też, że prześlecie skrypt firewall
komuś, kto ma włączony ECN. Dlatego wydaje się sensownym pomysłem użycie
interfejsu /proc, aby całkowicie wyłączyć ECN:
Listing 5.1: Całkowicie wyłączyć ECN. |
if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi
|
Przekazywanie pakietów
Jeżeli używamy naszego komputera z Linuksem jako routera, możemy zechcieć
włączyć przekazywanie pakietów IP, co pozwoli kernelowi przepuszczać pakiety
pomiędzy eth0 a eth1 i z powrotem. W naszej przykładowej konfiguracji, gdzie
karta eth0 została podłączona do naszej sieci LAN, a eth1 do Internetu,
włączenie przekazywania pakietów IP stanowi krok niezbędny, aby pozwolić sieci
LAN komunikować się z Internetem przez nasz komputer z Linuksem. Aby włączyć
przekazywanie pakietów IP, użyjcie poniższej linijki:
Listing 5.2: Przekazywanie pakietów. |
# echo 1 > /proc/sys/net/ipv4/ip_forward
|
Przygotowanie odrzuceń
Dotychczas odrzucaliśmy cały samowolny ruch przychodzący z Internetu. Chociaż
jest to dobry sposób na trzymanie z dala niechcianych działań z sieci, ma
również kilka wad. Największym problemem takiego podejścia jest łatwość, z jaką
atakujący może wykryć, że używamy firewalla, skoro nasza maszyna nie odpowiada
standardowymi komunikatami TCP reset i ICMP port-unreachable - a które
normalna maszyna powinna odsyłać jako znak nieudanego połączenia lub braku
oczekiwanej usługi.
Zamiast dać atakującemu znać, że używamy firewalla (i w ten sposób zasugerować
mu, że może za nim ukrywamy jakieś cenne usługi, do których warto się dobrać),
korzystniej dla nas będzie twierdzić, że nie mamy żadnych działających usług.
Uzyskamy to poprzez dodanie poniższych dwóch regułek na końcu naszego łańcucha
INPUT:
Listing 5.3: Przygotowanie odrzuceń. |
# iptables -A INPUT -p tcp -i eth1 -j REJECT --reject-with tcp-reset
# iptables -A INPUT -p udp -i eth1 -j REJECT --reject-with icmp-port-unreachable
|
Pierwsza reguła zajmuje się poprawnym odstrzeliwaniem połączeń TCP, podczas gdy
druga odpowiada za UDP. Wobec tych dwóch reguł intruzowi bardzo trudno będzie
wykryć, że w rzeczywistości korzystamy z firewalla. Przy pewnej dozie szczęścia
atakujący odpuści sobie naszą maszynę i poszuka innych celów, łatwiejszych do
upolowania.
Oprócz uczynienia naszej zapory trudniejszą do wykrycia, regułki te również
eliminują opóźnienia związane z podłączaniem się do konkretnych serwerów ftp i
irc. Opóźnienia te spowodowane są przez serwer wykonujący ident lookup do
naszej maszyny (podłączenie na porcie 113) i rozłączający się dopiero po ok. 15
sekundach. Teraz nasz firewall zwróci komunikat TCP reset i ident lookup
zakończy się niezwłocznie zamiast powtarzać się przez 15 sekund (podczas kiedy
my cierpliwie będziemy czekać na odpowiedź serwera).
Ochrona przed podszywaniem się (spoofingiem)
W wielu dystrybucjach, kiedy interfejsy sieciowe włączą się, zostaje
dodane do systemu kilka starych reguł ipchains. Reguły te zostały stworzone
przez autorów dystrybucji, aby poradzić sobie z problemem zwanym "spoofing".
Polega on na tym, że źródłowy adres pakietów zostaje podmieniony, przez co
zawierają one nieprawdziwą wartość (script-kiddies często się tym posługują).
Chociaż możemy utworzyć podobne reguły iptables blokujące podrobione pakiety,
jest na to łatwiejszy sposób. Współczesne jądro systemu ma wbudowaną zdolność
odrzucania podrobionych pakietów. Wszystko, co trzeba zrobić, to uruchomić to
przez prosty interfejs /proc.
Listing 5.4: Ochrona przed podszywaniem się (spoofingiem). |
for x in lo eth0 eth1
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done
|
Ten skrypt shellowy nakaże kernelowi odrzucić wszystkie podrobione pakiety na
interfejsach lo, eth0 i eth1. Możemy również dodać te linijki do naszego
skryptu firewalla lub do skryptu włączającego nasze interfejsy lo, eth0 i eth1.
Maskarada (Masquerading)
Translacja Adresów Sieciowych - NAT (network address translation) oraz
maskowanie IP (IP masquerading), chociaż nie do końca związane z ideą zapory
ogniowej, często są używane razem z nią. Przyjrzymy się tutaj dwóm popularnym
konfiguracjom NAT i Maskarady, których moglibyśmy potrzebować. Pierwsza reguła
zajmuje się sytuacjami, w których mamy połączenie wdzwaniane (ppp0) do
Internetu, w którym zastosowane jest dynamiczne przyznawanie numeru IP:
Listing 5.5: Maskarada (Masquerading) |
# iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
|
Jeżeli mamy taką sytuację, możemy również zmienić te fragmenty skryptu, które
odwołują się do "eth1" (nasz przykładowy router DSL) na "ppp0". Nie ma także
problemu z dodawaniem reguł dotyczących interfejsu "ppp0", chociaż takiego na
razie nie mamy. Kiedy tylko zostanie włączony, wszystko zadziała doskonale.
Należy się również upewnić, że również przekazywanie IP zostało włączone.
SNAT
Jeżeli do połączeń internetowych używamy DSL, mamy prawdopodobnie jedną lub dwie
możliwe konfiguracje. W jednej nasz router DSL lub modem ma swój własny numer
IP i przeprowadza dla nas translację adresów IP. W takim razie nie ma już
potrzeby wykonywać NAT na naszym Linuksie, skoro zajmuje się tym router DSL.
Jeżeli jednak chcemy sprawować ściślejszą kontrolę nad naszą funkcją NAT,
możemy dogadać się z naszym ISP (dostawcą łącza internetowego) na temat
konfiguracji naszego routera DSL w "tryb mostka" (bridged mode). W tym trybie
nasz firewall stanie się oficjalną częścią sieci naszego dostawcy łącza, a
nasz router DSL transparentnie przekaże ruch IP pomiędzy maszynami ISP a naszym
pudłem z Linuksem, bez zawiadamiania kogokolwiek o sobie. Nie będzie miał
adresu IP, zamiast tego eth1 (w naszym przykładzie) ukazuje numer IP. Jeżeli
ktoś spinguje nasz numer IP, otrzyma odpowiedź od naszej Linuksowej skrzynki, a
nie od routera.
W przypadku takiego rodzaju ustawień, możemy zechcieć użyć SNAT (source NAT)
zamiast maskarady. Poniżej linijka, którą należy dodać do naszego firewalla:
Listing 5.6: SNAT |
# iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to 1.2.3.4
|
W tym przykładzie "eth1" powinno zostać zamienione na nazwę interfejsu
bezpośrednio podłączonego do naszego routera DSL, a 1.2.3.4 na nasz statyczny
numer IP (naszego interfejsu sieci ethernet). Znowu, nie zapomnijmy uruchomić
przekazywania IP (IP forwarding).
Kwestia NAT
Szczęśliwie dla nas, NAT i maskarada działają całkiem nieźle z firewallem.
Pisząc reguły zapory można po prostu zapomnieć, że używamy translacji adresów
sieciowych. Nasze regułki powinny akceptować (accept), porzucać (drop) lub
odrzucać (reject) pakiety w oparciu o ich "prawdziwe" źródło i adresy docelowe.
Kod filtrujący zapory ogniowej widzi oryginalny adres źródłowy pakietu i
ostateczny adres docelowy. To bardzo dobrze dla nas, ponieważ pozwala to
pracować naszej zaporze, nawet jeżeli chwilowo wyłączymy NAT lub maskowanie
pakietów.
Rozumienie tabel
W powyższych przykładach NAT/maskarady dodajemy reguły do łańcucha, ale również
robimy coś innego. Zwróćcie uwagę na opcję "-t". Ta opcja pozwala nam wyznaczyć
tabelę, do której należy nasz łańcuch. Jeżeli ją pominiemy, zostanie on
zapisany do domyślnej tabeli "filter". Zatem wszystkie nasze poprzednie komendy
nie związane z NAT modyfikowały łańcuch INPUT będący częścią tabeli "filter".
Zawiera ona wszystkie reguły dotyczące akceptowania lub odrzucania pakietów,
podczas gdy tabela "nat" (jak można przypuszczać) zawiera reguły związane
z translacją adresów sieciowych. Istnieją również inne wbudowane łańcuchy tabel
iptables, opisane szczegółowo na stronach manuala iptables oraz w zasobach
HOWTO Rusty'ego (szukajcie odsyłaczy w Zasobach na
końcu tego przewodnika).
Nasz ulepszony skrypt
Teraz, skoro przyjrzeliśmy się grupie możliwych ulepszeń, pora zwrócić uwagę na
kolejny, bardziej elastyczny skrypt uruchamiający/wyłączający firewall:
Listing 5.7: Nasz ulepszony skrypt |
#!/bin/bash
UPLINK="eth1"
ROUTER="yes"
NAT="1.2.3.4"
INTERFACES="lo eth0 eth1"
if [ "$1" = "start" ]
then
echo "Starting firewall..."
iptables -P INPUT DROP
iptables -A INPUT -i ! ${UPLINK} -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -i ${UPLINK} -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i ${UPLINK} -j REJECT --reject-with icmp-port-unreachable
if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi
for x in ${INTERFACES}
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done
if [ "$ROUTER" = "yes" ]
then
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ "$NAT" = "dynamic" ]
then
echo "Enabling masquerading (dynamic ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j MASQUERADE
elif [ "$NAT" != "" ]
then
echo "Enabling SNAT (static ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j SNAT --to ${UPIP}
fi
fi
elif [ "$1" = "stop" ]
then
echo "Stopping firewall..."
iptables -F INPUT
iptables -P INPUT ACCEPT
iptables -t nat -F POSTROUTING
fi
|
6.
Pełnostanowe serwery (stateful)
Przegląd reguł
Zanim zaczniemy dopasowywać nasz skrypt do potrzeb zapory na konkretnym
serwerze, należy wiedzieć, jak wyświetlić listę naszych aktualnie działających
reguł firewalla. Aby zobaczyć reguły w tabeli "filter" łańcucha INPUT, należy
wpisać:
Listing 6.1: Przegląd reguł. |
# iptables -v -L INPUT
|
Opcja -v pozwala na szczegółowy ogląd, dzięki czemu można zobaczyć wszystkie
pakiety i bajty przepuszczane przez regułę. Można również sprawdzić nasz
POSTROUTING w tabeli "nat" przy pomocy komendy:
Listing 6.2: Oglądanie reguł tabeli POSTROUTING |
# iptables -t nat -v -L POSTROUTING
Chain POSTROUTING (policy ACCEPT 399 packets, 48418 bytes)
pkts bytes target prot opt in out source destination
2728 170K SNAT all -- any eth1 anywhere anywhere
to:215.218.215.2
|
Przygotowanie do pracy
W tej chwili nasz firewall nie akceptuje żadnych połączeń z zewnątrz do usług
na naszej maszynie, ponieważ przepuszcza wyłącznie przychodzące pakiety z
połączeń nawiązanych (ESTABLISHED) lub powiązanych (RELATED). Ponieważ upuszcza
wszystkie pakiety przychodzące w stanie NEW, każda próba nawiązania połączenia
zostaje odrzucona bezwarunkowo. Tymczasem poprzez wybiórcze pozwalanie
niektórym typom ruchu przychodzącego na przekroczenie naszej zapory, możemy
udostępnić niektóre z usług dla osób z zewnątrz.
Pełnostanowe HTTP
Chociaż chcemy dopuścić niektóre połączenia przychodzące, prawdopodobnie nie
dotyczy to każdego ich rodzaju. Sensownie jest zacząć od polityki "zabroń
domyślnie" (tak jak mamy to teraz) i otwierać dostęp do tych usług, które mamy
zamiar udostępnić. Na przykład, jeżeli uruchamiamy serwer WWW, dopuścimy
pakiety w stanie NEW do naszej maszyny, ale tylko skierowane na port 80 (HTTP).
I to wszystko, co potrzebujemy ustawić. Skoro tylko wpuścimy pakiety w stanie
NEW, pozwoliliśmy na nawiązanie połączenia. Skoro połączenie zostało nawiązane,
włączyła się nasza działająca reguła wpuszczająca pakiety stanu RELATED i
ESTABLISHED i pozwala połączeniu HTTP działać bez przeszkód.
Przykład pełnostanowego HTTP
Przyjrzyjmy się "sercu" naszej zapory i nowej regule dopuszczającej
przychodzące połączenia HTTP:
Listing 6.3: Przykład pełnostanowego HTTP. |
iptables -P INPUT DROP
iptables -A INPUT -i ! ${UPLINK} -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport http -m state --state NEW -j ACCEPT
iptables -A INPUT -p tcp -i ${UPLINK} -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i ${UPLINK} -j REJECT --reject-with
icmp-port-unreachable
|
Nowa reguła pozwala przejść przez zaporę przychodzącym pakietom TCP w stanie
NEW skierowanym na port 80. Zwróćmy uwagę na położenie tej reguły. To bardzo
istotne, że pojawia się ona przed regułami REJECT. Skoro iptables uruchomi
pierwszą pasującą regułę, umieszczenie jej po linijce REJECT spowodowałoby, że
nie przyniosłaby żadnego efektu.
Nasz finalny skrypt firewalla
Teraz przyjrzyjmy się naszemu finalnemu skryptowi zapory, który może zostać
zastosowany na laptopie, stacji roboczej, routerze czy serwerze (lub
jakiejkolwiek ich kombinacji).
Listing 6.4: Nasz finalny skrypt firewalla |
#!/bin/bash
UPLINK="eth1"
ROUTER="yes"
NAT="1.2.3.4"
INTERFACES="lo eth0 eth1"
SERVICES="http ftp smtp ssh rsync"
if [ "$1" = "start" ]
then
echo "Starting firewall..."
iptables -P INPUT DROP
iptables -A INPUT -i ! ${UPLINK} -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
for x in ${SERVICES}
do
iptables -A INPUT -p tcp --dport ${x} -m state --state NEW -j ACCEPT
done
iptables -A INPUT -p tcp -i ${UPLINK} -j REJECT --reject-with tcp-reset
iptables -A INPUT -p udp -i ${UPLINK} -j REJECT --reject-with icmp-port-unreachable
if [ -e /proc/sys/net/ipv4/tcp_ecn ]
then
echo 0 > /proc/sys/net/ipv4/tcp_ecn
fi
for x in ${INTERFACES}
do
echo 1 > /proc/sys/net/ipv4/conf/${x}/rp_filter
done
if [ "$ROUTER" = "yes" ]
then
echo 1 > /proc/sys/net/ipv4/ip_forward
if [ "$NAT" = "dynamic" ]
then
echo "Enabling masquerading (dynamic ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j MASQUERADE
elif [ "$NAT" != "" ]
then
echo "Enabling SNAT (static ip)..."
iptables -t nat -A POSTROUTING -o ${UPLINK} -j SNAT --to ${UPIP}
fi
fi
elif [ "$1" = "stop" ]
then
echo "Stopping firewall..."
iptables -F INPUT
iptables -P INPUT ACCEPT
iptables -t nat -F POSTROUTING
fi
|
7.
Budowanie lepszego serwera zapory ogniowej
Ulepszenia serwera
Często można uczynić zaporę odrobinkę "lepszą". Oczywiście, "lepszą" zależnie
od indywidualnych potrzeb. Nasz działający skrypt może im w pełni odpowiadać,
lub wymagać dodatkowego "podrasowania". Ta sekcja to trochę "książka kucharska"
pełna pomysłów pokazujących sposoby ulepszania naszego firewalla
pełnostanowego.
Techniki zapisywania do logów
Na razie nie omawialiśmy w ogóle kwestii zapisywania do logów. Mamy specjalny
obiekt o nazwie LOG, którego można do tego użyć. Do niego należy specjalna
opcja --log-prefix, która pozwala określić brzmienie tekstu, który
pojawi się obok obrazu pakietu w logach systemowych. Poniżej zamieszczam
przykład regułki logowania:
Listing 7.1: Przykład reguły logowania. |
# iptables -A INPUT -j LOG --log-prefix "bad input:"
|
Nie chcielibyśmy jej dodać jako pierwszej regułki łańcucha INPUT, ponieważ
spowodowałoby to zapisywanie do logów każdego pakietu, który byśmy otrzymali!
Zamiast tego należy ją umieścić na końcu łańcucha INPUT z intencją logowania
dziwnych pakietów i innych anomalii.
Istotna uwaga na temat obiektu LOG. Normalnie, kiedy pakiet pasuje do reguły,
może zostać zaakceptowany, odrzucony lub upuszczony, a żadne następne reguły
nie zostaną uruchomione. Jeżeli będzie pasował do reguły logowania, zostanie to
zapisane do logów, chociaż nie został zaakceptowany, odrzucony ani upuszczony.
Zamiast tego pakiet podąża do następnej reguły lub zostaje zastosowana
domyślna polityka, o ile reguła logowania jest ostatnią w łańcuchu.
Obiekt LOG może również być połączony z modułem "limit" (opisanym na stronie
man iptables), aby zminimalizować duplikowanie wpisów do logów. Poniżej
przykład:
Listing 7.2: Limitowanie wpisów do logów. |
# iptables -A INPUT -m state --state INVALID -m limit --limit 5/minute -j LOG --log-prefix "INVALID STATE:"
|
Tworzenie własnych łańcuchów
iptables pozwala definiować własne łańcuchy, które mogą zostać zaliczone
do obiektów w regułach. Ci, którzy chcą opanować tę sztukę powinni spędzić
trochę czasu przekopując się przez Packet filtering HOWTO na stronie projektu
netfilter/iptables (http://www.netfilter.org/).
Wprowadzanie w życie polityki korzystania z sieci
Zapory ogniowe dają dużo władzy tym, którzy chcą wdrożyć politykę korzystania z
sieci korporacyjnej czy akademickiej. Można kontrolować, które pakiety nasza
maszyna przekaże dalej poprzez dodawanie reguł i ustawianie polityki dla
łańcucha FORWARD. Dzięki dodaniu reguły do łańcucha OUTPUT można kontrolować,
co się stanie z pakietami, które zostały stworzone lokalnie, przez użytkowników
naszej maszyny linuksowej. iptables posiada również niesamowite
możliwości filtrowania lokalnie wygenerowanych pakietów w oparciu o ich
użytkownika (uid lub gid). Aby znaleźć więcej informacji na ten temat,
przeszukaj strony man iptables ze słowem "owner".
Inne poglądy na bezpieczeństwo
Nasza przykładowa zapora ogniowa monitoruje uważnie wyłącznie ruch przychodzący
z Internetu, jako że założyliśmy, że cały ruch pochodzący z naszego LAN-u jest
w pełni zaufany. Zależnie od sieci, takie założenie może, ale nie musi być w
porządku. Nic nie stoi na przeszkodzie, abyśmy skonfigurowali nasz firewall do
ochrony przed ruchem pochodzącym z wnętrza LAN. Należy również rozważyć, czy
różne rejony sieci wymagają takiej samej ochrony. Mogłoby mieć sens
skonfigurowanie dwóch oddzielnych "stref" ochrony LAN, każdej z jej własną
polityką bezpieczeństwa.
8.
Zasoby
tcpdump
W tej sekcji wymienię kilka pomysłów, które można by uznać za pomocne przy
konstruowaniu naszego własnego pełnostanowego firewalla. Zacznijmy od ważnego
narzędzia...
tcpdump stanowi podstawowe narzędzie
do niskopoziomowego podglądu wymiany pakietów i weryfikowania czy nasza zapora
pracuje prawidłowo. Jeżeli tego nie macie, musicie to zainstalować. Jeżeli już
to macie, zacznijcie używać.
netfilter.kernelnotes.org
Zajrzyjcie na stronę projektu netfilter/iptables
(http://www.netfilter.org). Znajdziecie tam wiele różnych rzeczy, w
tym źródła iptables i
netfilter FAQ. Także Rusty's
Remarkably Guides stanowią doskonałe HOWTO, zawierające podstawowe
koncepcje pracy w sieci, netfilter (iptables) HOWTO, NAT HOWTO, netfilter
hacking HOWTO dla programistów i deweloperów.
Strona man iptables
W sieci dostępna jest duża ilość różnych materiałów na temat filtrowania
pakietów, ale nie należy zapominać o tych podstawowych. Strona man iptables
jest bardzo szczegółowym i błyskotliwym przykładem na to, jak powinna wyglądać
strona man. I świetnie się ją czyta.
Advanced Linux routing and traffic control HOWTO
Warto zajrzeć też do Advanced Linux
Routing and Traffic Control HOWTO. Można znaleźć tam rozdział o tym, jak
użyć iptables do znakowania pakietów, a następnie funkcjonalności routera Linux
do trasowania pakietów na podstawie tych znaczników.
Uwaga:
To HOWTO zawiera odwołania do funkcjonalności Linuksa zwanej kontrola ruchu
(traffic control) czyli quality od service (dostępnej przez nową komendę
tc). Ta nowa funkcja, pomimo że bardzo interesująca, na razie nie jest
dobrze udokumentowana. Próba ustalenia wszystkich aspektów kontroli ruchu w
Linuksie może się stać dosyć frustrującym zadaniem.
|
Listy mailingowe
Użytkownicy z pytaniami na temat użytkowania netfilter/iptables, jego ustawień
i konfiguracji mogą uzyskać pomost na liście mailingowej
użytkowników netfilter. Lista jest także dla tych, którzy chcieliby
podzielić się swoim doświadczeniem i wiedzą.
Pytanie, sugestie lub pomoc przy chęć pomocy w rozwijaniu netfilter/iptables,
można kierować na listę mailingową
użytkowników netfilter.
Możecie również przeglądać archiwa tych list, znajdują się one pod tymi samymi
adresami.
Building Internet Firewalls, Second Edition - Budowanie internetowych zapór ogniowych - edycja druga
W lipcu 2000 roku wydawnictwo O'Reilly opublikowało znakomitą książkę -- Building Internet Firewalls,
Second Edition. Stanowi ona doskonały podręcznik, szczególnie w momencie,
kiedy chcemy skonfigurować nasz firewall, aby akceptował (lub całkowicie
odrzucał) mało znany protokół, z którym nie jesteśmy "na ty".
Cóż, to by było na tyle, jeśli chodzi o listę pomocy. Kończy się również nasz
przewodnik. Mam nadzieję, że będzie on użyteczny. Proszę o wszelkie opinie na
jego temat.
Wasze uwagi
Wszystkie uwagi na temat tego przewodnika należy kierować do autora, Daniela
Robbinsa na adres Daniel Robbins.
|