Ubuntu Türkiye

Yazılım => [Nasıl] Anlatımları => Konuyu başlatan: plymouth - 24 Ağustos 2018 - 20:12:51

Başlık: [Nasıl] Dinamik IPTables Karaliste kuralları
Gönderen: plymouth - 24 Ağustos 2018 - 20:12:51
Merhaba

Uzun süredir PF'in (OpenBSD) dinamik listeleri ile firewall kurallarını hazırlayıp sistem scirptleri ile düzenli olarak günlük, saatlik periyotlarla güncellenen
ve davetsiz misafirleri uzak tutan bir yapı kullanıyorum. En basit anlatımla, PF'e dinamik olarak güncellenen bir dosyanın yerini gösterip,
ona bu dosyadaki ip adreslerinden gelen istekleri "DROP" etmesini söylüyorsunuz. Yazdığınız bir script ile
güvenilir kaynaklardan aldığınız karaliste dosyalarını sisteme indirip PF'in bellekte tuttuğu listeyi ve diskteki karşılığını güncellemeniz yeterli.

Bu basit yapıya Linux üzerindede ihtiyacım oldu ve alttaki basit scripti yazdım. Bunu kullanmak zorunda değilsiniz ancak fikir verici bir örnek olarak
burada paylaşıyorum.

Öncelikle alttaki script ne yapar size onu açıklayayım.
Güvenilir kaynaklardan başlayalım. İnternetten erişime açık tüm sistemlere sürekli olarak port tarama, şifre ve kullanıcı adı tahmini yapmaya çalışan
saldırılar gelir. Artık bu durum güneşin her sabah doğması kadar normal görünmeye başladı. Özellikle önüne erişim denetimi ve protokol analizi yapabilen
herhangi bir koruyucu koyamadığımız ve ilginç bir şekilde tüm dünyaya açık kalması gereken sunucular için kullanılabilir ve ekonomik görünen çözüm
dinamik kurallar kullanarak kötü niyetlileri diğerlerinden ayırmaya çalışmaktır. Kimin iyi kimin kötü olduğuna nasıl karar veriyoruz peki ? Genellikle bir iki
yöntem kullanılıyor. Mesela anlık bağlantı sayısı "X" den fazla olanları "Y" dakika erişimden düşür. Saniyede "K"dan fazla paket gönderen IP adreslerini "Z" karalisteye al.
Ya da, bizim burada kullanacağımız metod gibi özellikle belli amaçlarla internet ortamından zararlı sistemleri ayıklamaya çalışan kurumlar var.
Topladıkları bilgileri internette paylaşıma sunarak bu zararlı sistemlerin size uğraması halinde kendinizi korumanız için size kullanışlı bilgiler veriyorlar. Mesela kim bunu yapıyor ? Kısa bir liste:


Alttaki script örnek olarak yukardaki listeden sadece birinin yayınladığı listeleri sisteme indirip, iptables/ipset ile kullanılabilecek şekilde ayrıştırır.
İptables ve ipset ile henüz tanışmayanlar için kısaca iptables; Linux kerneli içinde bulunan ve sisteme giren-çıkan paketleri kontrol ederek, onlarla
ilgili ne yapılacağına karar veren netfilter isimli bölümün komut satırı üzerinde çalışan yönetim arayüzüdür. İptables ile belirli kriterleri sıralayarak
bir paketin sisteme girip giremeyeceğine ya da sistemden çıkıp çıkamayacağına karar verecek kuralları yazarız. Bu kurallara göre netfilter paketleri
ne yapacağına karar verir. Çok yetenekli bir firewall yazılımıdır ve burada bahsettiğim işlevlerinin dışında daha başka marifetleride var. Ne var ki,
bu kısmı benim size anlatmayı hedeflediğim bölümlerin dışında kalıyor o yüzden sizi "ipset" ile tanıştırayım. İpset, temel olarak basit bir liste yönetim
yazılımıdır ve ip adreslerini, subnetleri saklamak için hazırlanmış bir komut satırı uygulamasıdır. Görece oldukça yeni bir özellik. Özellikede PF'in bu özelliğe
uzun zaman önce sahip olduğunu düşünecek olursak bi' hayli yeni sayılır. Doğum tarihi olarak 2.6.3? kernel işaret edilmiş. İpset'in bizim için özel olan kısmı
tahmin edeceğiniz gibi iptables kurallarını yeniden yüklemeye gerek olmadan (reload/restart) hızlıca IP listeleri üzerinde değişklik yapmamıza imkan tanıyor.

İndirdiğmiz IP listelerini ayrıştırmaktan bahsediyordum en son. Ayrıştırmamızın sebebi ipset IP adresleri ile subnet'leri karıştırarak kullanmaya pek sıcak bakmıyor.
Özelliklede uzun listelerde arama yapmak ve karışık kullanılan listelerin kapladığı alanın büyüklü biraz problemli olabiliyor. Bu nedenle ayrıştırma kriteri
IP adresleri ve subnetler şeklindedir. Bu yüzden IP adresleri; "YIL-AY-GÜN-Saat.Dakika.Saniye-badhosts" isimli ipset içinde, subnetler; "YIL-AY-GÜN-Saat.Dakika.Saniye-subnets"
isimli ipset içinde tutulur. Son olarak bu iki ipset tek bir ipset altında toplanır. Özel bir IP set altında toplanır demek daha doğru olur. "list:set" tipinde özel bir ipset.
List:set tipindeki IP setler bu scriptin önemle vurguladığı bir özellik. Zira bu tip bir ipset başka bir ipset'i kendi içinde bir liste elemanı olarak saklayabilir. Netfilter içinde "list:set"
tipinde bir ipset bulunan bir iptables kuralı ile karşılaştığında bu listenin sıralamasına sağdık kalarak elindeki IP adresini bu ipsetler içinde arar.
Bu ipset olmazsa her ipset için bir iptables kuralı yazmanız gerekirdi. Yani yukardaki
listeden elde ettiğimiz tüm karalisteleri bir ipset olarak sakladığımızı düşünün. Toplam dört tane ipset'imiz olur. Bu ipsetlerin her biri için bir iptables kuralı olması gerekir.
Neyseki buna gerek yok. Altta örnek olması için paylaştığım iptables kural satırları arasında "DropList" olarak ismi geçen aslında "list:set" tipinde bir
ipset. İsminin ne olacağı size kalmış. Ama bu scripti çalıştırmadan önce en azından bir tane "list:set" tipinde ipset tanımı yapmış olmanız gerek.

Son olarak kullanıma hazır hale getirdiğimiz ipset'leri iptables ile ilişkilendirmek için hemen alttaki gibi bir kural yazarak iptables'a ipset listelerimizi işaret ediyoruz.
"<>" arasına yukarda söylediğim gibi "list:set" tipinde bir ipset ismi yazmalısınız. En azından paylaştığım script bunu gerektiriyor.

iptables -A INPUT -m set --match-set <DropList> src -j DROP

Sisteminizdeki tüm değişiklerden sonra mutlaka değişikliğin beklenmeyen olası etkileri için log dosyalarını kullanmak basit ama etkili bir önleme tekniğidir.
Bu nedenle, yukardaki tek satırlık kural yerine aslında alttaki daha uygun olur. Çünkü çöpe attığınız her paket için sisteminizdeki genel amaçlı log dosyasına
ilginizi çekebilecek bir log satırı ekler.

iptables -A INPUT -m set --match-set <DropList> src -j LOG --log-prefix "IPSET-Dropped: "
iptables -A INPUT -m set --match-set <DropList> src -j DROP


Kısa, öz ve açıklayıcı olması için hazırladığım bu "NASIL" makalesinin işinize yaramasını umuyorum. Ben scriptimi Rhel 6 ve 7 (x86_64) üzerinde denedim. Elimde kurulu bir Ubuntu
sistemim olmadığı için maalesef Ubuntu ile test edemedim. Belki ilerleyen zaman diliminde Ubuntu'yu da eklemem mümkün olabilir. Scriptim BSD lisansına tabidir.

Unutmayın, man sayfaları en büyük dostunuzdur.

#!/bin/bash
#
#.*Copyright (c) 2018, plymouth
#.*All rights reserved.
#.*
#.*Redistribution and use in source and binary forms, with or without
#.*modification, are permitted provided that the following conditions are met:
#.*1. Redistributions of source code must retain the above copyright
#.*   notice, this list of conditions and the following disclaimer.
#.*2. Redistributions in binary form must reproduce the above copyright
#.*   notice, this list of conditions and the following disclaimer in the
#.*   documentation and/or other materials provided with the distribution.
#.*3. All advertising materials mentioning features or use of this software
#.*   must display the following acknowledgement:
#.*   This product includes software developed by the <organization>.
#.*4. Neither the name of the <organization> nor the
#.*   names of its contributors may be used to endorse or promote products
#.*   derived from this software without specific prior written permission.
#.*
#.*THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
#.*EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#.*WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#.*DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
#.*DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#.*(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#.*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#.*ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#.*(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#.*SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
####
# This script provides valid black list entries for iptables/ipset
# Make sure there are a tool chain which full of usable tools
# plymouth - v 0.1:4

for tool in "wget" "curl" "tr" "head" "perl" "egrep" "grep" "wc" "date" "cut" "logger"
do
        if [ ! -x "/usr/bin/${tool}" ] && [ ! -x "/bin/${tool}" ]; then

                echo "There is no $tool, so you shall not pass"
                exit 10;

        fi

done

if [ $# -ne 1 ]; then
regCond="^Name:\ .*"
ipSetListName=$( ipset list | egrep -A 1 "Name:" | grep -B 1 "list:set" | head -n 1 )
     if [[ ! $ipSetListName =~ $regCond ]]; then

  echo "Sorry, there must be at least one ipset configured as list:set type "
echo "Or, you can pass existing ipset name as an argument while invoking this script"
echo ""
echo "Usage: iptables-ipset-update.sh <ipsetname>"
exit 13

     fi

  ipSetListName=${ipSetListName#Name: }


else

  par="$1"
  res=$( ipset list | egrep -cE " ${par}$" )
  if [ $res -eq 1 ]; then

    ipSetListName="$par"

  else

    echo "$par is not exist or not an ipset"
   exit 16

  fi

fi



_date=$( date +%F-%H.%M.%S )
tmpdir=$( head -c 120 /dev/urandom | tr -cd "0-9A-Za-z" | head -c 24 )
mkdir "/tmp/${tmpdir}"
if [ $? -ne 0 ]; then

  echo "Looks like you do not have permission to write under /tmp"
exip 11

fi

tmpDir="/tmp/${tmpdir}"

links[0]=https://www.binarydefense.com/banlist.txt
links[1]=https://rules.emergingthreats.net/blockrules/compromised-ips.txt
links[2]=https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt
links[3]=https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level3.netset
links[4]=https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level2.netset
links[5]=https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset

#####

fetchLists(){


        for url in ${!links[@]}
        do
                fileName=$( echo ${links[$url]} | awk -F "/" ' NF>0 { print $NF }')
                /usr/bin/wget -c "${links[$url]}" -O "${tmpDir}/${fileName}"
                if [ $? -ne 0 ]; then
                        /usr/bin/curl "${links[$url]}" -O "${tmpDir}/${fileName}"
                fi
        done

        perl -lne 'print if ! /^\s*(#.*)?$/' ${tmpDir}/*.txt ${tmpDir}/*.netset | sort -uV > "${tmpDir}/pf-badhost.txt"


}

splitIntoCategories(){

  local count subnet
        echo "splitting into subnet categories..."
egrep -v "/" ${tmpDir}/pf-badhost.txt > ${tmpDir}/ipset-badhosts
egrep "/" ${tmpDir}/pf-badhost.txt | cut -d "/" -f 2 | sort -n | uniq -c \
| while read -r count subnet
   do

   if [ $subnet -gt 8 ]; then

    egrep "\/${subnet}$" "${tmpDir}/pf-badhost.txt" > "${tmpDir}/ipset-subnet-${subnet}"
   echo "subnet $subnet file created "
   echo "The $count number of networks will be blocked in /${subnet}"

   fi

   done

}

loadIPSets(){

  local res
echo "generating ip sets.... "
ipset create "${_date}-badhosts" hash:ip hashsize 16384 maxelem $( wc -l "${tmpDir}/ipset-badhosts" \
| cut -d " " -f 1 ) > /dev/null 2>&1
res=$?
if [ $res -ne 0 ] && [ $res -ne 1 ]; then
    echo "ipset-${_date}-badhosts could not create"
    exit 15
fi

  while read -r line
do

   ipset -! add "${_date}-badhosts" $line

  done < "${tmpDir}/ipset-badhosts"

  echo "... the next one"
maximum=$( wc -l ${tmpDir}/ipset-subnet-* | grep total | cut -d " " -f 2 )

   ipset create "${_date}-subnets" hash:net family inet maxelem $maximum > /dev/null 2>&1
  res=$?
  if [ $res -ne 0 ] && [ $res -ne 1 ]; then

    echo "Ooops, I could not create ipset for subnets"
   exit 12

   fi

   while read -r line
  do

    ipset -! add "${_date}-subnets" $line

   done < <(cat ${tmpDir}/ipset-subnet* )

  echo "swapping old ipsets with newest ..."
read -a listOfSets <<< $( ipset list "$ipSetListName" | grep -A 100 "Members:" | grep -v "Members:" )
if [ ${#listOfSets[@]} -eq 0 ]; then

   echo "No old ip sets found"
  ipset add $ipSetListName "${_date}-badhosts"
  ipset add $ipSetListName "${_date}-subnets"
  logger -p local0.info "All Ip sets are updated, check log files for related dropped connection attempts"

  else

  for setName in ${!listOfSets[@]}
do
  ipset del $ipSetListName ${listOfSets[$setName]}
  ipset destroy ${listOfSets[$setName]}
done
 
  ipset add $ipSetListName "${_date}-badhosts"
  ipset add $ipSetListName "${_date}-subnets"
  logger -p local0.info "All Ip sets are updated, check log files for related dropped connection attempts"

  fi

}

fetchLists
splitIntoCategories
loadIPSets

# Cleaning out
/bin/rm -rf "/tmp/${tmpdir}"