Hlavní navigace

nftables: akce prováděné nad pravidly včetně nastavení NAT

Linuxový firewall iptables je tu s námi téměř dvacet let, ale je postupně nahrazován novým moderním řešením zvaným nftables. Dnes se podíváme na to, jaké akce můžeme s pakety provádět a jak udělat třeba NAT.
Petr Krčmář
Doba čtení: 7 minut

Sdílet

minulém článku našeho seriálu o nftables jsme si řekli, jak pracovat s pravidly, jak je přidávat, upravovat či mazat a jak můžeme vytvářet počítadla a přidávat komentáře. Dnes se zaměříme na akce, tedy na to, co lze po rozhodnutí s paketem udělat.

Přijetí a zahození provozu

Základní akce, stejně jako v případě iptables, jsou accept a drop. Dovolují vybraný provoz propustit či zahodit. Samozřejmě můžeme tuto akci kombinovat s dalšími, takže je možné například použít počítadlo, jak jsme si ukázali v předchozím článku.

Základní použití je snadné, můžeme se rozhodnout, že chceme veškerý příchozí provoz zablokovat a počítat zablokované pakety. Pozor na to, že akce drop je akcí konečnou a další pravidla za ní už se neprovádějí.

# nft add rule filter output counter drop

Podívejme se, jak vypadá takto nastavený firewall:

# nft list table filter
table ip filter {
        chain output {
                 type filter hook output priority 0;
                 counter packets 1 bytes 84 drop
        }
}

Odmítnutí provozu

Můžeme také provoz slušně odmítnout, tedy odeslat zpět informaci o tom, že jsme z nějakého důvodu pakety nepřijali. Pokud neuvedeme jiný typ odpovědi, odešle jádro zprávu typu ICMP nebo ICMPv6 typu port unreachable.

# nft add rule filter input reject

Můžeme ovšem také blíže specifikovat důvod odmítnutí. Zápis takto rozšířeného pravidla vypadá následovně:

# nft add rule filter input reject with icmp type host-unreachable

Máme více možností, kterých můžeme při odmítnutí využít.  V případě ICMP jsou k dispozici následující varianty:

  • net-unreachable – cílová síť nedostupná
  • host-unreachable – cílový hostitel nedostupný
  • prot-unreachable – cílový protokol nedostupný
  • port-unreachable – cílový port nedostupný
  • net-prohibited – síť administrativně zakázána
  • host-prohibited – hostitel administrativně zakázán
  • admin-prohibited – komunikace administrativně zakázána

Pro ICMPv6 je zápis odlišný:

Root linux

# nft add rule ip6 filter input reject with icmpv6 type no-route

Opět je k dispozici více voleb:

  • no-route – k cíli nevede žádná trasa
  • admin-prohibited – komunikace administrativně zakázána
  • addr-unreachable – cílová adresa nedostupná
  • port-unreachable – cílový port nedostupný

Pokud používáte univerzální tabulku inet, která pokrývá oba protokoly, můžete použít abstrakci nazvanou ICMPx, která dovoluje odmítnout provoz IPv4 i IPv6 v jednom pravidlu:

# nft add rule inet filter input reject with icmpx type no-route

Protože obě rodiny mají rozdílné důvody odmítání paketů, existuje pevné mapování, které pro každý z protokolů vybírá odlišnou variantu:

Označení icmpx ICMPv6 ICMPv4
admin-prohibited admin-prohibited admin-prohibited
port-unreachable port-unreachable port-unreachable
no-route no-route net-unreachable
host-unreachable addr-unreachable host-unreachable

Skok do jiného řetězce

Podobně jako v případě iptables je i v nftables rozumné dělit velké sady pravidel do menších celků a členit je do samostatných řetězců. Vyhneme se tak rozsáhlé ploché struktuře a pro pravidla vytváříme strom řetězců, které sdružují související pravidla.

Nejprve si tedy musíme vytvořit běžný řetězec bez hooku. O tomto principu jsme se zmiňovali v druhém článku. Do takového řetězce neproudí žádný provoz automaticky, ale je na nás, abychom tam nějaký provoz poslali z jiných řetězců, už na základě předchozího výběru. Pozor, zároveň platí, že skákat můžeme právě jen do běžného řetězce, tedy řetězce bez hooku.

Můžeme si například vytvořit řetězec pro UDP pakety:

# nft add chain ip filter udp-chain

Poté do řetězce input přidáme nové pravidlo, které nám UDP pakety pošle do samostatného řetězce. Výhodou je, že tím odkloníme celý UDP provoz a v řetězci input už se o něj nemusíme starat.

# nft add rule ip filter input ip protocol udp jump udp-chain

V nově používaném řetězci pak můžeme pakety dále filtrovat nebo si je třeba pro začátek jen počítat:

# nft add rule ip filter udp-chain counter

Akce jump vs. goto

V příkladu výše jsme si ukázali akci jump, existuje ovšem i podobná akce goto. Rozdíl mezi nimi je v tom, co se stane s paketem, který projde až na konec nového řetězce. V případě použití akce jump se takový paket vrátí do původního řetězce a je v něm dále zpracováván.

Pokud ovšem použijeme goto, paket už se nám do původního řetězce nevrátí a pokud dorazil na konec pravidel v novém řetězci, je na něj aplikována jeho výchozí politika.

Zaznamenávání do logu

Informace o určitém druhu provozu můžeme nechat ukládat do systémového logu. Obvykle se to dělá například u SSH, kde chceme mít přehled o tom, z jakých adres k nám přichází nová spojení.

Můžeme tu velmi dobře využít toho, že nftables umožňuje spojit více akcí do jednoho pravidla. Můžeme tak daný provoz například propustit a ještě zalogovat. Do zprávy můžeme uvést vlastní prefix, kterým bude informace uvozena a umožní nám pak snadno v logu tento typ zpráv vyhledávat:

# nft add rule filter input tcp dport 22 ct state new log prefix \"SSH spojeni: \" accept

Je potřeba mít na vědomí, že akce se vyhodnocují vždy zleva doprava. Můžeme tak akce a pravidla řetězit, což opět snižuje množství potřebných pravidel. Například zde zalogujeme veškerý provoz přicházející na rozhraní a zároveň pak povolíme ten směřující na port 22.

# nft add rule filter input iif eth0 log tcp dport 22 accept

Překlad adres pomocí NAT

Pro překlad adres mezi sítěmi se používá NAT (Network Address Translation). Ten umožňuje přepsat při průchodu routerem adresy v hlavičkách, což se nejčastěji používá v sítích s privátními IPv4 adresami, které potřebují mít zároveň přístup k internetu pomocí adresy na vnějším rozhraní routeru.

V nftables je NAT implementován pomocí řetězce se samostatným typem nat. První paket nového spojení způsobí vyhledání informací v tabulce vazeb, případně vytvoření nového záznamu. Poté je paket podroben potřebné transformaci a odeslán. Další pakety už na základě vytvořené vazby v tabulce přímo procházejí úpravou.

Stavový NAT

Existuje několik základních typů překladu adres, rozdělených podle povahy úpravy, která je na paketech prováděna. Ty se dělí na dvě velké skupiny: stavový a nestavový NAT.

Stavový využívá jadernou službu sledování spojení (connection tracking, zkráceně conntrack), která udržuje v paměťové struktuře stavovou informaci o jednotlivých spojeních. Podle metainformací v této struktuře pak NAT rozhoduje o úpravách paketů náležejících k danému spojení.

V některých speciálních případech pak může posloužit bezestavový NAT, který je staticky nastavený a nevyžaduje doplňování informací o jednotlivých spojeních. Většina běžných nasazení ale počítá se stavovým řešením, které zahrnuje také následující typy překladu.

Source NAT

Přepisuje zdrojovou adresu odchozích paketů. Toto je obvykle NAT, který si představí většina uživatelů. Na hranici sítě se stará o to, aby byly zdrojové privátní adresy překládány na jednu veřejnou.

Nejprve je pro něj potřeba vytvořit tabulku a příslušný řetězec se správným typem, který je napojený na hook postrouting. Nakonec přidáme samotné pravidlo, které zachytí pakety se správnou zdrojovou adresou a výstupním rozhraním ( oif). U nich se postará o přepis na veřejnou adresu.

# nft add table nat
# nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
# nft add rule nat postrouting ip saddr 192.168.1.0/24 oif eth0 snat 203.0.113.10

Maškaráda

Zvláštní varianta Source NAT, kdy je adresa odchozích paketů automaticky přepsána adresou odchozího rozhraní. Opět využijeme bod postrouting, jako v předchozím případě, ale samotné pravidlo bude tentokrát velmi prosté:

# nft add rule nat postrouting masquerade

Maškaráda má jednodušší konfiguraci a jednoduše funguje i na dynamicky přidělovaných adresách na výstupním rozhraní. Nevýhodou je, že je méně výkonná, protože její dynamičnost vyžaduje neustálou kontrolu adres na rozhraní, na které je možné provádět překlad.

Destination NAT

Přepisuje cílovou adresu procházejících paketů. Používá se obvykle pro přesměrování portů z vnějšího rozhraní routeru na cílový počítač uvnitř privátní sítě. Tentokrát je potřeba adresu přepisovat na začátku průchodu síťovým subsystémem, takže příslušný řetězec zavěsíme na prerouting.

V příkladu vybereme pakety, které přicházejí z vybraného vstupního rozhraní ( iif) a směřují na konkrétní porty (HTTP a HTTPS) a jejich cílovou adresu přepíšeme na adresu našeho interního web serveru:

# nft add table nat
# nft add chain nat prerouting { type nat hook prerouting priority \-100 \; }
# nft add rule nat prerouting iif eth0 tcp dport { 80, 443 } dnat 192.168.1.200

Přesměrování

Tentokrát speciální případ předchozího Destination NAT, přičemž je přepisován pouze cílový port, nikoliv IP adresa. Hodí se ve chvíli, kdy potřebujeme udělat lokální přesměrování portů. V následujícím příkladu přesměrujeme provoz s cílovým portem 8080 na port 80.

# nft add rule nat prerouting tcp dport 8080 redirect to 80

Bezestavový NAT

Zvláštním typem překladu je už dříve zmíněný bezestavový NAT, který mění každý procházející paket podle předem stanovených pravidel. Nezajímají ho žádné metainformace o spojeních a nepotřebuje udržovat v paměti žádný stav. Výhodou je vysoký výkon a nenáročnost na systémové zdroje. Nevýhodou je omezenost použití daná statickou konfigurací.

Následující příklad přepíše zdrojovou adresu a port označených paketů. Takto upravené pakety pak už nebudou procházet connection trackingem.

# nft add rule ip raw prerouting ip protocol tcp ip daddr set 192.168.1.100 tcp dport set 10 notrack

Příště struktury pro výkon

V dalším díle si ukážeme některé pokročilé datové struktury, které dokáže nftables používat pro zvýšení svého výkonu. Pokud jste s iptables někdy použili sady informací v ipset, budete o krok napřed.