iptables a nftables w Ubuntu: jak sprawdzić reguły?
Dowiedz się, dlaczego polecenie iptables w Ubuntu zapisuje reguły nftables. Sprawdź backend za pomocą iptables -V i zobacz, jak ufw oraz Docker współdzielą jeden filtr.
iptables kontra nftables w systemie Ubuntu: co działa na Twoim serwerze?
W systemie Ubuntu 20.04 i nowszych polecenie iptables stanowi interfejs zapisujący reguły nftables. W jądrze systemu działa jeden filtr pakietów, nftables, a dwa polecenia przestrzeni użytkownika służą do jego programowania. Linia iptables -A INPUT działa dokładnie tak samo jak wcześniej, a utworzona przez nią reguła jest regułą nftables, którą może wyświetlić nft.
Zweryfikuj to na własnym serwerze, zanim przyjmiesz to za pewnik.
iptables -V
sudo update-alternatives --display iptables
sudo nft list rulesetW systemie Ubuntu 24.04 (iptables 1.8.10, stan na sierpień 2026) polecenie iptables -V wyświetla iptables v1.8.10 (nf_tables). Nazwa w nawiasach oznacza backend. (nf_tables) oznacza, że polecenie komunikuje się z nftables. (legacy) oznacza stary backend x_tables, który Ubuntu nadal dostarcza jako iptables-legacy i który jądro utrzymuje jako całkowicie oddzielny zestaw reguł. update-alternatives wyświetla dowiązanie symboliczne odpowiadające za ten wybór: link currently points to /usr/sbin/iptables-nft.
Na świeżym serwerze VPS bez skonfigurowanej zapory sieciowej polecenie sudo nft list ruleset nie wyświetla nic. Ten pusty wynik stanowi punkt odniesienia. Dodaj jedną regułę w stary sposób i sprawdź ponownie.
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo nft list ruleset# Warning: table ip filter is managed by iptables-nft, do not touch!
table ip filter {
chain INPUT {
type filter hook input priority filter; policy accept;
tcp dport 22 counter packets 0 bytes 0 accept
}
chain FORWARD {
type filter hook forward priority filter; policy accept;
}
chain OUTPUT {
type filter hook output priority filter; policy accept;
}
}Twoja reguła iptables jest regułą nftables. iptables-nft oznacza tablice, które tworzy, a nft wyświetla to ostrzeżenie po wykryciu znacznika, ponieważ edycja takiej tablicy za pomocą nft powoduje, że dwa narzędzia zarządzają tymi samymi regułami. Sprawdź, co wygenerowało polecenie: tablicę, której nie nazwano, oraz łańcuchy, o które nie wnioskowano. Jest to stary model i zmienia się on jako pierwszy w momencie bezpośredniego zapisu reguł nftables.
Co ukrywa przed Tobą iptables -L
iptables -L wyświetla wyłącznie tablicę filter. Reguły NAT (network address translation) wymagają iptables -t nat -L, a reguły mangle wymagają -t mangle. Obsługa IPv6 znajduje się w osobnym poleceniu, ip6tables, które posiada własną kopię wszystkich reguł. Serwer może zatem wyglądać na poprawnie skonfigurowany w jednym zestawieniu, podczas gdy pakiety są odrzucane lub modyfikowane przez reguły z tablicy, której nie sprawdzono.
sudo nft list ruleset wypisuje wszystkie rodziny, tablice, łańcuchy oraz reguły w jednym wyjściu. Na serwerze, którego konfiguracji nie tworzono samodzielnie, to polecenie jest najszybszym sposobem na sprawdzenie faktycznie załadowanych reguł. Należy dodać -a, aby wyświetlić identyfikatory (handles) reguł, które są niezbędne do usunięcia pojedynczej reguły zamiast całego łańcucha.
Warto wyrobić sobie dwa nawyki podczas pracy z tym narzędziem. iptables -L rozwiązuje adresy i porty na nazwy, więc na serwerze z niedziałającym resolverem polecenie może sprawiać wrażenie zawieszonego: należy używać iptables -nvL. Należy również potwierdzić, że starszy backend jest pusty za pomocą sudo iptables-legacy -nvL, ponieważ jeśli reguły istnieją w obu backendach, jądro systemu przetwarza oba zestawy, a żadne z zestawień nie pokazuje pełnego obrazu konfiguracji.
Tabele i łańcuchy tworzone samodzielnie, a nie dziedziczone
nftables startuje z pustą konfiguracją. Tabela filter nie istnieje, dopóki nie zostanie utworzona, a słowo filter jest jedynie wybraną nazwą. Łańcuch przetwarza pakiety tylko wtedy, gdy przypisano mu typ, hook oraz priorytet, co czyni go łańcuchem bazowym. Łańcuch bez tych parametrów jest osiągalny tylko przez jawne polecenie jump lub goto, więc nie generuje narzutu, dopóki żaden ruch nie zostanie do niego przekierowany.
Drugą istotną zmianą jest rodzina inet. Jedna tabela inet obsługuje IPv4 oraz IPv6 w ramach tych samych reguł, co eliminuje całą klasę błędów, w których port jest zamknięty w iptables, a otwarty w ip6tables. Ta rozbieżność występuje na tyle często, że posiada własny tryb awarii na systemach z ufw.
Poniżej znajduje się kompletny zestaw reguł serwerowych. Należy go umieścić w /etc/nftables.conf.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set admin_ips {
type ipv4_addr
flags interval
elements = { 203.0.113.5, 198.51.100.0/24 }
}
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
ip protocol icmp accept
meta l4proto ipv6-icmp accept
tcp dport 22 ip saddr @admin_ips counter accept
tcp dport { 80, 443 } counter accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}Przeczytaj dwukrotnie drugą linię. flush ruleset usuwa każdą tabelę na serwerze, w tym tabele utworzone automatycznie przez ufw oraz Docker. Zanim uruchomisz to na działającym serwerze, przeczytaj dalszą część instrukcji.
Pierwsza reguła w łańcuchu input wykonuje większość pracy. ct state established,related accept zezwala na powrót odpowiedzi dla połączeń zainicjowanych lokalnie, dzięki czemu reszta łańcucha musi decydować tylko o nowych połączeniach. ct state invalid drop odrzuca pakiety, które nie pasują do żadnego znanego połączenia ani poprawnego żądania nawiązania sesji. Wszystko, co znajduje się poniżej, stanowi jawne otwarcie portu, a policy drop obsługuje resztę ruchu.
Przed załadowaniem pliku sprawdź jego poprawność i utrzymuj otwartą drugą sesję SSH. policy drop w połączeniu z jedną literówką w regule SSH spowoduje utratę dostępu do serwera.
sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo nft list rulesetnft -c -f analizuje plik i zgłasza błędy bez ładowania konfiguracji. Poprawna analiza nie generuje żadnego wyjścia.
Zbiory zastępują długie listy reguł
tcp dport { 80, 443 } to zbiór anonimowy: jedna reguła i jedno wyszukiwanie zamiast jednej reguły na port. Nazwany zbiór, taki jak admin_ips, oferuje więcej, ponieważ można go modyfikować w trakcie działania firewalla.
sudo nft add element inet filter admin_ips { 203.0.113.9 }
sudo nft list set inet filter admin_ips
sudo nft delete element inet filter admin_ips { 203.0.113.9 }Brak przeładowywania i brak zmiany numeracji reguł; dopasowanie pozostaje pojedynczym wyszukiwaniem, niezależnie od tego, czy zbiór zawiera pięć adresów, czy pięćdziesiąt tysięcy. flags interval pozwala zbiorowi przechowywać zakresy oraz prefiksy CIDR (classless inter-domain routing), takie jak 198.51.100.0/24. Bez tej flagi zbiór przyjmuje tylko pojedyncze adresy, a ładowanie prefiksu kończy się niepowodzeniem.
Zbiory mogą również automatycznie usuwać swoje elementy.
set banned {
type ipv4_addr
flags timeout
timeout 1h
}Dzięki regule ip saddr @banned drop każdy element usuwa się sam godzinę po dodaniu. W ten sposób akcja nftables w fail2ban na Ubuntu 24.04 blokuje adres: dodaje element do zbioru, a nie dodaje nową regułę. Jeśli same porty są nowym zagadnieniem, warto zacząć od czym faktycznie jest port w systemie Linux.
Jedna różnica często zaskakuje użytkowników podczas migracji. nftables nie zlicza pakietów, dopóki użytkownik o to nie poprosi. iptables -nvL zawsze pokazuje liczniki dla każdej reguły. W nftables tylko reguły zawierające słowo kluczowe counter posiadają liczniki, dlatego należy umieścić counter w każdej regule, która może wymagać późniejszego debugowania.
Jak haki i priorytety określają kolejność
Łańcuch bazowy definiuje hak, czyli punkt w ścieżce pakietu, w którym jest on przetwarzany. prerouting uruchamia się przed decyzją o routingu. input uruchamia się dla pakietów zaadresowanych do tego urządzenia. forward uruchamia się dla pakietów przesyłanych przez urządzenie. output uruchamia się dla pakietów pochodzących z procesów lokalnych. postrouting uruchamia się jako ostatni, tuż przed opuszczeniem urządzenia przez pakiet.
Priorytet porządkuje łańcuchy wewnątrz jednego haka, zaczynając od najniższej wartości liczbowej. nftables przypisuje klasycznym wartościom nazwy: raw to -300, mangle to -150, dstnat to -100, filter to 0, srcnat to 100. Zapis priority filter; jest równoważny zapisowi priority 0;.
Kluczowa kwestia decydująca o możliwości łączenia narzędzi: każdy łańcuch bazowy zarejestrowany na haku uruchamia się zgodnie z priorytetem. Pakiet zaakceptowany w łańcuchu nie kończy swojej drogi: accept kończy jedynie dany łańcuch, a pakiet przechodzi do kolejnego łańcucha bazowego na tym samym haku. drop jest ostateczne w każdym przypadku i natychmiast zatrzymuje pakiet. Zatem permisywna reguła w Twojej tablicy nie cofnie odrzucenia pakietu przez tablicę ufw, niezależnie od kolejności ich uruchomienia, a Twoje accept nie zapewnia ochrony przed łańcuchem, który uruchamia się później.
Dwa łańcuchy bazowe na tym samym haku z tym samym priorytetem uruchamiają się w kolejności rejestracji, która zależy od tego, która usługa wystartowała pierwsza. Ta kolejność może ulec zmianie po restarcie. Jeśli musisz uruchomić własną tablicę obok ufw, nadaj jej unikalny priorytet, aby kolejność była jawnie określona, zamiast polegać na wyścigu procesów.
Dlaczego nie trzeba tworzyć reguły odwrotnego NAT?
Jest to pytanie, w którym najczęściej popełnia się błąd, dlatego oto bezpośrednia odpowiedź. Mechanizm śledzenia połączeń (connection tracking) tworzy odwrotne tłumaczenie automatycznie. Nie ma potrzeby dodawania drugiej reguły.
Tabela nat wykonująca obie części typowego zadania VPS wygląda następująco.
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "enp1s0" tcp dport 8080 dnat ip to 10.0.0.5:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.0.0.0/24 oifname "enp1s0" masquerade
}
}Tylko pierwszy pakiet połączenia jest sprawdzany w łańcuchu nat. Gdy reguła zostanie dopasowana, jądro zapisuje to tłumaczenie w tabeli śledzenia połączeń wraz z wpisem dotyczącym danego połączenia. Każdy kolejny pakiet, w obu kierunkach, jest przepisywany na podstawie zapisanego wpisu i żadna reguła nie jest ponownie odczytywana. Zainstaluj narzędzie conntrack i przejrzyj aktywny wpis.
sudo apt install -y conntrack
sudo conntrack -L -p tcptcp 6 431999 ESTABLISHED src=198.51.100.20 dst=203.0.113.10 sport=54321 dport=8080 src=10.0.0.5 dst=198.51.100.20 sport=80 dport=54321 [ASSURED] mark=0 use=1Należy odczytywać to jako dwie krotki. Pierwsze cztery pola to połączenie w postaci wysłanej przez klienta, zaadresowane do 203.0.113.10:8080, czyli Twojego adresu publicznego. Drugie cztery pola to odpowiedź, której oczekuje jądro, już odwrócona i przetłumaczona, pochodząca z 10.0.0.5:80, czyli rzeczywistego backendu. Ta druga krotka jest regułą odwrotną. Jądro zapisało ją w momencie dopasowania pierwszego pakietu.
Dlatego nie należy tworzyć reguły dla kierunku powrotnego. Nie może ona zostać dopasowana, ponieważ pakiety powrotne należą do ustanowionego połączenia i nigdy nie docierają do łańcucha nat, a gdyby w jakiś sposób zostały dopasowane, doszłoby do ponownego tłumaczenia pakietu, który jądro już poprawiło.
Miejsce, w którym musi znajdować się reguła przepisująca, wynika z tego samego mechanizmu. Tłumaczenie adresu docelowego musi odbyć się w prerouting, przed decyzją o routingu, ponieważ routing musi widzieć nowy adres docelowy, w przeciwnym razie pakiet trafi w niewłaściwe miejsce. Ruch generowany przez sam serwer jest obsługiwany w punkcie zaczepienia output z tego samego powodu. Tłumaczenie adresu źródłowego, w tym przepisanie portu źródłowego, musi odbyć się w postrouting, po tym jak routing wybierze interfejs wyjściowy. masquerade pobiera swój adres z tego interfejsu, a interfejs nie jest znany, dopóki routing nie zostanie wykonany.
Dlatego reguła taka jak ta poniżej powinna znajdować się na końcu ścieżki i nigdzie indziej.
ip saddr 10.0.0.0/24 oifname "enp1s0" snat ip to 203.0.113.10:20000-30000Zakres portów przepisuje port źródłowy wraz z adresem źródłowym, co jest pożądane, gdy wielu klientów wewnętrznych współdzieli jeden adres publiczny i dochodzi do kolizji ich portów źródłowych. Odpowiedź dociera zaadresowana do portu z tego zakresu, conntrack dopasowuje ją do wpisu, a oryginalny port źródłowy zostaje przywrócony przed dostarczeniem pakietu. Ponownie, nie jest potrzebna druga reguła.
Praktyczna konsekwencja: zmiana reguły NAT nie wpływa na istniejące połączenia, ponieważ ich tłumaczenie jest już zapisane. Zachowują one stare zachowanie do momentu wygaśnięcia ich wpisów. sudo conntrack -D -p tcp --dport 8080 usuwa pasujące wpisy, a sudo conntrack -F usuwa je wszystkie. Z tym drugim należy postępować ostrożnie na serwerze NAT, ponieważ zapisane tłumaczenia utrzymują bieżące połączenia przy życiu, więc ich usunięcie natychmiast zrywa wszystkie połączenia przechodzące przez serwer.
ufw i Docker tworzą własne reguły
ufw jest nakładką na iptables, która w systemie Ubuntu stanowi interfejs dla nftables. Serwer z aktywnym ufw posiada zatem tabelę ip filter wypełnioną łańcuchami o nazwach ufw-before-input, ufw-user-input i podobnymi, a także kopię ip6 filter o tej samej strukturze. Stan ten można sprawdzić za pomocą sudo nft list ruleset | grep ufw. Łańcuchy te są generowane na podstawie plików w /etc/ufw, a ufw reload nadpisuje je od podstaw, dlatego ręcznie dodana reguła iptables znika przy następnym przeładowaniu. Podstawy ufw dla VPS omawiają ten układ plików.
Docker samodzielnie konfiguruje firewall i nie korzysta z ufw. Publikacja portu za pomocą -p 80:80 powoduje zapisanie reguły DNAT w tabeli nat oraz reguły accept w ścieżce forward, przy czym obie są przetwarzane przed łańcuchami użytkownika ufw. Wynik tego działania jest dla wielu zaskoczeniem: ufw deny 80 jest załadowane, a kontener pozostaje dostępny z Internetu. Rozwiązanie znajduje się w łańcuchu DOCKER-USER, który Docker pozostawia do dyspozycji użytkownika, a dlaczego kontenery Docker ignorują ufw szczegółowo to wyjaśnia. Bieżącą konfigurację na serwerze można sprawdzić za pomocą sudo nft list ruleset | grep -i docker.
Należy ponownie przeczytać linię flush ruleset z powyższej konfiguracji. Usuwa ona każdą tabelę, w tym tabele zarządzane przez oba wspomniane narzędzia. Na hoście z Dockerem opublikowane porty przestają działać do momentu, aż sudo systemctl restart docker odbuduje łańcuchy. Ta jedna linia jest najczęstszą przyczyną, dla której użytkownicy wyłączają własne usługi podczas porządkowania firewalla.
Reguły zachowujące trwałość po restarcie
Żaden z zestawów reguł nie jest domyślnie trwały. Jądro systemu usuwa wszystkie ustawienia podczas wyłączania, dlatego każda ze stron rozwiązuje ten problem za pomocą osobnego pakietu.
W przypadku nftables plik /etc/nftables.conf jest odczytywany przez nftables.service. System Ubuntu dostarcza tę usługę w stanie wyłączonym, dlatego należy sprawdzić jej status przed poleganiem na niej.
systemctl is-enabled nftables
sudo nft -c -f /etc/nftables.conf
sudo systemctl enable --now nftablesDla iptables odpowiednim pakietem jest iptables-persistent, który instaluje netfilter-persistent i zapisuje reguły do plików /etc/iptables/rules.v4 oraz /etc/iptables/rules.v6.
sudo apt install -y iptables-persistent
sudo netfilter-persistent saveNie należy uruchamiać obu rozwiązań jednocześnie. Dwa pliki, z których każdy deklaruje zarządzanie firewallem, będą się rozbiegać, a ten wczytany jako ostatni nadpisze konfigurację w sposób niemożliwy do przewidzenia na podstawie analizy samych plików.
Istnieje również ryzyko związane z eksportowaniem aktywnego zestawu reguł. Polecenie sudo nft -s list ruleset > /etc/nftables.conf przechwytuje wszystko, co jest w danej chwili załadowane, w tym tablice ufw oraz tablice Docker. Przywrócenie takiego zrzutu podczas startu systemu spowoduje wczytanie zamrożonej kopii reguł, które te narzędzia powinny budować samodzielnie, a następnie utworzenie drugiej kopii po ich uruchomieniu. Należy eksportować wyłącznie własną tablicę za pomocą sudo nft -s list table inet filter. Flaga -s pomija liczniki, które nie powinny znajdować się w pliku konfiguracyjnym.
Czy należy włączać ufw na VPS?
Należy pozostawić ufw w spokoju, chyba że wymagana jest funkcjonalność, której to narzędzie nie obsługuje. ufw sprawdza się w typowych zastosowaniach VPS: domyślna polityka odrzucania ruchu z kilkoma otwartymi portami. Zastępowanie go ręcznie napisanym zestawem reguł bez wyraźnej potrzeby zwiększa jedynie nakład pracy przy utrzymaniu systemu, nie oferując dodatkowych korzyści.
Należy przejść na natywne rozwiązania, gdy wymagania wykraczają poza model ufw: NAT i przekierowanie portów, zestawy adresów aktualizowane w czasie wykonywania, jedna reguła obsługująca obie rodziny adresów lub własne priorytety łańcuchów. Są to uzasadnione powody, których ufw nie jest w stanie obsłużyć.
W przypadku przejścia na rozwiązania natywne, należy zrobić to w pełni. Należy wykonać sudo ufw disable oraz sudo systemctl disable --now ufw, a następnie potwierdzić za pomocą sudo nft list ruleset, że tablice zostały usunięte, po czym załadować własny plik konfiguracyjny. Serwer, na którym jednocześnie działa ufw oraz ręcznie napisane tablice, nadal przepuszcza ruch, jednak aktywna polityka staje się sumą dwóch zestawów reguł, których kolejność oceny zależy od momentu uruchomienia usług. W takiej sytuacji analiza plików konfiguracyjnych nie pozwala na jednoznaczne określenie faktycznego stanu zabezpieczeń serwera.
Migracja istniejącego zestawu reguł iptables
iptables-translate konwertuje pojedynczą regułę i wyświetla jej odpowiednik w formacie nftables. Narzędzie to nie wprowadza żadnych zmian w systemie.
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPTnft add rule ip filter INPUT tcp dport 22 counter acceptiptables-restore-translate -f /etc/iptables/rules.v4 wykonuje to samo zadanie dla całego zapisanego zestawu reguł. Wynik działania tego polecenia należy traktować jako wstępny szkic. Konwersja odbywa się w sposób mechaniczny, reguła po regule, co skutkuje zachowaniem starych nazw tabel i łańcuchów, wygenerowaniem dwóch oddzielnych zestawów reguł dla IPv4 i IPv6 oraz brakiem wykorzystania zbiorów (sets), które stanowią o głównej wartości migracji. Należy ręcznie przepisać konfigurację na jedną tabelę inet, a następnie sprawdzić ją za pomocą nft -c -f, zanim zostanie wdrożona na serwerze produkcyjnym.
Adresy użyte w tych przykładach pochodzą z zakresów dokumentacyjnych 203.0.113.0/24 oraz 198.51.100.0/24, a enp1s0 jest nazwą interfejsu. Należy użyć własnych wartości z ip route show default oraz ip -br addr zamiast kopiować przykłady, ponieważ współczesne obrazy Ubuntu rzadko używają nazwy eth0 dla jakiegokolwiek interfejsu.
FAQ
Czy iptables jest przestarzałe w systemie Ubuntu?
Polecenie to nie zostanie usunięte i nadal działa w systemie Ubuntu 24.04. Zmiana dotyczy mechanizmu działania: iptables stanowi interfejs, który zapisuje reguły nftables poprzez backend iptables-nft. Weryfikację można przeprowadzić za pomocą iptables -V, co w wersji 24.04 zwraca iptables v1.8.10 (nf_tables). Starszy backend x_tables jest nadal dostarczany jako iptables-legacy i przechowuje całkowicie oddzielny zestaw reguł, dlatego należy stosować tylko jeden backend, a nie oba jednocześnie.
Czy potrzebuję drugiej reguły, aby cofnąć NAT dla ruchu powrotnego?
Nie. Mechanizm śledzenia połączeń przechowuje translację w momencie, gdy pierwszy pakiet połączenia dopasuje się do reguły nat, a każdy kolejny pakiet w obu kierunkach jest przepisywany na podstawie tego wpisu. sudo conntrack -L wyświetla to jako dwie krotki na połączenie: kierunek oryginalny oraz już odwróconą odpowiedź. Reguła napisana dla kierunku powrotnego nie zadziała, ponieważ pakiety powrotne nigdy nie docierają do łańcucha nat.
Czy mogę jednocześnie używać ufw oraz własnych reguł nftables?
Jest to technicznie możliwe, ale prowadzi do problemów. Każdy łańcuch bazowy podpięty pod hook jest wykonywany, więc aktywna polityka stanowi sumę obu zestawów reguł, uporządkowaną według priorytetu, a w przypadku równego priorytetu – według kolejności uruchomienia usług. Polecenie drop w którymkolwiek z nich jest ostateczne, a accept w Twoich regułach nie powstrzyma drugiego narzędzia przed odrzuceniem tego samego pakietu. Należy wybrać jedno narzędzie. W przypadku wyboru nftables, najpierw wyłącz ufw i upewnij się, że jego tablice zniknęły z sudo nft list ruleset.
Jak sprawić, aby reguły nftables przetrwały restart systemu Ubuntu?
Umieść zestaw reguł w /etc/nftables.conf, sprawdź go za pomocą sudo nft -c -f /etc/nftables.conf, a następnie wykonaj sudo systemctl enable --now nftables. Usługa nie jest domyślnie włączona, więc warto jednorazowo wykonać systemctl is-enabled nftables. Podczas generowania tego pliku należy zrzucać tylko własną tablicę za pomocą sudo nft -s list table inet filter, ponieważ pełny zrzut list ruleset przechwytuje również tablice zarządzane samodzielnie przez ufw oraz Docker.