iptables - przystępnie

Spis treści

  1. Wprowadzenie
    1. Informacje ogólne. Czym jest, a czym nie jest iptables?
    2. Możliwości. Do czego służy?
  2. Składnia polecenia
    1. Objaśnienie składni. Łańcuch? Tabela? Reguła?
    2. Tabele. I która służy do czego.
    3. Pojawia się pakiet. Co możemy z nim zrobić?
  3. Dopasowywanie pakietów.
    1. Na podstawie źródła i celu. Skąd? Dokąd?
    2. Na podstawie właściwości pakietu.
  4. Zastosowania praktyczne
    1. Jak pisać rc.firewall. Czyli własny skrypt startowy.
    2. Firewall dla Linuksa. Jak zablokować niektóre usługi.
    3. Routing i maskarada. Linux jako router.

Wprowadzenie.

Informacje ogólne.

Narzędzie iptables służy do tworzenia, usuwania i sprawdzania właściwości tabel zarządzania pakietami sieciowymi IPV4 w systemach operacyjnych rodziny Linux/Unix/BSD. Sam w sobie natomiast nie jest firewallem lub daemonem. Pakiety routowane i filtrowane są z poziomu jądra(kernela) systemu.

Program iptables zostaje więc wywołany z poziomu skryptu lub linii poleceń, ustawia, kasuje lub listuje (w zależności od parametrów wywołania) daną regułę postępowania z pakietami i kończy działanie.

Możliwości.

Skoro sam iptables nie jest odpowiedzialny za postępowanie z pakietami IPV4 jego możliwości zależą w pewnej części od wersji jądra systemu operacyjnego. Jeśli jakaś właściwość jest nieobsługiwana przez dane jądro - update iptables nic nie da i póki nie zmienimy wersji jądra wprowadzona przez iptables reguła postępowania może zostać zignorowana lub uznana za błędną.

Przy pomocy iptables możemy poinstruować kernela jak zachować się w przypadku otrzymania lub wygenerowania pakietu IPV4 dostosowanego do danej reguły. Reguła ta dotyczyć może właściwości pakietu takich jak protokół, adres z którego pakiet został otrzymany lub do którego pakiet zmierza, użytkownika w systemie, który wygenerował pakiet, źródło pakietu i wielu innych. Za pomocą iptables decydujemy jak potraktować dany pakiet, możemy go całkowicie zignorować, przesłać dalej, przesłać zmieniając jego właściwości takie jak TTL lub adres docelowy, a nawet zarejestorwać jego wystąpienie przy pomocy dmesg lub syslogd.

Składnia polecenia.

Objaśnienie składni.

iptables przyjmuje kilka składni. Są to:

W powyżej użytej składni występuje często kilka nic nie mówiących słów - czas dowiedzieć się co też one oznaczają.

TABELA - jest to za każdym razem nazwa jednej z czterech wbudowanych tabel. Te tabele to: filter, nat, mangle oraz raw. Jeśli parametru [-t] nie podamy domyślnie polecenie dotyczyło będzie tabeli filter.

ŁAŃCUCH - jest to nazwa stworzonego przez nas lub wbudowanego w tabelę łańcucha. Wbudowane łańcuchy odnoszą się automatycznie do pewnych pakietów, na przykład łańcuch INPUT w tabeli filter stosuje się automatycznie do wszystkich pakietów, których celem jest nasz host.

REGUŁA - a właściwie WŁAŚCIWOŚCI_REGUŁY - służą do opisu własności pakietów. w przypadku pojawienia się pakietu w danym łańcuchu jest on porównywany z daną regułą i jeśli ją spełnia - trafia do CELU (opcja -j CEL), a jeśli nie porównywana jest z następną regułą aż do końca łańcucha. Ostatecznie, jeśli nie zostanie skierowany przez żadną regułę do innego CELU - zostaje portaktowany zgodnie z domyślną polityką (opcja -P) łańcucha.

CEL - celem łańcucha może być:

Istnieją również CELE rozszerzone, które dostarczane są jako moduły (i nie zawsze są dostępne po standardowej instalacji). Są to:

OPCJE - używane zazwyczaj dla dostosowania informacji wyświetlanych na konsoli podczas wywołania iptables. Najczęściej używane to:

Tabele.

Tabela filter

Zawiera wbudowane łańcuchy:

Tabela nat

Trafiają do niej pakiety nawiązujące nowe połączenia. Zawiera wbudowane łańcuchy:

Tabela mangle

Zawiera wbudowane łańcuchy:

Tabela raw

Używana przed wszystkimi innymi tablicami. Zawiera następujące łańcuchy:

Pojawia się pakiet.

Pakiet może "pojawić się" i w zależności od sposobu w jaki się pojawił odbędzie swoją drogę przez różne łańcuchy różnych tablic. W tych łańcuchach możemy na podstawie dopasowania pakietu do reguł zmodyfikować go lub odrzucić. Najpierw warto wiedzieć przez które łańcuchy przechodzi jak wygenerowany pakiet:

Dopasowanie pakietów.

No podstawie źródła i celu.

Najprostszą metodą postępowania z pakietami jest DROP, czyli odrzucenie pakietu, dlatego właśnie DROP będziemy używali w naszych pierwszych przykładach. Domyślną metodą postępowania z pakietami jest ACCEPT, dlatego też DROP jako jego przeciwieństwo wydaje się najodpowiedniejszy.

Powiedzmy, że z naszego lokalnego komputera chcemy zablokować wszelki dostęp na komputer o adresie 10.0.0.2. Użyjemy na pewno łańcucha OUTPUT, bo chodzi nam o pakiety generowane lokalnie.

Posłużymy się również opcją pozwalającą na przeskoczenie od razu do celu. Opcja ta to -j CEL. Opcja -j oszczędza wiele pracy przy pisaniu prostych reguł firewalla. Polecenie będzie miało następującą postać:

iptables -A OUTPUT -d 10.0.0.2 -j DROP

Tłumaczenie: -A to dodanie reguły, OUTPUT to łańcuch pakietów stworzonych lokalnie, -d 10.0.0.2 to właściwości reguły a -j DROP to opcje. Parametr -d jak łatwo się domyślić oznacza cel pakietu i przyjmuje wartość adresu IP (jak powyżej), domeny lub adresu sieci (w postaci skróconej, na przykład 10.0.0.0/24).

Parametr -d można też odwrócić znakiem wykrzyknika:

iptables -A OUTPUT -d ! 10.0.0.2 -j DROP

Tym razem reguła dotyczyła będzie wszystkich lokalnie utworzonych pakietów, których przeznaczeniem nie jest 10.0.0.2.

Sytuacja blokowania (zwłaszcza na własnym komputerze) dostępu do jakiegoś hosta rzadko się jednak zdarza. Zazwyczaj zależy nam na zablokowaniu dostępu do naszego komputera lokalnego lub naszej sieci "z zewnątrz". najprostszy przykład polegać może na zablokowaniu dostępu do naszego komputera z określonego innego komputera. Powiedzmy, że chcemy aby nasz komputer był niedostępny dla sieci 192.168.0.0/8:

iptables -A INPUT -s 192.168.0.0/8 -j DROP

Tym razem używamy łańcucha INPUT, gdyż dotyczy on pakietów, których celem jest nasz komputer. Reguła -s oznacza "źródło pakietu", czyli adres IP, domenę lub sieć z którego pakiet został nadany.

Podobnie jak w przypadku -d możemy odwrócić źródło przy pomocy wykrzyknika - będziemy wtedy dostępni tylko i wyłącznie dla sieci 192.168.0.0/8.

Źródło i cel pakietu to jednak nie tylko adres IP maszyny. W niektórych wypadkach chcemy zablokować dostęp tylko do niektórych usług na naszym serwerze, a każda odpalona usługa sieciowa(zwana pod Linuksem zazwyczaj daemonem) nasłuchuje na jakimś porcie. Na przykład jeśli mamy uruchomionego Apache'a lub inny serwer HTTP nasłuchuje on na połączenia domyślnie na porcie 80. Serwery FTP to port 21, serwery SSH to port 22, MySQL - 3306 itd.

Chcemy udostępnić światu port 80 (naszą stronę www), ale nie chcemy by dowiedzieli się oni, że korzystamy na przykład z bazy MySQL lub serwera FTP:

iptables -A INPUT --protocol tcp --destination-port ! 80 -j DROP

Opcja dopasowania pakietu --protocol tcp zostanie objaśniona później. Na razie wystarczy wiedza, że --destination-port wymaga podania protokołu tcp lub udp.

Powyższa linia odrzuca wszystkie połączenia TCP poza tymi, które skierowane są na PORT 80. Tym razem nie podawaliśmy adresu z którego pochodzi pakiet, więc reguła dotyczy pakietów niezależnie od ich źródła.

W powyższym przykładzie pokazaliśmy utworzenie reguły za pomocą dwóch reguł: tylko pakiety spełniające tą zależność, że są pakietami tcp oraz tą, że nie są skierowane na port 80 zostaną przekierowane do CELU tej reguły.

Oczywiście można w ten sposób złożyć dowolną regułę składającą się z szeregu małych, o ile nie wykluczają się one wzajemnie.

Na podstawie właściwości pakietu.

Właściwości pakietu są bardziej złożone niż tylko jego źródło i cel. Możemy się posłużyć tymi właściwościami aby odkryć zamiary przychodzących pakietów i oddzielić te pożądane od mogących szkodzić. Możemy tworzyć reguły łapiące pakiety danego protokołu (opcja --protocol przyjmująca parametry: icmp, tcp, udp, all lub dowolny protokół/numer protokołu z pliku /etc/protocols), pakiet przychodzący z danego interfejsu sieciowego (opcja -i). Więcej w manualu w dziale MATCH EXTENSIONS i przykładach.

Zastosowania praktyczne.

Jak pisać rc.firewall.

Jako że chcemy aby rc.firewall pełniło rolę skryptu startowego - nie możemy odwoływać się do komendy iptables bez podania ścieżki - możemy ją sprawdzić komendą whereis iptables. Najczęściej ścieżka do niej to /sbin/iptables. Podczas samego pisania mogą przydać się dwie dodatkowe komendy:

Inny program mogący się przydać podczas sprawdzania naszego skryptu to nmap sprawdzający które porty na danej maszynie są otwarte (a co za tym idzie - które usługi są udostępnione).

Wszelkie parametry warto umieszczać w zmiennych zdefiniowanych na początku naszego skryptu - tak by nie napracować się zbytnio w wypadku potrzeby zmiany konfiguracji naszego firewalla.

Firewall dla Linuksa.

Domyślną polityką kernela jest ACCEPT dla wszystkich pakietów w tabeli filter w łańcuchach FORWARD, INPUT i OUTPUT. O ile ACCEPT wpisany w łańcuch OUTPUT jest nam bardzo na rękę (nie moglibyśmy nawiązywać połączeń, gdyby było inaczej), o tyle dwa pozostałe łańcuchy otwierają nasze usługi dla reszty świata, a nie zawsze tego chcemy. Ustawimy domyślną politykę w tych dwóch łańcuchach na DROP (odrzucenie pakietów).

iptables -P FORWARD DROP
iptables -P INPUT DROP

Te dwie linijki tworzą już całkiem niezły firewall - nie odpowiadamy na pingi, nikt za pomocą programu nmap lub podobnego nie zobaczy jakie porty są u nas otwarte.

Nasze usługi zostały ukryte - czas otworzyć kilka portów dla wybranych usług, na przykład dostęp do SSH (port 22) i HTTP (port 80). Zrobimy to w podany sposób:

iptables -A INPUT --protocol tcp --destination-port 22 -j ACCEPT
iptables -A INPUT --protocol tcp --destination-port 80 -j ACCEPT

Pamiętamy, że domyślna polityka łańcucha (deklarowana opcją -P) stosowana jest dopiero jeśli pakiet nie został przez żadną inną regułę skierowany do innego CELU. Aby lepiej to zrozumieć posłużmy się dwoma przykładami dla podanej wyżej polityki i dwóch powyższych reguł:

Przykład I: Inny komputer próbuje nawiązać połączenie z naszym komputerem na porcie 21 (domyślnie jest to port FTP):

  1. Pakiet tcp wpada do łańcucha INPUT tabeli filter
  2. Tam porównywany jest kolejno z pierwszą regułą: --protocol tcp --destination-port 22, której nie spełnia, następnie z drugą --protocol tcp --destination-port 80, której również nie spełnia.
  3. Skoro nie spełnił żadnej z reguł zastosowana zostanie domyślna polityka łańcucha, jest to zdefiniowany przez nas DROP. Nawet jeśli więc na lokalnym komputerze mamy otwarty port 21 połączenie zostanie odrzucone.

Przykład II: Inny komputer próbuje nawiązać połączenie z naszym komputerem na porcie 80 (domyślnie jest to port HTTP)

  1. Pakiet tcp wpada do łańcucha INPUT tabeli filter
  2. Tam porównywany jest kolejno z pierwszą regułą: --protocol tcp --destination-port 22, której nie spełnia, następnie z drugą --protocol tcp --destination-port 80, którą spełnia, więc ...
  3. ... nastąpi zdefiniowany przez nas skok -j ACCEPT, pakiet zostanie zaakceptowany i jego porównywanie z kolejnymi regułami nie będzie wykonywane.

Czas ująć powyższe reguły w łatwy w konfiguracji skrypt.

#!/bin/bash
#deklaracja zmiennych
IPTABLES=/sbin/iptables #ścieżka do iptables
NO_ROUTING=1 #czy forwardować pakiety?

#dla poniższych argumentów 1 udostępnia usługę,
#każda inna wartość zabrania do niej dostępu

OPEN_FTP=0  #czy udostępnić usługę FTP
OPEN_SSH=1  #czy udostępnić usługę SSH
OPEN_HTTP=1 #czy udostępnić usługę HTTP

#czyścimy tablicę z poprzednich reguł
$IPTABLES -F
#ustawiamy domyślną politykę
$IPTABLES -P INPUT DROP
if [ "$NO_ROUTING" = "1" ]; then
    $IPTABLES -P FORWARD DROP
fi

if [ "$OPEN_FTP" = "1" ]; then
    $IPTABLES -A INPUT --protocol tcp --destination-port 21 -j ACCEPT
fi

if [ "$OPEN_SSH" = "1" ]; then
    $IPTABLES -A INPUT --protocol tcp --destination-port 22 -j ACCEPT
fi

if [ "$OPEN_HTTP" = "1" ]; then
    $IPTABLES -A INPUT --protocol tcp --destination-port 80 -j ACCEPT
fi
#otwieramy jeszcze drogę powrotną pakietom FTP, HTTP, SSH ...
$IPTABLES -A INPUT --protocol tcp --source-port 21 -j ACCEPT
$IPTABLES -A INPUT --protocol tcp --source-port 80 -j ACCEPT
$IPTABLES -A INPUT --protocol tcp --source-port 8080 -j ACCEPT
$IPTABLES -A INPUT --protocol tcp --source-port 22 -j ACCEPT
#koniecznie otwórzmy drogę do DNS-a
$IPTABLES -A INPUT --protocol udp --source-port 53 -j ACCEPT
#i komunikatorowi gg
$IPTABLES -A INPUT --protocol tcp --source-port 8074 -j ACCEPT

Lepiej jest oczywiście rozwinąć listę opcji o dodatkowe usługi (nawet jeśli w momencie pisania skryptu nie planujemy ich uruchamiania) - dla większej wygody w przyszłości. Na przykład można zadeklarować porty 8080 (HTTP-PROXY), 1550 (używany przy wymianie plików przez gadu-gadu), 3306 (domyślny port MySQL). Powyższy skrypt można jednak uznać za bazowy dla dalszych modyfikacji.

W niektórych przypadkach chcemy jednak zablokować pewnym nieproszonym gościom dostęp do wszystkich usług uruchomionych na naszym komputerze. Można dodać do naszego skryptu kilka linijek, które skutecznie będą powstrzymywać wybranych intruzów przed atakami:

#poniższa lista to lista hostów całkowicie zablokowanych
BLOCKED="192.168.4.112 200.212.8.32 maciek.com"
for adres in $BLOCKED
do
    iptables -I INPUT 1 -s $adres -j DROP
done

Ważne w przypadku tak zbudowanego firewalla jest, by te reguły znalazły się na początku łańcucha (były przetwarzane jako pierwsze), dlatego używamy opcji -I. Inaczej pakiet wysłany z takiego zablokowanego adresu byłby akceptowany przez pierwsze w naszym firewallu reguły i nie byłby sprawdzany pod kątem pochodzenia.

Routing i maskarada.

Jak dotąd używaliśmy iptables do ograniczania dostępu do pewnych usług na naszym komputerze. Ciekawszym i bardziej zaawansowanym zastosowaniem programu jest tzw. ROUTING, czyli przekierowywanie pakietów w obrębie kilku hostów lub sieci.

O ile przy tworzeniu firewalla interesowały nas głównie pakiety skierowane bezpośrednio do naszego komputera (łańcuch INPUT) o tyle przy konfiguracji naszego routera będziemy zajmowali się pakietami trafiającymi do łańcuchów PREROUTING, FORWARD i POSTROUTING, czyli takich, które docierają do naszego komputera, ale nie są przeznaczone bezpośrednio dla niego. Najprostszą taką sytuacją jest mała sieć domowa złożona z komputera z Linuksem (podłączonego bezpośrednio do internetu) [nazywać go będziemy Routerem] oraz drugiego komputera podłączonego do Routera przez inny jego interfejs sieciowy.

Router ma za zadanie NAT-ować połączenie, czyli udostępniać internet drugiej maszynie. Aby to osiągnąć włączone musi być przekazywanie pakietów IP:

echo "1" > /proc/sys/net/ipv4/ip_forward

Następnie iptables - interesują nas takie pakiety, które nawizują nowe połączenia, i to tylko te z adresu naszego drugiego komputera. Chcemy zmodyfikować nagłówek IP, a konkretnie informację o pochodzeniu tego pakietu podając adres naszego Routera zamiast adresu drugiego komputera.

Pakiety nawiązujące nowe połączenie to tablica nat - jeśli wartość nagłówka zostanie zmieniona w tej tablicy, to zostanie też zmieniona we wszystkich pozostałych nagłówkach pakietów tego połączenia. Łańcuch w którym zmienimy tą wartość to POSTROUTING czyli tuż przed opuszczeniem przez pakiet naszego Routera, pozostaje tylko kwestia dobrania CELU pakietu.

Mamy dwie możliwości sugerowane przez manual iptables: pierwszą jest MASQUERADE i powinno się jej używać jeśli nasz Router jest podłączony przez modem z zmiennie przydzielanym adresem IP, drugim rozwiązaniem jest SNAT - tego CELU powinniśmy używać, jeśli mamy stałe IP na Routerze. Odpowiednie linie dla obu przypadków to:

iptables -t nat -A POSTROUTING -s 10.0.0.2 -d 0.0.0.0/0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.0.0.2 -d 0.0.0.0/0 -j SNAT --to-source nasz_stały_adres_ip

adres 0.0.0.0/0 podany jako przeznaczenie pakietu oznacza w praktyce jakikolwiek adres IP

I już na drugim komputerze możemy cieszyć się dostępem do internetu.

Oczywiście niewielka modyfikacja wystarczy, aby przez nasz Router łączył się nie jeden komputer a cała sieć osiedlowa:

iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -d 0.0.0.0/0 -j MASQUERADE

Zarządzając taką siecią osiedlową może się zdarzyć, że któryś z jej użytkowników zechce "udostępnić światu" niektóre usługi oferowane przez jego komputer, na przykład zechce mieć dostęp do plików umieszczonych na HTTP komputera domowego z pracy. Oczywiście możemy przekierować odpowiedni port Routera (widzianego z zewnątrz) na inny port komputera wewnątrz sieci (niewidzianego z zewnątrz). Nie zapominamy przy tym, że jeśli podajemy numery portów musimy też podać protokół - tcp lub udp. Zmian dokonujemy w łańcuchu PREROUTING tabeli nat, a CELEM pakietu będzie DNAT z opcją pozwalającą przekierować go na dowolny inny adres i port.

iptables -t nat -I PREROUTING 1 --protocol tcp --destination-port 16080 -j DNAT --to-destination 10.0.0.38:80

Jak łatwo się domyślić 10.0.0.38:80 to adres usługi na komputerze wewnątrz sieci a 16080 to adres na którym tę usługę udostępniamy za pomocą Routera.