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.
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.
iptables przyjmuje kilka składni. Są to:
iptables [-t TABELA] -A ŁAŃCUCH WŁAŚCIWOŚCI_REGUŁY [OPCJE]iptables [-t TABELA] --append ŁAŃCUCH WŁAŚCIWOŚCI_REGUŁY [OPCJE]
iptables [-t TABELA] -D ŁAŃCUCH WŁAŚCIWOŚCI_REGUŁY [OPCJE]iptables [-t TABELA] --delete ŁAŃCUCH WŁAŚCIWOŚCI_REGUŁY [OPCJE]
iptables [-t TABELA] -D ŁAŃCUCH NUMER_REGUŁY [OPCJE]iptables [-t TABELA] --delete ŁAŃCUCH NUMER_REGUŁY [OPCJE]iptables [-t TABELA] -I ŁAŃCUCH [NUMER_REGUŁY] WŁAŚCIWOŚCI_REGUŁY [OPCJE]iptables [-t TABELA] --insert ŁAŃCUCH [NUMER_REGUŁY] WŁAŚCIWOŚCI_REGUŁY [OPCJE]
iptables [-t TABELA] -R ŁAŃCUCH NUMER_REGUŁY WŁAŚCIWOŚCI_REGUŁY [OPCJE]iptables [-t TABELA] --replace ŁAŃCUCH NUMER_REGUŁY WŁAŚCIWOŚCI_REGUŁY [OPCJE]
iptables [-t TABELA] -L [ŁAŃCUCH] [OPCJE]iptables [-t TABELA] --list [ŁAŃCUCH] [OPCJE]iptables [-t TABELA] -F [ŁAŃCUCH] [OPCJE]iptables [-t TABELA] --flush [ŁAŃCUCH] [OPCJE]iptables [-t TABELA] -Z [ŁAŃCUCH] [OPCJE]iptables [-t TABELA] --zero [ŁAŃCUCH] [OPCJE]iptables [-t TABELA] -N ŁAŃCUCHiptables [-t TABELA] --new-chain ŁAŃCUCH
iptables [-t TABELA] -X [ŁAŃCUCH]iptables [-t TABELA] --delete-chain [ŁAŃCUCH]iptables [-t TABELA] -E STARA_NAZWA_ŁAŃCUCHA NOWA_NAZWA_ŁAŃCUCHAiptables [-t TABELA] --rename-chain STARA_NAZWA_ŁAŃCUCHA NOWA_NAZWA_ŁAŃCUCHA
iptables [-t TABELA] -P ŁAŃCUCH CEL [OPCJE]iptables [-t TABELA] --policy ŁAŃCUCH CEL [OPCJE]iptables -h
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:
icmp odpowiada
wybranym typem odpowiedzi, przy innych protokołach działa jak DROPOPCJE - używane zazwyczaj dla dostosowania informacji wyświetlanych na konsoli podczas wywołania iptables. Najczęściej używane to:
-n - wyświetla (przy listowaniu przez -L
adresy IP hostów zamiast ich nazw (domyślnie iptables próbuje rozwiązać
nazwy i wyświetlić nazwy hostów).-v - przy listowaniu pokazuje ilość
wysłanych/odebranych przez dany łańcuch pakietów i bajtów oraz domyślną
politykę łańcucha.--line-numbers - wyświetla (przy listowaniu przez
-L) numery linii dla poszczególnych reguł.-x podaje dokładne dane przy listowaniu liczby
wysłanych/odebranych pakietów (domyślnie występiją zaokrąglenia).iptables -L -v -x-j CEL - bardzo przydatna opcja kierująca pakiety
spełniające podaną regułę od od razu do CELU.filterZawiera wbudowane łańcuchy:
INPUT - pakiety przeznaczone do odbioru przez
lokalny komputer.FORWARD - pakiety routowane poprzez lokalny komputer.OUTPUT - pakiety wygenerowane przez lokalny komputer.natTrafiają do niej pakiety nawiązujące nowe połączenia. Zawiera wbudowane łańcuchy:
PREROUTING - dla zmian w pakietach w momencie, kiedy
się pojawiają.OUTPUT - dla zmian w pakietach, które utworzone zostały
na lokalnym komputerze, przed ich routingiem.POSTROUTING - dla zmian w pakietach tuż przed ich
przesłaniem dalej.mangleZawiera wbudowane łańcuchy:
PREROUTING - dla zmian w przychodzących pakietach zanim
zostaną one routowane.OUTPUT - dla zmian w pakietach, które utworzone zostały
na lokalnym komputerze, przed ich routingiem.INPUT (od kernela 2.4.18) - dla pakietów zmierzających
do lokalnego komputera.FORWARD (od kernela 2.4.18) - dla pakietów będących
routowanymi przez lokalny komputer.POSTROUTING (od kernela 2.4.18) - dla pakietów po
procesie routowania, zanim opuszczą one lokalny komputer.rawUżywana przed wszystkimi innymi tablicami. Zawiera następujące łańcuchy:
PREROUTING - dla pakietów przychodzących przez
jakiekolwiek urządzenie sieciowe (w rozumieniu eth0, ppp0, lo itd.)OUTPUT - dla pakietów wygenerowanych przez lokalne
procesy.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:
OUTPUT tablicy rawOUTPUT tablicy mangleOUTPUT tablicy nat (tylko
jeśli jest pakietem nawiązującym nowe połączenie)OUTPUT tablicy filterPREROUTING tablicy rawINPUT tablicy mangleINPUT tablicy filterPREROUTING tablicy rawPREROUTING tablicy manglePREROUTING tablicy nat
(tylko pakiet tworzący nowe połączenie)FORWARD tablicy mangleFORWARD tablicy filterPOSTROUTING tablicy manglePOSTROUTING tablicy nat
(tylko pakiet tworzący nowe połączenie)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.
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.
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:
iptables-save - Wypisuje aktualną zawartość tablic na
konsolę. Będziemy to wyjście przekierowywać do pliku na przykład
iptables-save > /root/kopia_iptablesiptables-restore - Przyjmuje wartości tablic z konsoli.
Będziemy je ładowali z tego samego pliku, do którego robiliśmy kopię
tablic, na przykład iptables-restore < /root/kopia_iptables
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.
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):
INPUT tabeli filter--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.Przykład II: Inny komputer próbuje nawiązać połączenie z naszym komputerem na porcie 80 (domyślnie jest to port HTTP)
INPUT tabeli filter--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 ...-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.
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.