Firewall kuralları yazarken en çok yapılan hatalardan biri, kuralların sırasını önemsememek. “Kural eklendi, çalışıyor” deyip geçmek cazip geliyor, ancak yanlış sıralanmış kurallar hem güvenlik açıklarına hem de ciddi performans sorunlarına yol açabiliyor. Bunu bir güvenlik denetiminde bizzat yaşadım: Onlarca kural eklenmiş bir sunucuda bazı bloklar hiç çalışmıyordu çünkü üstteki geniş kapsamlı bir ACCEPT kuralı her şeyi geçiriyordu. iptables’ta kural sırası, kuralın kendisi kadar kritik.
iptables Nasıl Kural İşler?
iptables, gelen veya giden bir paketi değerlendirirken zincirdeki (chain) kuralları yukarıdan aşağıya sırayla tarar. İlk eşleşen kural uygulanır ve zincir tamamlanır. Yani bir paket ilk eşleşen kuralla karşılaştığında, altındaki kurallar artık o paket için işleme alınmaz.
Bu mekanizma çok basit görünüyor ama pratikte hayatı zorlaştıran pek çok senaryoya kapı açıyor. Düşün: Önce tüm trafiği kabul eden geniş bir kural, sonra belirli bir IP’yi engelleyen kural. Alt kural hiçbir zaman tetiklenmez çünkü paket zaten üstteki ACCEPT kuralıyla işlendi ve zinciri terk etti.
# Kural sırasını görüntülemek için:
iptables -L INPUT --line-numbers -v -n
# Çıktı örneği:
# Chain INPUT (policy DROP)
# num pkts bytes target prot opt in out source destination
# 1 1523 98K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
# 2 892 54K ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
# 3 0 0 DROP tcp -- * * 192.168.1.100 0.0.0.0/0
Üçüncü kural, 192.168.1.100 adresini engellemek istiyor. Ama bu IP port 22’ye bağlanmaya çalışırsa, ikinci kural onu kabul edecek ve üçüncü kural hiç devreye girmeyecek. Klasik bir sıralama hatası.
Kural Sıralamasının Temel Prensipleri
Özelden Genele Doğru Sıralama
En temel kural: özel ve kısıtlayıcı kurallar her zaman genel ve izin verici kuralların önüne gelmelidir. Engelleme kuralları, ilgili izin kurallarından önce tanımlanmalıdır.
# YANLIŞ sıralama:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -s 10.0.0.5 -p tcp --dport 80 -j DROP
# DOĞRU sıralama:
iptables -A INPUT -s 10.0.0.5 -p tcp --dport 80 -j DROP
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
Yanlış sıralamada 10.0.0.5 hiçbir zaman engellenmez. Doğru sıralamada ise önce bu IP kontrol edilir, eşleşirse DROP uygulanır; diğer tüm IP’ler bir sonraki kuralla ACCEPT’e devam eder.
Beyaz Liste ile Çalışırken Sıralama
Güvenli bir yaklaşım olarak varsayılan politikayı DROP yapıp yalnızca gerekli trafiği ACCEPT etmek yaygındır. Bu durumda sıralama biraz daha kritik hale gelir çünkü en çok eşleşen kuralları üste almak performans açısından önem kazanır.
# Varsayılan politikayı ayarla
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Loopback arayüzü her zaman en üste
iptables -A INPUT -i lo -j ACCEPT
# Mevcut bağlantıları kabul et (stateful kurallar)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Sonra spesifik servis kuralları
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
Burada ESTABLISHED,RELATED kuralının üstte olması hem güvenlik hem de performans açısından kritik. Mevcut oturumların paketleri sürekli gelen trafiktir ve bu kuralın üstte olması, her paketin bütün zinciri taramasını engeller.
Gerçek Dünya Senaryosu: Yanlış Sıralama Yüzünden Kilitlenme
Bir üretim sunucusunda şu kurallara sahip olduğunu varsay:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -s 0.0.0.0/0 -j DROP
Son kural tüm geri kalan trafiği kesiyor. Bu iyi görünüyor. Sonra birisi “güvenliği artırayım” diyerek şunu ekliyor:
iptables -I INPUT 1 -p tcp --dport 22 -m recent --update --seconds 60 --hitcount 5 -j DROP
Bu kural SSH brute force koruması için. Ancak daha sonra bir başkası aceleyle şunu ekliyor:
iptables -I INPUT 1 -s 203.0.113.50 -j ACCEPT
Şimdi 203.0.113.50 numaralı IP zincirinizde her şeyden önce ACCEPT görüyor. Bu IP’den gelen SSH brute force denemeleri artık hiçbir şekilde engellenmeyecek. Kural sayısı arttıkça böyle çakışmalar kaçınılmaz oluyor.
Kural Ekleme Yöntemleri ve Sıralama
iptables’ta bir kuralı eklemenin birden fazla yolu var ve her biri farklı pozisyona ekliyor:
-A (Append): Zincirin sonuna ekler -I (Insert): Belirtilen pozisyona ekler, pozisyon verilmezse başa ekler -R (Replace): Belirtilen satırdaki kuralın yerini alır
# Zincirin sonuna ekle
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
# İkinci pozisyona ekle
iptables -I INPUT 2 -p tcp --dport 8080 -j ACCEPT
# Dördüncü kuralı değiştir
iptables -R INPUT 4 -p tcp --dport 8080 -j DROP
# Belirli pozisyondaki kuralı sil
iptables -D INPUT 3
# Kural içeriğiyle sil
iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
Dinamik olarak kural ekleyip sildiğin ortamlarda -I ve -D komutlarını satır numarasıyla kullanmak yerine, mümkün olduğunca kural içeriğiyle silmek daha güvenli. Çünkü başka bir süreç araya kural eklediğinde satır numaraları kayabilir.
Performans Optimizasyonu: En Çok Eşleşeni Üste Al
Kural sıralamasının güvenlik boyutunun yanında ciddi bir performans boyutu da var. Her gelen paket, eşleşen bir kural bulana kadar zinciri tarar. Eğer en sık eşleşen kural en alttaysa, her paket için gereksiz iş yapılmış olur.
# Paket ve byte sayaçlarını görmek için:
iptables -L INPUT -v -n --line-numbers
# Sayaçları sıfırla
iptables -Z INPUT
# Birkaç dakika sonra tekrar bak
iptables -L INPUT -v -n --line-numbers
Yüksek trafikli bir sunucuda pkts sütununa bak. En çok paket alan kural hangisi? O kural zincirin üstünde olmalı. Düşük paket sayılı kural altta olabilir.
Gerçek bir web sunucusu senaryosunda şu sıralama mantıklıdır:
# 1. Loopback (her zaman önce)
iptables -A INPUT -i lo -j ACCEPT
# 2. Mevcut bağlantılar (en yüksek hacimli trafik)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 3. HTTP/HTTPS (yüksek hacimli)
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 4. SSH (düşük hacimli ama kritik)
iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
# 5. Diğer servisler
iptables -A INPUT -p tcp --dport 3306 -s 10.0.1.0/24 -j ACCEPT
# 6. Engelleme kuralları ve varsayılan düşürme
iptables -A INPUT -j LOG --log-prefix "INPUT-DROP: "
iptables -A INPUT -j DROP
ipset ile Büyük Listeleri Optimize Etme
Yüzlerce IP adresini tek tek kural olarak eklemek hem sıralama karmaşıklığı yaratır hem de performansı düşürür. Bu durumda ipset devreye girer.
# ipset yükle (Debian/Ubuntu)
apt-get install ipset
# IP seti oluştur
ipset create BLOCKED_IPS hash:ip
# IP ekle
ipset add BLOCKED_IPS 192.168.1.100
ipset add BLOCKED_IPS 203.0.113.50
ipset add BLOCKED_IPS 198.51.100.25
# Tek kural olarak iptables'a ekle
iptables -I INPUT 1 -m set --match-set BLOCKED_IPS src -j DROP
# Seti kaydet
ipset save > /etc/ipset.conf
# Seti geri yükle
ipset restore < /etc/ipset.conf
Bu yaklaşımla yüzlerce IP adresi tek bir iptables kuralına indirgenmiş olur. Hem zincir tarama süresi kısalır hem de kural yönetimi kolaylaşır. 1000 ayrı DROP kuralı yerine tek bir ipset kuralı, performans farkını hissettirmenin garantili yolu.
Gerçek Dünya Senaryosu: E-Ticaret Sunucusu Kural Optimizasyonu
Bir e-ticaret platformu düşün. Yoğun saatlerde saniyede binlerce HTTP isteği geliyor. Yıllar içinde birikmiş 45 iptables kuralı var ve sistem yöneticisi zaman zaman ağ gecikmesi şikayetleri alıyor.
Mevcut durum analizi:
# Mevcut kuralları ve istatistikleri gör
iptables -L INPUT -v -n --line-numbers | head -20
# Çıktı incelendiğinde:
# - Kural 38: ESTABLISHED,RELATED - 2.1M paket (en fazla!)
# - Kural 3: ACCEPT port 443 - 890K paket
# - Kural 1: Belirli IP bloklama - 12 paket
ESTABLISHED,RELATED kuralı 38. sırada. Bu demek oluyor ki her mevcut bağlantı paketi, 37 kurulu geçtikten sonra eşleşiyor. Optimizasyon:
#!/bin/bash
# Optimize edilmiş kural seti
# Mevcut kuralları temizle
iptables -F INPUT
iptables -F OUTPUT
iptables -F FORWARD
# Varsayılan politika
iptables -P INPUT DROP
# 1. Loopback
iptables -A INPUT -i lo -j ACCEPT
# 2. ESTABLISHED/RELATED - EN ÜSTE taşındı
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 3. Yüksek hacimli servisler
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 4. API sunucusu (orta hacim)
iptables -A INPUT -p tcp --dport 8443 -s 10.0.0.0/8 -j ACCEPT
# 5. Veritabanı (düşük hacim, iç ağdan)
iptables -A INPUT -p tcp --dport 5432 -s 10.0.1.0/24 -j ACCEPT
# 6. SSH (çok düşük hacim)
iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
# 7. ICMP ping
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
# 8. Diğer her şeyi düşür
iptables -A INPUT -j DROP
echo "Kurallar uygulandı"
iptables -L INPUT --line-numbers -n
Bu optimizasyondan sonra ağ gecikmesindeki düşüş genellikle ölçülebilir düzeyde olur. Paketin geçtiği ortalama kural sayısı 25’ten 2-3’e düşer.
Kullanıcı Tanımlı Zincirlerle Yapıyı Düzenlemek
Kural sayısı arttıkça tek bir INPUT zinciri yönetilemez hale gelir. Kullanıcı tanımlı zincirler hem okunabilirlik hem de ufak çaplı performans avantajı sağlar.
# Yeni zincirler oluştur
iptables -N SSH_RULES
iptables -N WEB_RULES
iptables -N DB_RULES
iptables -N BLOCK_LIST
# SSH kurallarını ayrı zincire al
iptables -A SSH_RULES -s 10.0.0.0/8 -j ACCEPT
iptables -A SSH_RULES -m recent --name SSH --update --seconds 300 --hitcount 5 -j DROP
iptables -A SSH_RULES -m recent --name SSH --set
iptables -A SSH_RULES -j ACCEPT
# Web kuralları
iptables -A WEB_RULES -p tcp --dport 80 -j ACCEPT
iptables -A WEB_RULES -p tcp --dport 443 -j ACCEPT
# Engel listesi
iptables -A BLOCK_LIST -s 192.168.100.0/24 -j DROP
iptables -A BLOCK_LIST -s 10.99.0.0/16 -j DROP
iptables -A BLOCK_LIST -j RETURN
# Ana INPUT zincirine bağla
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -j BLOCK_LIST
iptables -A INPUT -p tcp --dport 22 -j SSH_RULES
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j WEB_RULES
iptables -A INPUT -j DROP
BLOCK_LIST zinciri RETURN ile bitiyor. Bu, eşleşme olmadığında paketin ana zincire geri döneceği anlamına gelir. Engel listesindeki bir IP’ye eşleşme olursa DROP uygulanır; diğerleri normal akışa devam eder.
Kural Setini Kaydetme ve Yükleme
Tüm bu çalışma, sunucu yeniden başlatıldığında kaybolmamalı:
# Debian/Ubuntu - kuralları kaydet
apt-get install iptables-persistent
netfilter-persistent save
# Veya manuel
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
# Kuralları geri yükle
iptables-restore < /etc/iptables/rules.v4
# RHEL/CentOS - eski yöntem
service iptables save
# systemd servis dosyası ile yükleme (modern yaklaşım)
cat > /etc/systemd/system/iptables-restore.service << 'EOF'
[Unit]
Description=iptables kurallarini yukle
Before=network.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
systemctl enable iptables-restore
systemctl start iptables-restore
Kural Sıralamasını Test Etme
Değişiklik yaptıktan sonra test etmeden üretim ortamına almak riskli. Önce test et:
# Belirli bir paketin hangi kural tarafından yakalandığını izle
iptables -I INPUT 1 -s TEST_IP -j LOG --log-prefix "TEST-TRACE: "
# Kernel izleme (daha detaylı)
modprobe nf_log_ipv4
sysctl net.netfilter.nf_log.2=nf_log_ipv4
iptables -t raw -A PREROUTING -s 192.168.1.100 -j TRACE
iptables -t raw -A OUTPUT -d 192.168.1.100 -j TRACE
# /var/log/kern.log veya journalctl ile izle
journalctl -f | grep "TRACE"
# Test sonrası temizle
iptables -t raw -D PREROUTING -s 192.168.1.100 -j TRACE
iptables -t raw -D OUTPUT -d 192.168.1.100 -j TRACE
Trace mekanizması, paketin hangi tabloda, hangi zincirde, hangi kuralla eşleştiğini satır satır gösterir. Karmaşık kural setlerinde bir paketin neden engellendiğini veya neden geçtiğini anlamak için biçilmiş kaftan.
Sık Yapılan Hatalar ve Kaçınma Yolları
ESTABLISHED/RELATED’ı alta koymak: Mevcut bağlantı paketleri en sık gelen paketlerdir. Bu kural mutlaka üstte olmalı.
Geniş ACCEPT kuralından sonra engelleme yazmak: /24 veya 0.0.0.0/0 gibi geniş bir ACCEPT kuralı varsa, ondan sonra gelen özel DROP kuralları işe yaramaz.
-A ile körü körüne eklemek: Her -A komutu kuralı sona ekler. Mevcut yapıyı kontrol etmeden -A kullanmak sıralamayı bozar.
Test etmeden production’a almak: Her zaman iptables-restore --test ile sözdizimi kontrolü yap. Kritik değişiklikler için 5 dakika içinde geri alacak bir cron kur:
# Değişiklik yapmadan önce 5 dakika içinde geri alacak zamanlayıcı kur
echo "iptables-restore < /etc/iptables/rules.v4.backup" | at now + 5 minutes
# Değişikliklerini yap, test et, çalışıyorsa zamanlayıcıyı iptal et
# at -l ile işi gör, atrm JOB_ID ile iptal et
Kural sayısını şişirmek: Her yeni gereksinim için yeni kural eklemek yerine mevcut kuralları ipset veya multiport ile birleştir.
# Kötü: 5 ayrı kural
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
iptables -A INPUT -p tcp --dport 8443 -j ACCEPT
iptables -A INPUT -p tcp --dport 3000 -j ACCEPT
# İyi: Tek kural
iptables -A INPUT -p tcp -m multiport --dports 80,443,8080,8443,3000 -j ACCEPT
Sonuç
iptables kural sıralaması, sadece “çalışıyor mu” sorusunun ötesinde “doğru çalışıyor mu” ve “verimli çalışıyor mu” sorularını birlikte yanıtlamayı gerektiriyor. Yıllar içinde birikmiş ve kimsenin dokunmaya cesaret edemediği kural setleri her ortamda mevcut. Bu kurallara düzen getirmek için yapman gereken adımlar basit: önce -v -n --line-numbers ile mevcut durumu gör, paket sayaçlarına bak, en yoğun kuralları üste taşı, engelleme kurallarını ilgili izin kurallarının önüne al, büyük listeleri ipset ile yönet, kullanıcı tanımlı zincirlerle yapıyı modülerleştir.
Performans gözle görülür derecede iyileşebilir. Daha da önemlisi, kural setinin okunabilir ve yönetilebilir olması operasyonel hataları azaltır. Yanlış sıralanmış bir güvenlik kuralı, olmayan bir güvenlik kuralından daha tehlikeli olabilir çünkü her şeyin yolunda gittiğini sanırsın.
Yeni bir sunucu kuruyorsan kural setini baştan doğru planla. Mevcut bir sistemi yönetiyorsan periyodik olarak kural sırasını gözden geçir. iptables hata vermez, yapmasını istediğini yapar; sorun genellikle bizim istediğimizi doğru ifade edip edemediğimizde yatıyor.