Stavové sledování spojení (connection tracking)

Linuxový conntrack

Vraťme se zpět k otázce: Jak zjednodušit konfiguraci firewallu a zároveň zvýšit úroveň bezpečnosti, kterou firewall poskytuje? Řekněme, že chceme povolit odpovědi, které přichází zvenčí na spojení, která jsme navázali my - tím tedy myslíme první odpověď (druhý TCP segment ve spojení s flagy SYN+ACK) a pak všechny další TCP segmenty s daty až do konce spojení. S connection trackingem to v iptables to uděláme snadno:

iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT

Toto pravidlo se však hodí dát blízko začátku řetězce, takže je na zvážení použít míst parametru -A (append) možnost -I (insert), kterým lze pravidlo vložit na konkrétní pozici a bez argumentu specifikující pozici pak na začátek.

Pro nftables bude v tabulce ip filter v řetězci INPUT pravidlo:

ct state established counter accept

S jakými packety má tedy toto pravidlo shodu? Nebudeme zabíhat do úplných podrobností, ale v principu stav established představuje packety, která jsou součástí již navázaných spojení. Pro TCP má první packet (TCP segment s flagem SYN=1), kterým klient zahajuje navazování spojení, stav new. Jakmile server odpoví dle TCP three-way handshake druhým packetem ve spojení, tedy segmentem s flagy SYN=1, ACK=1, je spojení považováno za navázané a od druhého packetu včetně mají packety stav established. Podobně bychom mohli rozebrat ukončení spojení, které pro TCP znamená, že jak klient, tak server musí nezávisle vyslat segment s flagem FIN=1 a druhá strana musí tento segment potvrdit (ACK=1). Teprve když je spojení oboustranně uzavřeno, smaže se z tabulky spojení.

Pro UDP, kde není explicitní navazování a ukončování spojení formalizováno, mají stavy new a established přibližnou vypovídací hodnotu a v principu zachycují první packet s konkrétní čtveřicí - zdrojová IP adresa, cílová IP adresa, zdrojový port a cílový port - jako new a všechny následující packety s touto resp. inverzní (s prohozeným zdrojem a cílem) čtveřicí jako established. Ukončení spojení je jednoduše předpokládáno, pokud spolu strany spojení (určené čtveřicí adres a portů) nekomunikují déle, než timeout určený sysctl parametrem net.netfilter.nf_conntrack_udp_timeout_stream a ten má, mimochodem, u moderních kernelů výchozí hodnotu 120 s.

Samostatnou kapitolou by bylo rozebrání všech situací, které se mohou stát při navazování a ukončování spojení, pokud se některý z packetů, který tyto akce řídí, ztratí. Podobně je zajímavé havarijní ukončování TCP spojení jednostranně pomocí TCP segmentů s flagem RST=1. My se ale spokojíme s tím, že v drtivé většině situací můžeme věřit tomu, že packet se stavem new je zkrátka nově navazované spojení a established je pokračování spojení, které jsme předtím umožnili navázat jiným pravidlem.

Ještě se rychle zmiňme o stavu related. Ten značí, že packet sice není přímo součástní běžícího spojení, avšak je ve vztahu s běžícím spojením a je proto zpravidla rozumné propouštět nejen established, ale i related packety:

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

nebo

ct state { established, related } counter accept

Co přesně znamená to, že packet je ve vztahu s běžícím spojením? To může být například ICMP datagram, kterým se signalizuje chyba vzniklá v daném spojení - přesto na related se nedá úplně spoléhat a ICMP je lepší povolit explicitně. A pak to mohou být další kanály od existujícího hlavního spojení - příkladem je FTP, kde je nejdříve navázáno TCP spojení jako řídící kanál a pak se pro každý přenos souboru otevírá další spojení na jiných portech. Pokud není řídící kanál šifrovaný a pokud má Netfilter zapnutý FTP helper - modul, který odposlouchává příkazový kanál - dokáže pak označit packety náležící přenosu souboru jako related k navázanému příkazovému kanálu.

Odkazy: