nftables ile Modern Linux Guvenlik Duvari Yapilandirmasi
Debian 10'dan bu yana sistemin varsayılan güvenlik duvarı katmanı nftables. Ubuntu 20.10 ile birlikte iptables komutları bile arka planda iptables-nft uyumluluk katmanından geçip nftables'a çevriliyor. Yani hâlâ iptables -A INPUT yazıyorsanız, emirlerinizi tercüman üzerinden eski bir API'ye söylüyorsunuz demektir. Doğrudan nft yazmak daha temiz sözdizimi, daha iyi performans ve iptables'ta karşılığı olmayan set, map ve meter özellikleri demek.
Tek zincirde IPv4 ve IPv6
Eski dünyada IPv4 için iptables, IPv6 için ip6tables, ARP için arptables, köprü trafiği için ebtables ayrı ayrı araçlardı. nftables bunları tek çatı altında topladı ve adres aileleri (address family) ile ayırt ediyor: ip sadece IPv4, ip6 sadece IPv6, arp ARP çerçeveleri, bridge köprü trafiği, netdev arayüz bazlı ingress için.
Günlük işlerde kullanılacak aile inet. IPv4 ve IPv6 paketlerini aynı zincirde işliyor. Ayrı ip ve ip6 tabloları açmak, her kuralın iki kopyasını tutup ömür boyu senkronize etmek anlamına geliyor. İstisnai bir durum olmadıkça gerek yok.
Tablo, zincir, kural
Üç kavram üzerine kurulu her şey. Tablo bir isim alanı: zincirleri, setleri ve durumlu nesneleri içinde barındırıyor. iptables'taki filter, nat, mangle gibi sabit tabloların aksine tabloları siz adlandırıyorsunuz ve aynı ailede birden fazla tablo bağımsız çalışabiliyor.
Zincirler iki türlü. Temel zincir (base chain) kernel'deki bir netfilter kancasına (hook) bağlı ve paketleri gerçekten görüyor. Normal zincirler ise sadece başka kurallardan jump ile çağrılıyor, karmaşık politikaları bölmek için kullanılıyor. Temel zincir oluştururken üç şey zorunlu: tip, kanca ve öncelik. Filtreleme için type filter, kancalardan input makineye gelen, forward yönlendirilen, output makineden çıkan paketleri yakalıyor. prerouting ve postrouting NAT senaryolarının adresleri.
Küçük bir sunucu kuralları, satır satır:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iifname "lo" accept
ct state invalid drop
ct state established,related accept
tcp dport 22 ct state new accept
tcp dport { 80, 443 } accept
ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } accept
ip6 nexthdr icmpv6 icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert, packet-too-big } accept
counter drop
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
flush ruleset dosyanın ilk satırı olmalı, yoksa her yüklemede kurallar üst üste birikir. policy drop ile hiçbir kurala takılmayan paket çöpe gidiyor; izin verilen trafik kural kural tanımlanıyor. Loopback her zaman açık kalmalı, ct state established,related satırı da bağlantı takibinin cevap trafiğini tek kuralda hallediyor.
Öncelik parametresi artı ya da eksi bir tam sayı, aynı kanca içinde küçük değer önce işletiliyor. Sayı yerine isim de kabul ediliyor: filter 0, dstnat -100, srcnat 100. NAT zincirleri için -200 alt sınırı var çünkü conntrack bu öncelikte kancaya takılıyor.
IPv6'yı sessizce öldürmeyin
Policy drop uygulanan bir kurallar setinde ICMPv6'ya izin vermeyi unutursanız IPv6 ağınız kırılır ve bunun nedenini bulmak saatlerinizi alır. Komşu keşfi (nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert) ve packet-too-big mesajları geçmezse adres çözümlemesi çalışmaz, MTU pazarlığı tıkanır. IPv4 ICMP'ye gösterilen hassasiyetin aynısı, hatta fazlası ICMPv6 için şart.
Bin kural yerine bir set
iptables'ta 10.000 IP adresini engellemek 10.000 ardışık karşılaştırma demekti; her paket eşleşme bulana kadar listeyi baştan sona yürüyordu. nftables'ta 10.000 elemanlı bir set hash araması: kümenin büyüklüğünden bağımsız sabit zamanda bitiyor. Better Stack'in karşılaştırmasında binlerce kural söz konusu olduğunda nftables iptables'tan 10 ila 100 kat daha hızlı çıkmış; az sayıda kuralda fark hissedilmiyor.
İki set türü var. Anonim setler küme parantezi içinde kural yazıldığı yerde tanımlanıyor ve kural silinince set de siliniyor. Adlandırılmış setler tablo içinde tanımlanıp kurallarda isimle kullanılıyor:
table inet filter {
set tcp_accepted {
type inet_service
flags interval
elements = { 22, 80, 443, 60000-61000 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
tcp dport @tcp_accepted accept
drop
}
}
Setler paket yolu üzerinden de güncellenebiliyor; kernel dinamik kara listeler kurmak için set ifadesini destekliyor. Paket akışından beslenen setlerde hafıza tükenmesini ve sonsuz büyümeyi engellemek şart: resmi manpage, kurallar içerisinden güncellenecek her sette hem azami küme boyutu hem zaman aşımı belirtilmesini şart koşuyor.
Arayüz eşleşmesinde ince ayrım
iif arayüzün index numarasıyla, iifname ismiyle eşleştiriyor. iif "eth0" yazdığınızda arayüz o anda mevcut olmak zorunda; kural eklendikten sonra arayüz başka bir isim alsa da eşleşme sürüyor çünkü kernel index'i saklıyor. tun/tap ya da ppp gibi sonradan doğan arayüzlerde iifname doğru tercih, kuralı eklerken arayüzün var olmasını beklemiyor. Her ikisinde sona yıldız ekleyerek önek eşleşmesi de yapılabiliyor.
NAT tarafı
Masquerade için nat tipinde postrouting zinciri gerekiyor. Sanal makinelere ya da WireGuard alt ağına internet çıkışı vermek tek satır:
table inet nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname "eth0" ip saddr 10.10.0.0/24 masquerade
}
}
Port yönlendirme prerouting tarafında. Dışarıdan 8080'e geleni iç sunucunun 80'ine taşıyan kural şöyle:
table inet nat {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
iifname "eth0" tcp dport 8080 dnat to 192.168.122.10:80
}
}
NAT zincirlerinde sadece bağlantının ilk paketi dolaşıyor, sonraki paketler conntrack kaydından geçiyor. Bu yüzden NAT kuralları filtre zincirlerinden bağımsız düşünülüyor.
Sayaç davranışı alışkanlık bozuyor
iptables'ta -nvL çıktısı her kuralın paket ve bayt sayacını otomatik gösteriyordu. nftables'ta durum farklı: açıkça counter anahtar kelimesini yazmadığınız sürece hiçbir şey sayılmıyor. Hangi kuralların üzerinden trafik geçtiğini görmek istiyorsanız kritik yollara counter accept ya da counter drop yazın, sonra nft list ruleset çıktısında sayıları okuyun. Kural sayısı çoksa sayaç eklemek maliyet yaratıyor, bu yüzden her yere değil gerçekten merak ettiğiniz yerlere koyun.
Kalıcılık ve otomatik yükleme
Komut satırından girilen her şey bellekte yaşıyor, yeniden başlatmada gidiyor. Kalıcı kurallar /etc/nftables.conf dosyasında tutuluyor ve systemd servisi açılışta bunu yüklüyor:
nft -c -f /etc/nftables.conf
nft -f /etc/nftables.conf
systemctl enable nftables --now
-c bayrağı dosyayı uygulamadan doğruluyor, sözdizimi hatasını yüklemeye kalkmadan yakalıyor. Çalışan kuralları dosyaya dökmek için nft list ruleset > /etc/nftables.conf komutu kullanılıyor; tasarım gereği listenin çıktısı tekrar girdi olarak verilebiliyor, iptables-save ve iptables-restore ikilisinin nftables karşılığı bu.
Bir uyarı: dosyanın başındaki flush ruleset kernel'deki tüm tabloları siler, Docker'ın, libvirt'nin, firewalld'nin ya da barındırma panellerinin kendi tabloları dahil. Böyle bir dosyayı yalnızca kurallar setinin tek sahibi olduğunuz makinelerde uygulayın. O daemonların tablolarıyla bir arada yaşamak istiyorsanız dosyayı kendi tablonuzla sınırlayın. Uzak sunucuda değişiklik yapmadan önce konsol erişiminizi ve geri dönüş yolunuzu hazır tutun; SSH üzerinden kural kural ekleme yapmak yerine izinleri de içeren tam dosyayı tek seferde yükleyin.
Eski kurallardan göç
Zor bir süreç değil. iptables-save ile mevcut kuralları dışa aktarıp iptables-translate aracından geçirmek tek tek komutları nft sözdizimine çeviriyor. Elde edilen çıktıyı elden geçirip set ve map kullanacak şekilde sadeleştirmek, birebir kopyalamaktan daha iyi sonuç veriyor; çünkü göçün asıl kazancı mimari değişiklikte.
Bir de güvenlik notu: nftables kernel tarafında çalıştığı için kernel açıkları doğrudan etkiliyor. 2025'te raporlanan CVE-2025-40206 gibi gerçek zafiyetler çıktı, kernelinizi güncel tutmak güvenlik duvarı kurallarınızı doğru yazmak kadar önemli.
Dosya içinde düzen
Kurallar büyüdükçe tek dosya okunmaz hale geliyor. nft betikleri include ile parçalara ayrılabiliyor, hatta joker karakter destekleniyor: include "/etc/nftables/rulesets/*.nft" satırı dizindeki tüm dosyaları sırayla dahil ediyor. Değişken de tanımlanabiliyor, define wan_if = "eth0" yazıp kurallarda $wan_if olarak kullanmak, arayüz ismi değiştiğinde tek satırı düzeltmek demek.
Ansible ya da Salt ile filo yönetiyorsanız nftables'ın atomik yükleme davranışı işinizi kolaylaştırıyor: dosyanın tamamı ya yükleniyor ya hiçbir şey değişmiyor, iki koşunun çakışıp kuralları ikiye katlaması gibi bir risk yok. Kuralları Git'te tutup test ortamında doğrulayıp üretime nft -f ile tek hamlede taşımak, filo ölçeğinde güvenlik duvarı yönetimin en rahat şekli.