SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor

iptables vs nftables op Ubuntu: hoe het werkt

Ontdek hoe iptables op Ubuntu 24.04 nftables-regels schrijft. Controleer uw back-end met iptables -V en zie waar ufw en Docker botsen in de actieve kernel-regelset.

iptables versus nftables op Ubuntu: welke gebruikt uw systeem?

Op Ubuntu 20.04 en later is het iptables-commando een interface die nftables-regels schrijft. Er draait één pakketfilter in de kernel, nftables, en twee user space-commando's programmeren dit. Een iptables -A INPUT-regel werkt nog steeds precies zoals voorheen, en de regel die het aanmaakt is een nftables-regel die nft kan weergeven.

Controleer dit op uw eigen server voordat u ervan uitgaat.

iptables -V
sudo update-alternatives --display iptables
sudo nft list ruleset

Op Ubuntu 24.04 (iptables 1.8.10, per augustus 2026) geeft iptables -V de waarde iptables v1.8.10 (nf_tables) weer. De naam tussen haakjes is de back-end. (nf_tables) betekent dat het commando communiceert met nftables. (legacy) staat voor de oude x_tables-back-end, die Ubuntu nog steeds meelevert als iptables-legacy en die de kernel als een volledig gescheiden regelset behoudt. update-alternatives toont de symlink achter die keuze: link currently points to /usr/sbin/iptables-nft.

Op een nieuwe VPS zonder geconfigureerde firewall geeft sudo nft list ruleset geen uitvoer. Die lege uitvoer is uw uitgangspunt. Voeg één regel toe op de oude manier en kijk opnieuw.

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;
  }
}

Uw iptables-regel is een nftables-regel. iptables-nft markeert de tabellen die het aanmaakt en nft geeft die waarschuwing weer wanneer het de markering ziet, omdat het bewerken van zo'n tabel met nft twee tools verantwoordelijk maakt voor dezelfde regels. Bekijk wat één commando heeft geproduceerd: een tabel die u geen naam heeft gegeven, en chains waar u niet om heeft gevraagd. Dat is het oude model, en het is het eerste dat verandert wanneer u direct nftables schrijft.

Wat iptables -L voor u verborgen houdt

iptables -L toont alleen de filter-tabel. NAT-regels (network address translation) vereisen iptables -t nat -L en mangle-regels vereisen -t mangle. IPv6 bevindt zich in een afzonderlijk commando, ip6tables, met een eigen kopie van elke regel. Een systeem kan er daarom in één overzicht schoon uitzien, terwijl er ergens anders pakketten worden geweigerd of herschreven door een tabel die u nooit heeft gecontroleerd.

sudo nft list ruleset print elke familie, elke tabel, elke chain en elke regel in één uitvoer. Op een server die u niet zelf heeft opgezet, is dat commando de snelste manier om te zien wat er daadwerkelijk geladen is. Voeg -a toe om regel-handles af te drukken; deze heeft u nodig om één specifieke regel te verwijderen in plaats van de gehele chain.

Twee gewoontes zijn het aanpassen waard terwijl u hier toch bent. iptables -L vertaalt adressen en poorten naar namen; op een systeem met een defecte resolver lijkt het commando daardoor vastgelopen: gebruik iptables -nvL. Controleer daarnaast of de legacy-backend leeg is met sudo iptables-legacy -nvL. Als er in beide back-ends regels bestaan, evalueert de kernel ze namelijk allebei, en toont geen van beide overzichten u het volledige beeld.

Tabellen en chains die u zelf aanmaakt, niet overerft

nftables begint met niets. Er is geen filter-tabel totdat u er een aanmaakt, en het woord filter is slechts een naam die u zelf heeft gekozen. Een chain ziet alleen pakketten wanneer u deze voorziet van een type, een hook en een prioriteit; dit maakt het een base chain. Een chain zonder deze eigenschappen wordt alleen bereikt via een expliciete jump of goto, waardoor deze geen resources verbruikt totdat er naar wordt gesprongen.

De andere grote verandering is de inet-familie. Eén inet-tabel verwerkt IPv4 en IPv6 in dezelfde regels. Dit elimineert een hele klasse fouten waarbij een poort is gesloten in iptables, maar wagenwijd openstaat in ip6tables. Dat verschil komt vaak genoeg voor om een eigen faalmodus op ufw-systemen te hebben.

Hieronder staat een volledige ruleset voor een server. Deze plaatst u in /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;
  }
}

Lees regel 2 twee keer. flush ruleset verwijdert elke tabel op de server, inclusief de tabellen die ufw en Docker zelf hebben aangemaakt. Lees verder voordat u dit uitvoert op een actieve server.

De eerste regel in de input chain doet het meeste werk. ct state established,related accept staat toe dat antwoorden op verbindingen die u zelf bent gestart, binnenkomen. Hierdoor hoeft de rest van de chain alleen beslissingen te nemen over nieuwe verbindingen. ct state invalid drop verwerpt pakketten die niet overeenkomen met een bekende verbinding of een geldige start. Alles daarna is een expliciet gat, en policy drop handelt de rest af.

Controleer het bestand voordat u het laadt en houd een tweede SSH-sessie open terwijl u dit doet. policy drop in combinatie met één typefout in de SSH-regel sluit u buiten van uw eigen server.

sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo nft list ruleset

nft -c -f parseert het bestand en rapporteert fouten zonder iets te laden. Een foutloze parse geeft helemaal geen uitvoer.

Sets vervangen lange regellijsten

tcp dport { 80, 443 } is een anonieme set: één regel en één opzoekactie, in plaats van één regel per poort. Een benoemde set zoals admin_ips gaat verder, omdat u deze kunt wijzigen terwijl de firewall actief is.

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 }

Geen herlaadactie, geen hernummering van regels, en de match blijft een enkele opzoekactie, ongeacht of de set vijf of vijftigduizend adressen bevat. flags interval is wat een set in staat stelt bereiken en CIDR (classless inter-domain routing) prefixen zoals 198.51.100.0/24 te bevatten. Zonder deze vlag accepteert de set alleen individuele adressen en mislukt het laden van de prefix.

Sets kunnen hun elementen ook automatisch laten verlopen.

set banned {
  type ipv4_addr
  flags timeout
  timeout 1h
}

Met een regel ip saddr @banned drop verwijdert elk element zichzelf een uur nadat het is toegevoegd. Dat is hoe de nftables-actie in fail2ban op Ubuntu 24.04 een adres blokkeert: het voegt een element toe aan een set, in plaats van een regel toe te voegen. Als poorten zelf nieuw terrein voor u zijn, begin dan bij wat een poort op Linux daadwerkelijk is.

Eén verschil zorgt voor verwarring tijdens een migratie. nftables telt geen pakketten tenzij u daarom vraagt. iptables -nvL toont altijd tellers voor elke regel. In nftables hebben alleen regels met het trefwoord counter tellers, dus voeg counter toe aan elke regel die u later mogelijk wilt debuggen.

Hoe hooks en prioriteiten de volgorde bepalen

Een base chain benoemt een hook; dit is het punt in het traject van het pakket waar de chain wordt uitgevoerd. prerouting wordt uitgevoerd vóór de routeringsbeslissing. input wordt uitgevoerd voor pakketten die aan deze machine zijn geadresseerd. forward wordt uitgevoerd voor pakketten die erdoorheen worden gerouteerd. output wordt uitgevoerd voor pakketten van lokale processen. postrouting wordt als laatste uitgevoerd, vlak voordat het pakket vertrekt.

Prioriteit bepaalt de volgorde van de chains binnen één hook, waarbij het laagste getal als eerste komt. nftables geeft de klassieke waarden namen: raw is -300, mangle is -150, dstnat is -100, filter is 0, srcnat is 100. Het schrijven van priority filter; is hetzelfde als het schrijven van priority 0;.

Nu het gedeelte dat bepaalt of het combineren van tools werkt. Elke base chain die op een hook is geregistreerd, wordt uitgevoerd in volgorde van prioriteit. Een pakket dat in uw chain wordt geaccepteerd, is nog niet klaar: accept beëindigt alleen die chain, en het pakket gaat door naar de volgende base chain op dezelfde hook. drop is overal definitief en stopt het pakket onmiddellijk. Een permissieve regel in uw tabel kan dus geen drop in de tabel van ufw ongedaan maken, ongeacht welke als eerste wordt uitgevoerd, en uw accept biedt geen bescherming tegen een chain die later wordt uitgevoerd.

Twee base chains op dezelfde hook met dezelfde prioriteit worden uitgevoerd in de volgorde van registratie, wat afhangt van welke service als eerste is gestart. Die volgorde kan na een herstart veranderen. Als u uw eigen tabel naast ufw moet draaien, geef deze dan een afwijkende prioriteit zodat de volgorde vastligt in plaats van afhankelijk te zijn van een raceconditie.

Waarom hoef ik geen reverse NAT-regel te schrijven?

Dit is de vraag die het vaakst verkeerd wordt begrepen, dus hier is het directe antwoord. Connection tracking schrijft de reverse-vertaling automatisch voor u. Er hoeft geen tweede regel te worden toegevoegd.

Een nat-tabel die beide helften van de gebruikelijke VPS-taak uitvoert, ziet er als volgt uit.

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
  }
}

Alleen het eerste pakket van een verbinding wordt getoetst aan een nat-chain. Wanneer een regel matcht, slaat de kernel die vertaling op in de connection tracking-tabel, naast het item van de verbinding. Elk volgend pakket, in beide richtingen, wordt herschreven op basis van het opgeslagen item; er wordt geen regel meer gelezen. Installeer de conntrack-tool en bekijk een live-item.

sudo apt install -y conntrack
sudo conntrack -L -p tcp
tcp 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=1

Lees dit als twee tuples. De eerste vier velden vormen de verbinding zoals de client deze verzond, geadresseerd aan 203.0.113.10:8080, uw publieke adres. De tweede vier zijn het antwoord dat de kernel verwacht, reeds omgedraaid en reeds vertaald, afkomstig van 10.0.0.5:80, de werkelijke backend. Die tweede tuple is de reverse-regel. De kernel schreef deze toen het eerste pakket matchte.

Schrijf dus geen regel voor de retourrichting. Deze kan niet matchen, omdat retourpakketten bij een bestaande verbinding horen en nooit een nat-chain bereiken. Mocht deze toch matchen, dan zou u een pakket vertalen dat de kernel al had gecorrigeerd.

Waar een herschrijving moet plaatsvinden, volgt uit hetzelfde mechanisme. Destination translation moet plaatsvinden in prerouting, vóór de routeringsbeslissing, omdat de routering de nieuwe bestemming moet zien; anders gaat het pakket naar de verkeerde plek. Verkeer dat de server zelf genereert, wordt om dezelfde reden afgehandeld in de output-hook. Source translation, inclusief het herschrijven van een bronpoort, moet plaatsvinden in postrouting, nadat de routering de uitgaande interface heeft gekozen. masquerade haalt het adres uit die interface, en de interface is pas bekend nadat de routering is uitgevoerd.

Daarom hoort een regel zoals deze aan het einde van het pad thuis en nergens anders.

ip saddr 10.0.0.0/24 oifname "enp1s0" snat ip to 203.0.113.10:20000-30000

Het poortbereik herschrijft de bronpoort samen met het bronadres; dit is wat u wilt wanneer veel interne clients één publiek adres delen en hun bronpoorten botsen. Een antwoord komt aan, geadresseerd aan een poort in dat bereik, conntrack matcht dit met het item, en de oorspronkelijke bronpoort wordt teruggezet voordat het pakket wordt afgeleverd. Nogmaals, geen tweede regel nodig.

Een praktisch gevolg: het wijzigen van een NAT-regel verplaatst geen verbindingen die al bestaan, omdat hun vertaling al is opgeslagen. Zij behouden het oude gedrag totdat hun items verlopen. sudo conntrack -D -p tcp --dport 8080 verwijdert de bijbehorende items en sudo conntrack -F verwijdert ze allemaal. Wees voorzichtig met de tweede optie op een NAT-box, omdat die opgeslagen vertalingen de huidige verbindingen in stand houden. Het wissen ervan verbreekt direct alle verbindingen die via de box lopen.

ufw en Docker schrijven beide hun eigen regels

ufw is een front-end voor iptables, wat op Ubuntu een front-end is voor nftables. Een ufw-systeem heeft daarom een ip filter-tabel vol met chains genaamd ufw-before-input, ufw-user-input enzovoort, plus een ip6 filter-kopie van dezelfde structuur. Bekijk dit met sudo nft list ruleset | grep ufw. Deze chains worden gegenereerd op basis van de bestanden in /etc/ufw, en ufw reload herschrijft deze volledig. Daarom verdwijnt een handmatig toegevoegde iptables-regel bij de volgende herlaadactie. De basis van ufw voor een VPS behandelt deze bestandsindeling.

Docker programmeert de firewall zelf en raadpleegt ufw niet. Het publiceren van een poort met -p 80:80 schrijft een DNAT-regel naar de nat-tabel en een accept-regel in het forward-pad; beide worden uitgevoerd vóór de user-chains van ufw. Het resultaat verrast iedereen de eerste keer: ufw deny 80 is geladen, en de container is nog steeds bereikbaar vanaf het internet. De oplossing bevindt zich in de DOCKER-USER-chain die Docker openlaat voor uw eigen regels, en waarom Docker-containers ufw negeren legt dit stapsgewijs uit. Bekijk wat er op uw systeem staat met sudo nft list ruleset | grep -i docker.

Lees nu die flush ruleset-regel uit de bovenstaande configuratie opnieuw. Deze verwijdert elke tabel, inclusief de tabellen die deze twee tools beheren. Op een Docker-host stoppen gepubliceerde poorten met werken totdat sudo systemctl restart docker de chains opnieuw opbouwt. Die ene regel is de meest voorkomende manier waarop mensen hun eigen services offline halen tijdens het opschonen van een firewall.

Regels die een herstart overleven

Geen van beide regelsets is uit zichzelf persistent. De kernel vergeet alles bij het afsluiten en beide kanten lossen dit op met een afzonderlijk pakket.

Voor nftables wordt /etc/nftables.conf gelezen door nftables.service. Ubuntu levert deze service uitgeschakeld, dus controleer dit voordat u erop vertrouwt.

systemctl is-enabled nftables
sudo nft -c -f /etc/nftables.conf
sudo systemctl enable --now nftables

Voor iptables is het pakket iptables-persistent, dat netfilter-persistent installeert en opslaat in /etc/iptables/rules.v4 en /etc/iptables/rules.v6.

sudo apt install -y iptables-persistent
sudo netfilter-persistent save

Draai ze niet allebei. Twee bestanden die elk beweren de firewall te bevatten, zullen uit elkaar gaan lopen. Het bestand dat als laatste wordt geladen wint, op een manier die niemand kan voorspellen door alleen de bestanden te lezen.

Er is een gerelateerde valkuil bij het dumpen van een actieve regelset. sudo nft -s list ruleset > /etc/nftables.conf legt alles vast wat op dat moment geladen is, inclusief de tabellen van ufw en Docker. Als u dit bij het opstarten herstelt, krijgt u een bevroren kopie van de regels die deze tools zelf verwachten op te bouwen, en vervolgens een tweede kopie zodra ze starten. Dump alleen uw eigen tabel met sudo nft -s list table inet filter. De vlag -s laat de tellers weg, aangezien deze niet in een configuratiebestand thuishoren.

Moet u ufw inschakelen op uw VPS?

Laat ufw ongemoeid, tenzij u functionaliteit nodig heeft die ufw niet kan bieden. ufw dekt de standaardtaken van een VPS: een standaard 'deny'-beleid met een beperkt aantal open poorten. Het vervangen hiervan door een handgeschreven ruleset enkel om het vervangen, levert u dezelfde firewall op, maar voegt een extra beheerlast toe.

Stap over op native tools wanneer uw behoeften buiten het model van ufw vallen: NAT en port forwarding, sets die u tijdens runtime bijwerkt, één regel die beide adresfamilies dekt, of ketenprioriteiten die u zelf bepaalt. Dit zijn gegronde redenen, en ufw biedt geen mogelijkheid om deze te configureren.

Als u voor native kiest, doe dit dan volledig. Voer sudo ufw disable en sudo systemctl disable --now ufw uit, bevestig met sudo nft list ruleset dat de tabellen zijn verwijderd en laad vervolgens uw eigen bestand. Een server die zowel ufw als een handgeschreven tabel draait, laat nog steeds verkeer door, maar het actieve beleid is nu de samenvoeging van twee rulesets die worden geëvalueerd in een volgorde die afhankelijk is van de opstartvolgorde van services. Hierdoor kan niemand op basis van de bestanden bepalen wat de server daadwerkelijk doet.

Migreren van een bestaande iptables-ruleset

iptables-translate converteert één regel en toont de nftables-vorm. Dit wijzigt niets op de server.

iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
nft add rule ip filter INPUT tcp dport 22 counter accept

iptables-restore-translate -f /etc/iptables/rules.v4 doet hetzelfde voor een volledige opgeslagen ruleset. Beschouw de uitvoer als een eerste concept. De conversie is mechanisch en regel voor regel; u krijgt dus de oude tabel- en chain-namen terug, twee afzonderlijke rulesets voor IPv4 en IPv6, en geen van de sets die de overstap de moeite waard maken. Herschrijf dit handmatig als één inet-tabel en controleer deze vervolgens met nft -c -f voordat u de configuratie op een actieve server toepast.

De adressen in deze voorbeelden zijn afkomstig uit de documentatiebereiken 203.0.113.0/24 en 198.51.100.0/24, en enp1s0 is een interfacenaam. Gebruik uw eigen gegevens uit ip route show default en ip -br addr in plaats van de mijne te kopiëren, aangezien huidige Ubuntu-images zelden iets eth0 noemen.

FAQ

Is iptables verouderd op Ubuntu?

Het commando verdwijnt niet en werkt nog steeds op Ubuntu 24.04. Wat is veranderd, is de onderliggende werking: iptables is een front-end die nftables-regels schrijft via de iptables-nft back-end. Controleer uw versie met iptables -V, die op 24.04 iptables v1.8.10 (nf_tables) weergeeft. De oude x_tables back-end wordt nog steeds meegeleverd als iptables-legacy en bevat een volledig gescheiden regelset; plaats regels daarom in slechts één back-end en niet in beide.

Heb ik een tweede regel nodig om NAT ongedaan te maken op de terugweg?

Nee. Connection tracking slaat de vertaling op wanneer het eerste pakket van een verbinding overeenkomt met een nat-regel; elk volgend pakket in beide richtingen wordt herschreven op basis van die opgeslagen invoer. sudo conntrack -L toont dit als twee tuples per verbinding: de oorspronkelijke richting, gevolgd door het reeds omgekeerde antwoord. Een regel die is geschreven voor de retourrichting helpt niet, omdat retourpakketten nooit een nat-chain bereiken.

Kan ik ufw en mijn eigen nftables-regels tegelijkertijd draaien?

Het werkt, maar u haalt zich problemen op de hals. Elke base chain op een hook wordt uitgevoerd, dus het actieve beleid is een combinatie van beide regelsets, gerangschikt op prioriteit en, bij gelijke prioriteit, op basis van welke service als eerste is gestart. Een drop in een van beide is definitief, en een accept in uw eigen regels voorkomt niet dat de andere tool hetzelfde pakket alsnog dropt. Kies één tool. Als u voor nftables kiest, schakel ufw dan eerst uit en bevestig dat de tabellen zijn verdwenen uit sudo nft list ruleset.

Hoe zorg ik ervoor dat nftables-regels een reboot overleven op Ubuntu?

Plaats de regelset in /etc/nftables.conf, controleer deze met sudo nft -c -f /etc/nftables.conf en voer vervolgens sudo systemctl enable --now nftables uit. De service is standaard niet ingeschakeld, dus het is raadzaam om systemctl is-enabled nftables eenmalig uit te voeren. Wanneer u dat bestand genereert, dump dan alleen uw eigen tabel met sudo nft -s list table inet filter, omdat een volledige list ruleset dump ook de tabellen bevat die ufw en Docker zelf beheren.