iptables to filtr pakietów dla systemu operacyjnego GNU/Linux używanym głownie jako router lub firewall, umożliwia definiowanie tabel zawierających łańcuchy reguł stosowanych dla pakietów. Każda z tabel służy do przetwarzania pakietów różnego rodzaju i zawiera kilka łańcuchów.
Domyśle tabele i łańcuchy iptables
- filter – domyślna tabela w której najczęściej tworzy się reguły. W niniejszym przykładzie właśnie w niej będziemy tworzyć nasze reguły. Tabela ta zawiera 3 domyślne łańcuchy:
- INPUT – obsługuje pakiety przychodzące z zewnątrz przeznaczone dla lokalnego hosta
- FORWARD – obsługuje pakiety routowane przez hosta pomiędzy interfejsami sieciowymi, czyli przechodzące przez hosta i przekazywane dalej
- OUTPUT – obsługuje pakiety generowanie lokalnie przez hosta na którym pracuje netfilter
- nat – tabela stosowana do pakietów, które nawiązują nowe połączenie, zawiera 3 domyślne łańcuchy:
- PREROUTING – służy do modyfikowania pakietów przychodzących od razu gdy przyjdą jeszcze przed ich dalszym filtrowaniem)
- OUTPUT – służy do modyfikowania pakietów generowanych lokalnie przez hosta przed ich routowaniem
- POSTROUTING – służy do modyfikowania pakietów, które mają wyjść, tuż przed ich wysłaniem
- mangle – tabela stosowana do wyspecjalizowanych zmian w pakietach, zawiera 5 domyślnych łańcuchów:
- PREROUTING – służy do modyfikowania pakietów przychodzących zanim zostaną rutowane
- OUTPUT – służy do modyfikowania pakietów generowanych lokalnie przez hosta przed ich routowaniem
- INPUT – służy do modyfikowania pakietów przychodzących z zewnątrz przeznaczonych do lokalnego hosta
- FORWARD – służy do modyfikowania pakietów routowanych przez hosta (pomiędzy interfejsami sieciowymi)
- POSTROUTING – służy do modyfikowania pakietów po routingu, tuż przed ich wysłaniem
- raw – do tej tabeli pakiety trafiają najpierw – ma ona najwyższy priorytet
- PREROUTING – pakiety przychodzące przez jakikolwiek interfejs sieciowy
- OUTPUT – pakiety generowane przez lokalne procesy
Akcje, operacje i opcje iptables
Pakiet trafia więc do tabeli tam natrafia na nasze reguły, które mogą skierować go do łańcucha zdefiniowanego przez użytkownika lub do jednej z wbudowanych akcji, w poniższym przykładzie wykorzystane zostanie tylko 5 z nich:
- ACCEPT – oznacza zaakceptowanie pakietu
- DROP – oznacza porzucenie pakietu
- REJECT – oznacza odrzucenie pakietu z powiadomieniem nadawcy o odrzuceniu
- RETURN – oznacza skierowanie z powrotem pakietu do łańcucha z którego przyszedł
- LOG – oznacza zapisanie informacji o pakiecie do /var/log/syslog
Z kolei podstawowe operacje na łańcuchach, wykorzystane w poniższym przykładzie to:
-F
– wyczyszczenie reguł z łańcucha-X
– skasowanie pustego łańcucha-P
– ustawienie domyślnej polityki dla łańcucha-A
– dodanie nowej reguły na końcu łańcucha-L
– listowanie reguł w łańcuchu
Podstawowa wersja firewalla opartego na iptables
Przedstawiona powyżej wiedza pozwala nam na napisanie podstawowej wersji firewalla, która naprawdę stanowi dobre zabezpieczenie:
1 2 3 4 5 6 7 8 9 10 11 |
iptables -F iptables -X iptables -F -t nat iptables -X -t nat iptables -F -t filter iptables -X -t filter iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT |
Pierwsze 6 reguł czyści nam naszego firewalla. Kolejne 3 ustalają domyślną politykę. Zakładam, że firewalla piszemy dla stacji roboczej, więc blokujemy natomiast pakiety przychodzące do naszego hosta przeznaczone dla niego lub do dalszego routowania. Natomiast pakiety wygenerowane lokalnie przez hosta i wychodzące na zewnątrz akceptujemy. Kolejna reguła to akceptacja lokalnego ruchu wchodzącego (w obrębie naszego hosta), jest to potrzebne do działania wielu lokalnych usług klient – serwer. Ostatnia reguła pozwala na połączenia już istniejące albo powiązane z już istniejącymi, jest to konieczne aby w ogóle nadal korzystać z internetu.
Wykorzystane powyżej przełączniki to:
-t
– wybiera tablicę w której dokonane mają być zmiany-i
– określa interfejs sieciowy, którym pakiet przyszedł (eth0, eth1, lo)-j
– określa łańcuch (akcję) do którego ma trafić pakiet-m
– wczytuje określony moduł
W powyższym przypadku wczytany zostaje moduł state, pozwalający na sprawdzenie stanu pakietu przy pomocy przełącznika –state. Stany mogą być następujące:
- NEW – pakiety, które tworzą nowe połączenie
- ESTABLISHED – pakiety, które należą do już nawiązanego połączenia
- RELATED – pakiety tworzące nowe połączenie związane z już istniejącym połączeniem
- INVALID – pakiety uszkodzone
Zaprzeczenie tworzymy poprzez poprzedzenie stanu wykrzyknikiem, przykładowo jeśli chcemy wpuścić wszystkie pakiety poza uszkodzonymi: --state ! INVALID
Ident
Wiele usług, jak na przykład SMTP czy FTP, przed połączeniem z klientem, próbuje się połączyć z usługą ident, jako że zgodnie z naszą domyślną polityką dla pakietów przychodzących pakiet taki zostaje porzucony, następuje kilkusekundowe opóźnienie, które jakkolwiek nie przeszkadza w komunikacji to jednak ją opóźnia, w związku z czym aby tego uniknąć należy poinformować serwer o odrzuceniu pakietu:
iptables -A INPUT -p tcp --dport 113 -j REJECT --reject-with icmp-port-unreachable
Nowe przełączniki to:
-p
– określa protokół sieciowy (tcp, udp, icmp)--dport
– określa port docelowy
Ping
Aby zabezpieczyć się przed atakiem Ping of death proponuję:
1 2 |
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j LOG --log-prefix "Ping: " iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT # Ping of death |
Pierwsza reguła zapisze kto pingował naszego hosta, druga odpowie na pinga z częstotliwością 1 na sekundę. Jeśli chcemy całkowicie wyłączyć odpowiadanie na pingi, oto bardziej paranoiczna wersja ostatniej reguły:
1 |
iptables -A INPUT -p icmp --icmp-type echo-request -j REJECT --reject-with icmp-host-unreachable |
Nowe przełączniki to:
--icmp-type
– pozwala na wybranie typu pakietów icmp które mają być poddane sprawdzeniu (echo-request, destination-unreachable, echo-reply)--limit
– określa limit przepuszczanych w jednostce czasu pakietów (wymagany moduł limit)--log-prefix
– powoduje dopisanie jakiegoś prefiksu do loga, co powoduje że staje się ona bardziej czytelny dla człowieka--reject-with
– powoduje odrzucenie danego pakietu z innym niż domyślnym pakietem icmp do wyboru
Programy peer-to-peer
Porty od 1024 wzwyż mogą być i są bindowane przez programy do komunikacji bezpośredniej np. Kadu, aby mogły one działać musimy więc te porty otworzyć na nowo przychodzące pakiety:
1 2 |
iptables -A INPUT -m state --state NEW -p tcp --dport 1024:65535 -j ACCEPT iptables -A INPUT -m state --state NEW -p udp --dport 1024:65535 -j ACCEPT |
Reguły te są możliwie najbardziej liberalne. Można oczywiście zawęzić zakres portów, ale wymaga to sprawdzenia dokumentacji każdego z używanych przez nas programów peer-to-peer.
Dostęp do serwerów
Jeśli mamy zainstalowane na naszym hoście jakieś serwery usług internetowych, czynimy podobnie jak w przypadku programów peer-to-peer, z tym że otwieramy konkretne porty, w poniższym przypadku są to porty: 22 (ssh), 80 (http) i 443 (https):
1 |
iptables -A INPUT -m multiport -p tcp --dports 22,80,443 -j ACCEPT |
Ochrona przed atakami
Do pełni szczęścia brakuje nam jeszcze tylko ochrony przed skanowaniem portów:
1 2 3 4 5 6 7 8 |
iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH ACK -j LOG --log-prefix "ACK scan: " iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH ACK -j DROP # Metoda ACK (nmap -sA) iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH FIN -j LOG --log-prefix "FIN scan: " iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH FIN -j DROP # Skanowanie FIN (nmap -sF) iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH PSH -j LOG --log-prefix "Xmas scan: " iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH FIN,URG,PSH -j DROP # Metoda Xmas Tree (nmap -sX) iptables -A INPUT -m conntrack --ctstate INVALID -p tcp ! --tcp-flags SYN,RST,ACK,FIN,PSH,URG SYN,RST,ACK,FIN,PSH,URG -j LOG --log-prefix "Null scan: " iptables -A INPUT -m conntrack --ctstate INVALID -p tcp ! --tcp-flags SYN,RST,ACK,FIN,PSH,URG SYN,RST,ACK,FIN,PSH,URG -j DROP # Skanowanie Null (nmap -sN) |
oraz przed atakiem SYN flood (denial-of-service):
1 2 3 4 5 |
iptables -N syn-flood iptables -A INPUT -p tcp --syn -j syn-flood iptables -A syn-flood -m limit --limit 1/s --limit-burst 4 -j RETURN iptables -A syn-flood -m limit --limit 1/s --limit-burst 4 -j LOG --log-prefix "SYN-flood: " iptables -A syn-flood -j DROP |
Nowe przełączniki to:
--ctstate
– określa stan pakietu (wymagany moduł conntrack)--tcp-flags
– pozwala na filtrowane na podstawie specyficznych flag TCP, za przełącznikiem może być znak ! oznaczający przeczenie, następnie łańcuch flag stanowiący maskę – listę flag do sprawdzenia, oraz kolejny łańcuch określający które flagi mają zostać ustawione.--syn
– skrót dla--tcp-flags SYN,RST,ACK SYN
--limit-burst
– określa maksymalną serię, po której określony limit się włącza
Instalacja skryptu firewalla opartego na iptables
Na koniec do naszego skryptu dodajmy możliwość wyłączenia firewalla. A oto gotowy skrypt do ściągnięcia:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#!/bin/bash # czyścimy wszystko iptables -F iptables -X iptables -F -t nat iptables -X -t nat iptables -F -t filter iptables -X -t filter if [ "$1" = "stop" ] then iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT exit fi # ustalamy domyślną politykę dla filtrów iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # pełny ruch na interfejsie lo (potrzebne do działania wielu lokalnych usług) iptables -A INPUT -i lo -j ACCEPT # odrzucamy ident iptables -A INPUT -p tcp --dport 113 -j REJECT --reject-with icmp-port-unreachable # ochrona przed atakami iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j LOG --log-prefix "Ping: " iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT # Ping of death # iptables -A INPUT -p icmp --icmp-type echo-request -j REJECT --reject-with icmp-host-unreachable iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH ACK -j LOG --log-prefix "ACK scan: " iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH ACK -j DROP # Metoda ACK (nmap -sA) iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH FIN -j LOG --log-prefix "FIN scan: " iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH FIN -j DROP # Skanowanie FIN (nmap -sF) iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH PSH -j LOG --log-prefix "Xmas scan: " iptables -A INPUT -m conntrack --ctstate NEW -p tcp --tcp-flags SYN,RST,ACK,FIN,URG,PSH FIN,URG,PSH -j DROP # Metoda Xmas Tree (nmap -sX) iptables -A INPUT -m conntrack --ctstate INVALID -p tcp ! --tcp-flags SYN,RST,ACK,FIN,PSH,URG SYN,RST,ACK,FIN,PSH,URG -j LOG --log-prefix "Null scan: " iptables -A INPUT -m conntrack --ctstate INVALID -p tcp ! --tcp-flags SYN,RST,ACK,FIN,PSH,URG SYN,RST,ACK,FIN,PSH,URG -j DROP # Skanowanie Null (nmap -sN) # Łańcuch syn-flood (obrona przed DoS) iptables -N syn-flood iptables -A INPUT -p tcp --syn -j syn-flood iptables -A syn-flood -m limit --limit 1/s --limit-burst 4 -j RETURN iptables -A syn-flood -m limit --limit 1/s --limit-burst 4 -j LOG --log-prefix "SYN-flood: " iptables -A syn-flood -j DROP # pozwalamy na wszystkie istniejące już połączenia oraz połączenia które są powiązane z istniejącymi już połączeniami iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # cały ruch powyżej portów 1024, to są porty które mogą być bindowane przez wszystko (chociażby np. Kadu) iptables -A INPUT -m state --state NEW -p tcp --dport 1024:65535 -j ACCEPT iptables -A INPUT -m state --state NEW -p udp --dport 1024:65535 -j ACCEPT # uruchomione serwery na portach poniżej 1024: ssh, http, https iptables -A INPUT -m state --state NEW -m multiport -p tcp --dports 22,80,443 -j ACCEPT # Samba iptables -A INPUT -m state --state NEW -m multiport -p udp --dports 137,138 --source 192.168.1.0/24 -j ACCEPT iptables -A INPUT -m state --state NEW -m multiport -p tcp --dports 139,445 --source 192.168.1.0/24 -j ACCEPT |
zapisujemy go, a następnie wykonujemy:
1 2 |
sudo cp firewall /etc/init.d/ sudo update-rc.d firewall defaults 90 |
co sprawi, że nasz firewall będzie uruchamiał się od razu po inicjalizacji kernela, ponadto od tej chwili możemy zatrzymać firewalla komendą:
1 |
sudo /etc/init.d/firewall stop |
i powtórnie uruchomić komendą:
1 |
sudo /etc/init.d/firewall |
natomiast sprawdzić stan firewalla możemy komendą:
1 |
sudo iptables -L |
Naszego firewalla możemy przetestować na stronie ShieldsUP!. Wybieramy skanowanie wszystkich portów. Powinniśmy uzyskać same zielone kwadraty.
Jeśli w sekcji dotyczącej pingowania wybraliśmy pierwszą regułę, to mimo wszystkich zielonych kwadratów serwis stwierdzi, że nie zdaliśmy testu (duży czerwony napis FAILED). Mamy dwa wyjścia, albo to olewamy albo stosujemy drugą z podanych reguł.