V minulém článku jsme se věnovali vlastnostem nftables a také rozdílům proti klasickému firewallu s iptables. Řekli jsme si, že jde o evoluci, která přináší mnoho užitečných vlastností a v některých distribucích už nahradila iptables, aniž si toho uživatelé vlastně všimli.
Co se dozvíte v článku
Dnes se podíváme na praktickou stránku věci, ukážeme si ovládání utility nft
a naučíme se s ní spravovat základní objekty v nftables: tabulky, řetězce a pravidla. Nic z toho není ve výchozím stavu v jádře zavedeno, takže musíme začít od začátku.
Pokud chceme vidět stav celého firewallu, všech tabulek, řetězců a pravidel, můžeme kdykoliv zavolat:
# nft list ruleset
Tabulky
Tabulky jsou nejobecnějším objektem v nftables. Obsahují řetězce a ty pak zase jednotlivá pravidla. Na rozdíl od iptables nejsou určeny žádné výchozí tabulky, jejich počet, jména a určení jsou plně v rukou správce.
Každá tabulka má ovšem pevně danou rodinu adres, pro které se uplatňuje. Jde vlastně o typ tabulky podle druhu provozu, jaký se v ní bude zpracovávat. Existuje celkem pět rodin: ip, ip6, inet, arp a bridge.
Zajímavá a velmi praktická je rodina inet, která dokáže obsluhovat zároveň pravidla pro IPv4 i pro IPv6. Není tedy proto nutné mít dva samostatné firewally pro dva různé světy. V příštím článku si ukážeme, jak je možné vytvořit jednotnou sadu pravidel pro čtyřku i šestku.
Založení nové tabulky je snadné, určíme jen její název a volitelně také rodinu. Výchozí je ip pro IPv4, ale my si hned můžeme vytvořit tabulku typu inet.
# nft add table inet filter
Tím jsme si vytvořili novou tabulku s námi zvoleným názvem filter, která bude přijímat řetězce pro adresní rodinu inet. Můžeme si nechat vypsat aktuální stav:
# nft list tables table inet filter
Vidíme, že tabulka se úspěšně založila. Můžeme si vypsat řetězce, které tabulka obsahuje. Zatím tam žádné nemáme, ale časem se nám bude hodit vědět, jak na to:
# nft list table inet filter table inet filter { }
Pokud bychom chtěli tabulku smazat, použijeme jednoduše stejnou syntaxi jako při zakládání, ale příkaz add
nahradíme příkazem delete
.
# nft delete table inet filter
Podobně pro vysypání všech řetězců a pravidel z tabulky použijeme příkaz flush
:
# nft flush table inet filter
Řetězce
Smyslem řetězců (chain) je držet pravidla. Na rozdíl od iptables v nftables opět nejsou žádné vestavěné řetězce. Pokud je tedy nedefinujeme a nepověsíme na správné místo v síťovém stacku (hook), pak do nich nepoteče žádný provoz.
Řetězce mají dva typy: base a regular. Ty první fungují jako vstupní body a jsou zavěšeny do zmíněných bodů ve stacku. Tudy vtéká provoz do vámi definovaného prostředí řetězců a pravidel. Druhý typ řetězců pak není přímo nikam napojen a slouží k přesměrování provozu z jiných řetězců. To se hodí při rozsáhlejším firewallu, kde můžete provoz podle pravidel směrovat do řetězců obsahující určitou podskupinu dalších pravidel. Tohle ostatně znáte už z iptables.
Běžný řetězec (regular)
Začneme vytvořením běžného řetězce, protože ten je jednodušší a přímočařejší. V naší tabulce filter si vytvoříme řetězec, který budeme mít v plánu používat pro pravidla týkající se webu.
# nft add chain inet filter webfilter # nft list table inet filter table inet filter { chain webfilter { } }
Už známým příkazem jsme si rovnou zobrazili obsah tabulky filter a vidíme v ní založený prázdný řetězec s názvem webfilter. Jak již bylo řečeno, takový řetězec se nebude na filtrování sám o sobě podílet, protože do něj nepotečou žádná data. K tomu potřebujeme založit nejméně jeden řetězec s hookem.
Řetězec s hookem (base)
Každý takový řetězec musí mít jeden ze tří typů: filter, route či nat. Nejběžnějším typem je samozřejmě filter, který nás dnes bude také zajímat. Route vyvolá vyhledání cesty pro odbavení paketu a umožňuje například implementovat selektory pro policy routing. Typ nat pak zpracovává jen první paket nového spojení a umožňuje definovat pravidla pro překlad adres pomocí dat z Connection Trackingu.
Náš řetězec musí mít také definován hook, tedy bod v síťovém stacku, ze kterého do něj odkloníme datový tok. Pro rodinu IP protokolů (tedy tabulky ip, ip6 či inet) je možné použít hooky: prerouting, input, forward, output či postrouting.
Poslední vlastností při definici řetězce je priorita. Ta určuje, v jaké fázi zpracování paketu se budou posuzovat pravidla v daném řetězci. Priorita může být kladná i záporná a síťový stack má definovanou řadu výchozích priorit podle stupně zpracování. Nás ovšem bude zajímat, že pokud existuje více řetězců se stejným hookem a různou prioritou, budou pakety postupně procházet všemi těmito hooky, pokud nebudou zamítnuty. Výchozí prioritou pro filtrování paketů je 0.
S těmito informacemi tedy můžeme vytvořit svůj první řetězec, který zavěsíme na input. Budeme v něm tedy filtrovat příchozí provoz a nazveme si ho inputchain. Všimněte si, že část s definicí hooku musíme vložit do apostrofů, aby nám závorky nezačal interpretovat shell.
# nft add chain inet filter inputchain '{ type filter hook input priority 0; }' # nft list table inet filter table inet filter { chain webfilter { } chain inputchain { type filter hook input priority 0; policy accept; } }
Stejně jako u iptables má i tady řetězec svou výchozí politiku (policy), která určuje, co se stane s paketem, pokud nevyhoví žádnému pravidlu. Výchozí stav je accept, tedy propustit. Pokud chceme neznámý provoz naopak zahazovat, můžeme politiku snadno přepnout:
# nft chain inet filter inputchain '{ policy drop ; }'
Opět máme kdykoliv možnost obsah řetězce vypláchnout nebo celý řetězec smazat:
# nft flush chain inet filter inputchain # nft delete chain inet filter inputchain
Pravidla
Pravidla jsou výrazy popisující vlastnosti zkoumaného paketu. Jádro paket porovná s vlastnostmi uvedenými v daném pravidle a pokud odpovídají, provede definované akce. Pravidla jsou organizována v řetězcích.
Pravidly se budeme podrobně zabývat v příštím článku, teď jen jeden konkrétní příklad. Řekněme, že v našem vstupním řetězci chceme povolit porty pro SSH (TCP/22) a HTTPS (TCP/443). Jelikož jsme si výchozí politiku nastavili na drop, musíme teď explicitně tyto porty povolit. Můžeme je označit čísly nebo názvem služby podle databáze v /etc/services
.
# nft add rule inet filter inputchain tcp dport { ssh, https } accept
Vytvořili jsme nové pravidlo v tabulce filter a řetězci inputchain, které se uplatňuje na TCP pakety s daným cílovým portem. Pro ten jsme vytvořili nepojmenovaný seznam a vložili do něj dvě položky. Pokud tomuto pravidlu paket vyhoví, je propuštěn.
Můžeme se opět podívat na současný stav našeho firewallu:
# nft list table inet filter table inet filter { chain webfilter { } chain inputchain { type filter hook input priority 0; policy drop; tcp dport { ssh, https } accept } }
Uložení a obnovení stavu
Všechny takto vytvořené objekty jsou pouze v paměti jádra a nepřežijí restart. Pro perzistentní konfiguraci je potřeba stav uložit do souboru, odkud může být při příštím startu načten. V prostředích se systemd se o to automaticky stará služba nftables.service
, která při startu hledá konfiguraci v /etc/nftables.conf
.
Pro uložení stavu stačí vypsat celou konfiguraci všech tabulek a zapsat ji do souboru. Uloží se tam nám známý výpis situace, který jsme si už několikrát v článku ukazovali:
# nft list ruleset > /etc/nftables.conf
Poté můžeme cvičně celý obsah nftables vypláchnout a znovu načíst ze souboru:
# nft flush ruleset # nft -f /etc/nftables.conf
Příště
V následujícím článku se budeme podrobně zabývat pravidly. Ukážeme si, jak přidávat pravidla na určité pozice, co vše můžeme na paketech zkoumat a navrhneme skutečnou podobu minimálního firewallu, který by bylo možné použít na serveru.