Teknoloji

nftables ile VPS Güvenlik Duvarı Rehberi: Rate Limit, Port Knocking ve IPv6 Kuralları Nasıl Tatlı Tatlı Kurulur?

Giriş: Uzak Bir VPS, Açık Bir Port ve Kafaya Takılan Bir Soru

Hiç başınıza geldi mi? Gece yarısı bir bildirim düşer: sunucunda garip bağlantı denemeleri var. Ben ilk fark ettiğimde, sanki pencereden içeri usulca bakıp giden biri gibi hissetmiştim. Birkaç dakika bakındım, log’ları kurcaladım ve düşündüm: bunca işin arasında şu güvenlik duvarını doğru ve akıllı şekilde kurmak neden bu kadar erteleniyor? Aslında mesele zor değil; doğru araç, net bir iskelet ve birkaç püf noktası. İşte bu yazıda, tam da bunları beraber adım adım kuracağız.

Elimizdeki araç nftables. Sisteminizde zaten var olma ihtimali yüksek, üstelik bir taşla iki kuş: IPv4 ve IPv6 için tek bir dil. Rate limit ile saldırı denemelerini yumuşatacağız, port knocking ile kapıları gizli bir tıklamayla açtıracağız, IPv6 kurallarıyla da o yeni ve geniş dünyada işleri rayına oturtacağız. Arada gerçek hayattan örnekler, birkaç pratik uyarı ve başıma gelen ufak tefek aksilikler de olacak. Hadi gelin, önce küçük bir iskelet kural seti oluşturup sonra üstüne kat çıkalım.

Neden nftables? Birkaç Sade Cümleyle

İtiraf edeyim, ben de uzun süre eski alışkanlıkların konforunda kaldım. Sonra bir gün yeni bir VPS kurarken, tek bir inet tablosuyla hem IPv4 hem IPv6 için kuralları yazmanın verdiği huzuru yaşadım. Tek bir dosya, tek bir akış, daha az kafa karışıklığı. Bir süre sonra, log’ları okurken kuralların dilinin ne kadar okunabilir olduğunu fark ettim: chain’ler, set’ler, map’ler… karmaşık değil, sadece alışkanlık gerektiriyor.

Mesela şöyle düşünün: SSH kapısını açacaksınız, ama sadece belirli bir hızda yeni bağlantı denemesine izin verip gerisini nazikçe bekleteceksiniz. Nftables ile bu işi tek bir satırda yapabiliyorsunuz. Ya da IP’yi geçici bir listeye ekleyip ikinci bir porta tıklayınca kapıyı açan küçük bir port knocking oyunu kurmak istiyorsunuz. Yine mümkün. Başlangıçta sihir gibi geliyor, sonra küçük ama güçlü pratikler bütünü olduğunu görüyorsunuz.

Hazırlık: Küçük Adımlar, Büyük Rahatlık

Servisi etkinleştir ve bir yedek al

Önce sistemde nftables servisinin hazır olduğundan emin olun. Çok sürpriz çıkmaz; ama kurallarınızı kalıcı hale getirmek için servisin etkin olması iyi fikir. Bir de kendinize iyilik yapın: halihazırdaki kuralları bir dosyaya kaydedin. Bir şeyler ters giderse geri dönüşünüz olsun.

nft list ruleset > /root/nft.backup
systemctl enable --now nftables

Kural dosyasıyla çalışırken değişiklikleri tek atışta yüklemek güven verir. Ancak en kritik uyarı şu: SSH erişimini kaybetmemek için önce yeni kuralları test edin, ardından ayakta bir SSH oturumunu açık bırakın. Hatta bazen kendime küçük bir güvenlik kemeri bağlarım: bir geri dönüş komutu planlar, eğer 60 saniye içinde her şey yolunda değilse eski kurallara döndürürüm.

( sleep 60; nft -f /root/nft.backup ) &
# 60 saniye içinde işler yolundaysa, bu geri dönüşü iptal edersiniz

İskelet: Temiz, Mantıklı ve Sakin Bir Kurallar Dizisi

Şimdi temel bir iskelet kural seti yazalım. Varsayılan politikalar sıkı olsun: gelenleri düşür, giden serbest kalsın, forward genelde yok. Loopback’e dokunma, established/related trafiğe yol ver, ardından bildiğimiz servisleri sırayla açalım.

flush ruleset

table inet filter {
  sets {
    # Port knocking için IPv4 ve IPv6 set'leri (aşağıda dolduracağız)
    knock1_v4 { type ipv4_addr; flags timeout; }
    knock2_v4 { type ipv4_addr; flags timeout; }
    knock1_v6 { type ipv6_addr; flags timeout; }
    knock2_v6 { type ipv6_addr; flags timeout; }
  }

  chains {
    input {
      type filter hook input priority 0; policy drop;

      # Kendimiz konuşabilelim
      iif lo accept

      # Halihazırda kurulmuş oturumlara dokunma
      ct state { established, related } accept

      # Ping ve arkadaşları - rate limit ile
      ip protocol icmp icmp type { echo-request, echo-reply, time-exceeded, destination-unreachable } limit rate 10/second burst 20 packets accept
      ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, destination-unreachable, packet-too-big, time-exceeded, parameter-problem, neighbor-solicitation, neighbor-advertisement, router-solicitation, router-advertisement } limit rate 10/second burst 20 packets accept

      # HTTP/HTTPS - örnek olarak açık
      tcp dport { 80, 443 } ct state new limit rate 300/second burst 600 packets accept
      tcp dport { 80, 443 } accept

      # SSH: bunu ileride port knocking ile daha akıllı açacağız
      tcp dport 22 ct state new limit rate 20/minute burst 10 packets accept
      tcp dport 22 accept

      # Gereksiz her şeyi düşür, çok konuşmadan
      counter drop
    }

    forward {
      type filter hook forward priority 0; policy drop;
    }

    output {
      type filter hook output priority 0; policy accept;
    }
  }
}

Burada iki küçük detay var. İlki, ICMP ve ICMPv6 mesajlarını tamamen kapatmayın; özellikle IPv6 için bu mesajlar ağın temel işaretleri gibi çalışır. İkincisi, rate limit’i HTTP/HTTPS için biraz yüksek bıraktım, çünkü gerçek trafik dalgalanmaları moral bozmasın. SSH tarafında ise daha sıkı bir limit var. Birazdan port knocking ekleyince SSH’u daha da saklayacağız.

Rate Limit: Saldırganı Yormak, Meşru Kullanıcıyı Üzmeyecek Kadar

Rate limit deyince akla bazen kapıyı zorla kilitlemek geliyor, halbuki niyet başka: servisinizin nefesini dengeli tutmak. Bir kapıdan saniyede yüzlerce yeni deneme akarsa, CPU’ya gereksiz yük biner. Birkaç basit kural, hem saldırganı yorar hem de meşru kullanıcının deneyimini bozmaz.

SSH için genelde dakikada belli sayıda yeni ct state new bağlantıya izin verip geri kalanı bekletmek iyi sonuç veriyor. Benim tecrübemde, dakikada 20 yeni deneme çoğu ortam için yeterli. Birkaç kez bu sayıyı düşürdüğüm oldu, ardından yeni bir geliştirici eklenince sınırlar kendini belli etti; bu yüzden ortamınıza göre ayarlayın.

# SSH - yeni bağlantıları sınırlı hızda kabul et
add rule inet filter input tcp dport 22 ct state new limit rate 20/minute burst 10 packets accept

Ping tarafında da benzer bir strateji iş görür. Tamamen kapatmak yerine, hem v4 hem v6 için makul bir hız. Çünkü bazen uptime kontrol eden sistemler veya otomasyon araçları ping atar; onların kalbini kırmayalım.

# ICMP ve ICMPv6 - nazik bir sınır
add rule inet filter input ip protocol icmp icmp type echo-request limit rate 10/second burst 20 packets accept
add rule inet filter input ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate 10/second burst 20 packets accept

Bu arada, uygulama seviyesinde rate limit bambaşka bir dünya. HTTP için Nginx ya da uygulama çerçevesinde farklı stratejiler gerekir. Sunucunun giriş kapısında yaptığımız limit, ağ seviyesinde küçük bir filtre gibi düşünülmeli.

Port Knocking: Kapıyı Gizleyip Doğru Ritmi Çalana Açmak

Port knocking’i ilk kez kurduğumda, sanki gizli bir kulüp kapısı gibi hissettim. Doğru kapıyı önce şöyle bir tıklarsınız, sonra bir diğerini, en son asıl kapı size açılır. Temel fikir bu. Güvenlik tekniği olarak mucize değil; sadece gizliliği artıran pratik bir katman. Hatalı yapılandırılırsa can sıkabilir, o yüzden çok net adımlar atalım.

Dinamik set’lerle iki adımlı bir oyun

Amacımız şu: belirlediğimiz iki farklı porta sırasıyla tıklayan IP adresini, belirlediğimiz süre boyunca bir set’te tutalım. İlk tıklamada knock1 set’ine, ikinci tıklamada knock2 set’ine geçsin. Sonra SSH’ı sadece knock2’daki adreslere açalım. IPv4 ve IPv6’yı ayrı ayrı düşünmek işimizi kolaylaştırıyor.

# Önce set'lerimizi tanımladık (üstte). Şimdi kurallar:

# 1. tık: IPv4
add rule inet filter input tcp dport 31001 ct state new add @knock1_v4 { ip saddr timeout 20s } counter drop
# 2. tık: IPv4 (ilk tık atanlar ikinci porta vurunca terfi eder)
add rule inet filter input ip saddr @knock1_v4 tcp dport 31002 ct state new add @knock2_v4 { ip saddr timeout 30s } counter drop

# 1. tık: IPv6
add rule inet filter input tcp dport 31001 ct state new add @knock1_v6 { ip6 saddr timeout 20s } counter drop
# 2. tık: IPv6
add rule inet filter input ip6 saddr @knock1_v6 tcp dport 31002 ct state new add @knock2_v6 { ip6 saddr timeout 30s } counter drop

# SSH'ı, sadece ikinci set'te olanlara aç
add rule inet filter input ip saddr @knock2_v4 tcp dport 22 accept
add rule inet filter input ip6 saddr @knock2_v6 tcp dport 22 accept

# Ve son olarak, SSH'a kalan herkesi düşür (ya da alt tarafta varsayılan drop'a bırakalım)

Burada süreleri kısa tuttum. Çünkü amaç kapıyı sürekli açık bırakmak değil, doğru tıklamayı yapanların birkaç dakika içinde SSH’a bağlanmasını sağlamak. Ben bazen ikinci set’in süresini 120 saniyeye çıkarıyorum. Böylece tıklama sırasını bilenler rahatça girişini yapıyor, başkası ise kapının açık olduğunu fark etmiyor bile.

Port knocking’i kullanırken bir mini not: Bir yerlerde tıklama sırasını otomatik yapan bir küçük script taşıyın. Benimkisi şöyle basit; netcat ya da curl ile iki porta birden dokundurup ardından SSH denemesi yapıyor. Böylece yeni bir VPS’e bağlanırken takılmıyorum.

# Basit bir knocking örneği (IPv4/IPv6 aynı portları kullanır)
# İlk tık
nc -zv YOUR_SERVER 31001 || true
# İkinci tık
nc -zv YOUR_SERVER 31002 || true
# Ardından SSH
ssh user@YOUR_SERVER

Ve tekrar altını çizeyim: bu bir maske. Asıl güvenlik, güçlü anahtarlar ve iyi SSH ayarlarından gelir. SSH tarafını derinlemesine düzenlemek isterseniz, şu yazıda hoş bir yolculuk var: VPS’te SSH güvenliği nasıl sağlamlaşır, FIDO2 anahtarları ve rotasyon.

IPv6 Kuralları: Yeni Mahallede Yol İşaretlerini Doğru Okumak

IPv6 bazen göz korkutuyor. En çok gördüğüm hata, ICMPv6’yı toptan kapatıp sonra ‘niye bazı şeyler çalışmıyor’ diye düşünmek oluyor. IPv6 dünyasında ICMPv6, adres çözümlemeden yola kadar birçok şeyi taşıyor. O yüzden bazı türleri özellikle izinli bırakmak gerekiyor. Üstteki iskelette bu izinler var ama bir paragrafa dökelim ki neyi niye yaptığımız net olsun.

Neighbor Discovery mesajları (neighbor-solicitation, neighbor-advertisement), router duyuruları (router-solicitation, router-advertisement) ve temel echo-request/echo-reply paketleri, ağın selamlaşma kuralları gibi. Bunları kısıtlarken tamamen kapatmayın. Sadece rate limit ekleyin, log’lamayı abartmayın. ICMPv6’yı boğarsanız, kendi kendinizi yormuş olursunuz.

Bir de ufak bir DNS ve AAAA kaydı notu: IPv6’yı açtığınız anda dış dünyadan erişilebilecek yeni bir kapınız var demektir. DNS’e ekleyeceğiniz küçük bir AAAA kaydı, bazen büyük bir aydınlanma yaratır. Hem performans hem de yönlendirme davranışları açısından ilginç sonuçlar görebilirsiniz. Ben ilk kez bunu kurcalarken şuna denk gelmiştim: küçük bir AAAA kaydı ve büyük bir aydınlanma.

Kalıcı Yapılandırma: Dosyayı Anlaşılır Tut, Yüklemeyi Tek Hamlede Yap

Gelelim kalıcılığa. Çoğu dağıtımda /etc/nftables.conf dosyasına yazdığınız kurallar, servis ayağa kalkınca otomatik yüklenir. Ben işimi bitirince kural setimi iki adıma bölerim: önce bir dosyada tertemiz yazarım, sonra tek hamlede yüklerim. Arada sentaks kontrolü de iyi gider.

# Yazdığınız dosyayı sınayın ve yükleyin
nft -c -f /etc/nftables.conf   # -c ile kontrol
nft -f /etc/nftables.conf      # her şey yolundaysa uygulayın
systemctl restart nftables     # servis üzerinden yenilemek isterseniz

Dosyanın içinde başta yaptığımız gibi ‘flush ruleset’ yazarak temiz bir başlangıç almak iyi alışkanlık. Ayrıca kural setini mantıksal bloklara ayırın: üstte set tanımları, sonra chain’ler, en sonda eklediğiniz özel istisnalar. Bir gün sonra dönüp baktığınızda kendinize teşekkür edersiniz.

Günlükleme (Log): Gürültüyü Süz, Sinyali Yakalayalım

İlk kurulumlarda log’u hevesle açıp bir süre sonra ‘bu kadarı da fazla’ dediğimiz çok olur. Dozu iyi ayarlayın. Paketlerin düşürüldüğü yerde küçük bir prefix ile log eklemek, hata ayıklarken ilaç gibi. Ama rate limit’i log’lara da uygulayın ki günlükler kendi kuyruğunu yemesin.

# Düşen paketleri sınırlı şekilde log'la
add rule inet filter input limit rate 5/second burst 10 packets log prefix 'nft-drop ' level info

Günlükleri bir yerde toplamak, özellikle birden fazla VPS’e sahipseniz, başka bir huzur veriyor. Ben bu noktada Grafana Loki + Promtail ikilisinden çok keyif aldım. Adım adım bir kurulum için şu rehber inanılmaz yardımcı: VPS log yönetimini rayına oturtmak: Loki, Promtail, alarm kuralları. Bir kez kurunca, nftables prefix’leriyle filtreleme yapmak çocuk oyuncağı oluyor.

Gerçek Hayattan Küçük Sahneler: Port Kapandı, Panik Yok

Bir keresinde SSH portunu sadece port knocking ile açılır yaptım, sonra knock dizisini unuttum. Evet, gülünç ama oluyor. Kurtaran şey, açık bıraktığım bir SSH oturumu ve hazırladığım bir geri dönüş komutuydu. Bir başka sefer, ICMPv6’yı fazla kısıp konteynerların birbirini görmesini zorlaştırdım; birkaç dakika ‘niye bu kadar yavaş’ diye düşündük durduk. Derken aklıma geldi, ICMPv6’ya çok cimri davranmışım. Sınırı genişletince her şey normale döndü.

Mesela şöyle düşünün: yeni bir uygulamayı canlıya alacaksınız, arada bir iki port açmanız gerekiyor, firewall’ı güncellediniz. O anda yaptığınız en iyi şey, sık kullanılan bağlantılara küçük bir sağlık kontrolü turu atmak. HTTP 200 dönüyor mu, SSH hala girilebilir mi, ping’ler makul hızda mı? Eğer bir de servislerinizi sakince yöneten bir dağıtım süreciniz varsa, işler çok daha huzurlu. Ben böyle anlarda şuna göz kırpıyorum: canlıya alırken panik yapmamak. Firewall ile uygulama dağıtımı yan yana yürüyünce keyifli oluyor.

Küçük İyileştirmeler: Servis Bazlı Aç-Kapa, Sağlıklı Varsayılanlar

İskeleti kurduk, knocking ile kapıyı gizledik, IPv6’daki işaretleri doğru okuduk. Bundan sonrası ince ayar. Servisleri tek tek açarken ihtiyacınız kadar açın. Mesela sadece 443 üzerinden servis veriyorsanız, 80’i açık tutmanız gerekmeyebilir; ya da 80’i 443’e yönlendiren bir reverse proxy kullanıyorsanız bir süre kontrollü bırakıp sonra kapatmayı deneyin. Aynı şekilde, UDP’ye mecbur kalmadıkça kapalı davranmak da hayat kurtarır. QUIC/HTTP3 kullanıyorsanız, 443/udp gerektiğini unutmayın.

Bir başka ince ayar da ülke veya ağ bazlı kısıtlama. Bunu nftables içinde set’lerle yapmak mümkün ama yönetmesi zorlaşabilir. Ben genelde uygulama katmanında veya upstream’de çözüyorum. Firewall, en sade haliyle ağ kapısındaki ilk süzgeciniz. Sade olan, sürdürülebilir olandır.

Kaynaklar ve Küçük Notlar

nftables konusunda resmi belgeler oldukça anlaşılır. Ben göz atarken en çok buralardan faydalanıyorum: Netfilter/nftables proje sayfası, daha pratik örnekler için nftables wiki ve günlük kullanım notları açısından ArchWiki nftables sayfası. Hepsini satır satır ezberlemeye gerek yok; kafanıza takılan yeri bir örnekle pekiştirmek çoğu zaman yeterli.

Bu arada, IPv6 ile DNS tarafı el ele yürür. Trafiğinizin nereden nasıl geldiğini anlamak için DNS ve yönlendirme stratejileriyle ufak denemeler yapmak ufuk açıyor. Eğer bu taraf ilginizi çekiyorsa, şuradaki derlemeyi seversiniz: gelişmiş DNS yönlendirme ve akıllı stratejiler.

Toparlama: Ufak Dokunuşlarla Büyük Sükunet

Bir VPS’i güvenceye almak göz korkutucu olmak zorunda değil. nftables ile tek bir dosyada hem IPv4 hem IPv6 için akıcı bir kurallar bütünü yazabiliyorsunuz. Rate limit’ler, port knocking ve doğru ICMPv6 izinleri bir araya gelince, hem saldırılara karşı daha çevik hem de meşru trafiğe karşı daha nazik bir sistem ortaya çıkıyor. Benim için en büyük rahatlık, kuralları net bloklara bölüp dosyayı kendi kendini anlatır hale getirmek oldu. Bir akşam yorgun döndüğümde bile neyin nerede olduğunu aramadan bulabiliyorum.

Pratik bir kapanış tavsiyesi: değişiklik yaparken her zaman açık bir SSH oturumu bırakın, mümkünse kısa bir geri dönüş komutunu arka planda çalıştırın. Log’ları aşırıya kaçmadan toplayın, gerektiğinde bakabileceğiniz bir düzende tutun. Merak ettiğinizde, ICMPv6 ve AAAA kayıtları gibi konuları ufak ufak kurcalayın; bazen tek bir satırın bütün resmi değiştirdiğine şahit olursunuz. Daha derinleşmek isterseniz, şu yazılar da hoş eşlik eder: merkezi loglama ve gözlemlenebilirlik ile beraber, güvenlik katmanını tamamlayan SSH güvenliğinin sıcak yolculuğu.

Umarım bu rehber, kafanızdaki düğümü çözmüş ve elinizi güçlendirmiştir. Bir dahaki buluşmamızda belki de küçük bir servis ağacının arka bahçesinde dolaşır, CDN ya da ters proxy önünde firewall stratejilerini konuşuruz. Şimdilik, ufak düzenlemeler yapın, nefes alın, gecenin sessizliğinde log’lara bir göz atın. Her şey yolundaysa, kendinize güzel bir çay ısmarlayın.

Sıkça Sorulan Sorular

Port knocking tek başına bir kale değil, akıllı bir perdedir. Kapının görünürlüğünü azaltır ve otomatik taramaları yorar. Asıl güvenliği güçlü SSH anahtarları, kapalı parola girişi ve iyi rate limit kuralları sağlar. Knocking'i bu önlemlerin yanında küçük bir artı olarak düşünmek en sağlıklısı.

IPv6 dünyasında ICMPv6 sadece ping değildir; adres keşfi, yol bilgisi ve hata iletileri gibi birçok temel işaret ICMPv6 ile taşınır. Tamamen kapatırsanız, bağlantılar garip şekilde yavaşlayabilir ya da kopabilir. Doğrusu, gerekli türleri izinli bırakıp makul bir rate limit eklemektir.

Kurallarınızı /etc/nftables.conf dosyasına yazıp servisle birlikte yüklemek pratik bir yoldur. Önce 'nft -c -f' ile kontrol edin, sonra 'nft -f' ile uygulayın ve nftables servisini etkinleştirin. Değişiklik yaparken açık bir SSH oturumu bırakın ve kısa süreli bir geri dönüş komutunu hazırda tutun.