Merhabalar,
Geçen ay e-bergi'de yayınlanan güvenlik duvarı yazısına bu ay devam ediyoruz. Geçen ayki yazıda iptables zincirlerinden bahsetmiştik ve güvenlik duvarı kurallarımızı INPUT, OUTPUT ve FORWARD zincirlerine eklemiştik. Bu zincirler netfilter/iptables sistemi tarafından tanımlanan zincirlerdi. Biz de kendi zincirlerimizi oluşturabiliriz. Böylelikle kurallarımızı mantıklı bölümlere ayırabiliriz. Ya da kural kümemizde farklı yerlerde tekrar edeceğimiz kuralları bir zincirde tutup, gerekli yerlerden bu zinciri çağırabiliriz.
Iptables Zincirleri
Yeni bir zincir oluşturmak için iptables komutunun -N ve -X parametrelerini kullanıyoruz. Komut satırından aşağıdaki gibi zincir oluşturup silebiliriz.
iptables -N zincir_adi
iptables -X zincir_adi
Iptables-restore komutuna vereceğimiz kural kümesi dosyası içinde yeni zincir oluşturmak için sadece aşağıdaki kullanım yeterli.
-N ornek_zincir
Bu yeni oluşturduğumuz zincire kural eklemek için INPUT ya da FORWARD yerine zincir adını kullanıyoruz.
-A ornek_zincir -p tcp --dport 80 -j ACCEPT
Bu oluşturduğumuz zinciri kural kümemizden çağırmadığımız sürece, zincir içindeki kuralların hiç bir etkisi olmayacak. Hatırlarsanız, iptables paketin geldiği ya da gittiği yöne göre, filter tablosundaki INPUT, OUTPUT ve FORWARD zincirlerindeki kuralları kontrol etmeye başlıyordu. Yeni oluşturduğumuz zinciri de, bu varsayılan zincirlerin içinden çağırmamız gerekir. Bunu da aşağıda görebileceğiniz gibi, -j ile yapıyoruz.
-A FORWARD -i eth0 -j ornek_zincir
Normal bir kural girer gibi, istediğimiz paketleri yakalayacak kuralları, varsayılan zincirlerden birine(burada FORWARD) giriyoruz. -j ile paketi ACCEPT,REJECT,DROP ile kabul etmek ya da reddetmek yerine, zincirin adını yazarak daha önce oluşturduğumuz zincirde gönderiyoruz. Paket, bu zincirdeki kurallarca işlenmeye devam edecek. Eğer işlenmekte olan paketle eşleşen bir kural bulunursa, onun "-j" ile belirttiği işlem yapılacak. Bu işlem kabul etmek veya reddetmek olabileceği gibi, paketi başka bir zincire göndermek de olabilir.
Zincirde yapacağımız son işlem olarak, zincir içindeki hiç bir kural tarafından yakalanmayan paketleri bizi çağıran zincire geri göndermeliyiz. Böylelikle hala hakkında karar verilmemiş paketler kural kümesi tarafından işlenmeye devam eder. Bunu da "-j RETURN" ile yapıyoruz. Ornek zincirimizi baştan sona yazarsak.
-N ornek_zincir
-A ornek_zincir -p tcp --dport 80 -j ACCEPT
-N ornek_zincir -j RETURN
Elimizdeki Kural Kümesinin Zincirlerle Sadeleştirilmesi
Şimdi geçen ayki yazının en sonundaki örneğe tekrar bakalım. INPUT zincirinin ve FORWARD zincirinin sonundaki yer alan kurallarla, izin vermediğimiz tüm paketleri reddediyorduk. TCP ve UDP paketleri için "ulaşılamadı" anlamına gelen cevaplar gönderirken, tüm diğer paketleri çöpe atıyorduk. Bu kuralları aşağıda görebilirsiniz.
...
# Güvenlik duvarı makinasına gelen diğer bütün bağlantı isteklerini reddet
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p ALL -j DROP
...
# Gelen ve giden tüm diğer bağlantıları reddet
-A FORWARD -p tcp -j REJECT --reject-with tcp-reset
-A FORWARD -p udp -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -p ALL -j DROP
Burada iki farklı zincirde, tamamen aynı üç kural var. Bu kuralları tek bir zincirde toplayıp, INPUT ve FORWARD zincirlerinin sonunda "-j" ile çağırabiliriz. Böylelikle reddetme kurallarımızda bir değişiklik yapmak istediğimizde, bunu tek bir yerde yapmamız yeterli olacak. Büyük kural kümeleriyle uğraşıldığında kuralları bu şekilde kopyalayıp kullanmak, ileride sorunlara yol açabilir. Bir kopyada yapılan değişikliği diğer kopyalarda yapmayı unutursanız, kural kümenizin neden sizin istediğiniz gibi çalışmadığını anlamanız zaman alabilir. Şimdi bu kuralları tek bir zincirde topladığımızda önceki yazıda son örnek olarak verdiğimiz kural kümesi neye benzeyecek buna bakalım.
*filter
:INPUT ACCEPT [:]
:FORWARD ACCEPT [:]
:OUTPUT ACCEPT [:]
# Paketleri protokolüne uygun olarak reddeden zincir.
-N paket_reddet
-A paket_reddet -p tcp -j REJECT --reject-with tcp-reset
-A paket_reddet -p udp -j REJECT --reject-with icmp-port-unreachable
-A paket_reddet -p ALL -j DROP
# zincirimizin sonu
-A paket_reddet -j RETURN
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m state --state INVALID -j DROP
# Bu bilgisayar güvenlik duvarını yönetmek için SSH bağlantısı yapabilir
-A INPUT -p tcp --dport 22 -s 10.0.0.45 -j ACCEPT
# Güvenlik duvarı makinasına gelen diğer bütün bağlantı isteklerini reddet
-A INPUT -j paket_reddet
#####
# İç ağ trafiğini filtrele
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
-A FORWARD -m state --state INVALID -j DROP
# 10.0.0.88 IP adresinde çalıştırdığımız DNS sunucusuna gelen paketlere izin ver
-A FORWARD -i eth0 -p udp --dport 53 -d 10.0.0.88 -j ACCEPT
# Dışarı giden trafiği incele Giden HTTP ve HTTPs trafiğine izin ver
# sadece 5.1.1.1 IP adresine çıkılmasına izin verme
-A FORWARD -i eth1 -p tcp --dport 80 -d ! 5.1.1.1 -j ACCEPT
-A FORWARD -i eth1 -p tcp --dport 443 -d ! 5.1.1.1 -j ACCEPT
# Gelen ve giden tüm diğer bağlantıları reddet
-A FORWARD -j paket_reddet COMMIT
Gördüğünüz gibi tekrar edilen kuralları bir zincirde toplamak kural kümemizi sadeleştirdi ve ileride yapacağımız değişikliklerde hata yapma olasılığımızı azalttı. Bu örnek kural kümesi çok uzun olmadığı için zincir kullanmanın getirileri çok büyükmüş gibi görülmese de, zincirler uzun bir kural kümesinde oldukça faydalı olacaktır. Zincir kullanmanın bir başka getirisi de, güvenlik duvarının hızını arttırabilmesi. Bir ağ paketi işlenirken, üzerinden geçtiği her kural paketi biraz daha geciktirir. Çok uzun kural kümelerinde gelen her paketin teker teker tüm kurallarca işlenmesi hissedilebilir gecikmelere yol açabilir. Zincirler kullanarak kural kümesini daha küçük parçalara bölebiliriz. Örneğin gelen TCP paketleri için ayrı bir zincir, gelen UDP paketleri için ayrı bir zincir, sunucuların bulunduğu IP aralığı için ayrı bir zincir tanımlayabiliriz. Daha sonra INPUT, OUTPUT ve FORWARD zincirlerinde paketleri ilgili zincirlere gönderirsek; her paket, kümemizdeki her kural tarafından işlenmemiş olur. Sadece paket hakkında karar verebilecek kuralların bulunduğu zincirin üzerinden geçilir, bu da kuralların işlenmesi sırasında oluşan gecikmeyi azaltmaya yardımcı olur.