Správa linuxového serveru: Linuxový firewall, základy iptables II

V tomto dílu se budu věnovat stavovému filtrování paketů a vytvoření velmi jednoduchého, v praxi použitelného firewallu pomocí Netfiltru. Upozorním také na to, jaká existuje podpora Netfiltru pro IPv6 a jak firewall nastavit pro IPv4 i IPv6.

Stavový firewall

Jedna ze stěžejních výhod Netfilteru je schopnost rozlišovat, ke kterým existujícím spojením paket patří, pokud k nějakým takovým vůbec patří, a filtrovat jej na základě této příslušnosti. Pomocí této vlastnosti je možné pravidla pro filtrování paketů vztáhnout pouze na žádosti o vytvoření nového spojení, zatímco všechny pakety náležející již vytvořeným spojením rovnou propustit.Stavové filtrování zajišťuje modul state, který rozlišuje čtyři stavy:

·        NEW - paket vytváří nové spojení nebo se vztahuje ke spojení, kde dosud neproběhla obousměrná komunikace

·        ESTABLISHED - paket se vztahuje ke spojení, kde probíhá obousměrná komunikace (tedy již k vytvořenému spojení)

·        RELATED - paket vytváří nové spojení, ale vztahuje se k některému z existujících (např. u FTP)

·        INVALID - paket se nevztahuje k žádnému známému spojení (obvykle je to paket, který tu nemá co dělat, a proto je dobré ho rovnou zahazovat)

Samotný firewall využívající stavového filtrování může vypadat třeba takto:

1.  iptables -P INPUT DROP
2.  iptables -P OUTPUT ACCEPT
3.  iptables -P FORWARD DROP
4.  iptables -A INPUT -i lo -j ACCEPT
5.  iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
6.  iptables -A INPUT -m state --state INVALID -j DROP
7.  iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
8.  iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
9   iptables -A INPUT -p icmp -j ACCEPT
10. iptables -A INPUT -j REJECT --reject-with icmp-admin-prohibited

První tři řádky specifikují politiku (viz minulý díl), čtvrtý řádek povoluje všechna příchozí spojení z místní smyčky (local loopback). Toto pravidlo je nesmírně důležité, neboť bez možnosti síťové komunikace po místní smyčce nebude ledacos správně fungovat, včetně třeba X serveru). Je tedy třeba explicitně povolit provoz místní smyčky všude tam, kde je politika pro řetěz INPUT či OUTPUT nastavena na DROP. Jen pro úplnost, pro řetěz OUTPUT by příslušné pravidlo vypadalo takto:

iptables -A OUTPUT -o lo -j ACCEPT

Pátý řádek využívá modulu state a propouští všechny pakety, které náleží již vytvořeným spojením (ESTABLISHED) nebo novým spojením, která k nim patří (RELATED).Šestý řádek zahazuje "neplatné" (INVALID) pakety, tedy pakety, které nepatří k žádným spojením. Zahazovat takové pakety je obvykle v pořádku, jen je třeba zmínit, že sem mohou patřit i pakety, o kterých systém pro analýzu stavů (connection tracking aneb conntrack) ztratí přehled. To se může stát třeba v případě, že dané spojení bude neaktivní (neproteče paket v žádném směru) déle, než je nastaven příslušný timeout. Pak conntrack dané spojení z tabulky vyřadí, a pokud se poté objeví paket náležející k danému zahozenému spojení, bude považován právě za INVALID.Jen tak pro zajímavost, zobrazením obsahu tohoto souboru je možné nechat si vypsat všechna spojení, o kterých má conntrack přehled:

/proc/net/ip_conntrack

Timeoutů v rámci conntracku je více, pro vytvořená (ESTABLISHED) TCP spojení je příslušná hodnota v souboru:

/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established

V tomto adresáři naleznete mnohá nastavení conntracku, včetně maximálního počtu spojení, o kterých siconntrack vede záznam (nf_conntrack_max). Za tuto menší odbočku se omlouvám, ale při ladění některých nepříjemných problémů se stavovým firewallem se vám může tato informace hodit. Zejména na domácích routerech, které obsahují Linux, může být záhodno některé z těchto hodnot prověřit, zejména, pokud vám "zamrzají" spojení - někdy totiž bývají nastavena příliš nízko (kvůli šetření paměti).

Ale teď už zpět k příkladu výše. Řádky 7 a 8 povolují nová TCP spojení směřující na porty 22 (SSH) a 80 (HTTP). Pokud by se v tomto případě jednalo o webserver, asi by bylo vhodné analogickým způsobem povolit další porty jako třeba HTTPS či SMTP. Devátá řádka povoluje příchozí ICMP pakety. I když to není úplná nutnost (resp. server bude fungovat a bude přístupný i bez toho), hodí se povolit minimálně ping, tedy ICMP echo request. Je samozřejmě možné specifikovat pouze tento typ ICMP paketů a ostatní již nebrat v úvahu:

iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

Poslední pravidlo v příkladu výše zařídí, že paket, který nevyhoví žádným předešlým pravidlům, bude odmítnut, tedy sice zahozen, ale odesílateli se vrátí příslušná ICMP zpráva o "nedoručení". Pokud by tam toto pravidlo nebylo, pak by bylo s paketem nevyhovujícím žádnému z pravidel naloženo dle politiky daného základního řetězu, tedy v tomto případě by byl zahozen (politika řetězu INPUT je nastavena na DROP, viz první řádek příkladu). Prosté zahazování paketů působí problémy klientům, kteří musejí při pokusu připojit se na filtrovaný port čekat, než vyprší příslušný timeout. Toto poslední pravidlo sice případným útočníkům o něco malinko usnadňuje práci, ale útočník si porty oskenuje, i když budou pakety směřující na nepovolené porty zahazovány - jen mu to bude trvat o něco déle.

IPv6 a Netfilter

V souvislosti s rychle docházejícím adresním prostorem IPv4 a neodvratným přechodem na IPv6 možná stojí za to uvést, že Netfilter je pro IPv6 již delší dobu plně připraven, ovšem s tím důležitým faktem, že filtry pro IPv4 a IPv6 provoz jsou oddělené. Proto, pokud jste nastavili firewall prostřednictvím nástroje iptablesa divíte se, proč se přes IPv6 dostanete bez problémů i k filtrovaným službám, je to právě z tohoto důvodu - filtr pro IPv6 jste totiž ještě nenastavili. Ten se nastavuje pomocí nástroje ip6tables, jehož syntaxe je prakticky totožná s nástrojem iptables.

U většiny pravidel je možné ponechat úplně stejně argumenty, pouze někde je třeba zohlednit rozdíly mezi protokoly IPv4 a IPv6. Jedním z takových míst je úloha protokolu ICMP - v rámci IPv6 totiž na úrovni ICMP probíhá třeba autokonfigurace (neighbour discovery, atd.), takže pokud nemáte ve firewallu povolený ICMP jako takový (nebo alespoň jeho příslušné typy), můžete se setkat s tím, že po nastavení IPv6 firewallu se zakázaným ICMP vám IPv6 konektivita vypadne. Protokol ICMP v rámci IPv6 můžete povolit takto:

ip6tables -A INPUT -p ipv6-icmp -j ACCEPT

Firewall uvedený výše by ve své IPv6 variantě vypadal po menší úpravě takto (rozdíly v argumentech jsou pouze na pátém a desátém řádku):

1.  ip6tables -P INPUT DROP
2.  ip6tables -P OUTPUT ACCEPT
3.  ip6tables -P FORWARD DROP
4.  ip6tables -A INPUT -i lo -j ACCEPT
5.  ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
6.  ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
7.  ip6tables -A INPUT -m state --state INVALID -j DROP
8.  ip6tables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT
9.  ip6tables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
10. ip6tables -A INPUT -j REJECT --reject-with icmp6-adm-prohibited

Jak integrovat firewall do systému

Na rozdíl od mnoha jiných distribucí nemá Debian ve startovacích skriptech zohledněno nastavování firewallu, takže vám nezbyde než si případný firewall napsat jako shellový skript a začlenit ho do bootovacího procesu. Možností, jak toto provést je mnoho, včetně možnosti napsat si vlastní init skript a startovat firewall jako kteroukoliv jinou systémovou službu. Já zde předvedu tu úplně nejjednodušší - příslušný skript lze umístit jako pre-up skript v konfiguračním souboru pro síťová rozhraní, kterým je/etc/network/interfaces, takto:

iface eth0 inet static
address 10.0.0.254
netmask 255.255.255.0
gateway 10.0.0.138
pre-up /root/firewall.sh