Блокируем много IP адресов с помощью ipset и iptablesПодписка на Комментарии к "Блокируем  много IP адресов с помощью ipset и iptables"

Аватар zenon

Вопросы борьбы со спамерами, ботами и прочей нечистью всегда актуальны. Хорошо когда список небольшой, с помощью iptables это решается элементарно, но вот когда адресов пару тысяч ... тут уже приходится не сладко.

И тут на помощь к нам приходит (барабанная дробь) ipset.
Ipset позволяет использовать большие таблицы IP и MAC адресов, подсетей, номеров портов совместно с iptables (подключение производится через одно правило, в таблице используется хэширование). Возможно быстрое обновление списка целиком.

Официальная страница - ipset.netfilter.org.
ipset представляет из себя модуль ядра ip_set, ряд вспомогательных библиотек и утилиту ipset для задания параметров.
Установка тривиальна, во всех современных дистрибутивах присутствует пакет с одноименным названием.
Модуль ядра можно проверить командой:

# modprobe ipt_set

В Debian 7 и выше:
# apt-get install ipset

В Debian squeeze есть пакет xtables-addons, контент которого дублирует netfilter-extensions, установка:
# aptitude install xtables-addons-source xtables-addons-common
# m-a -v -t auto-install xtables-addons

В Gentoo соответственно проверить ядро на присутствие модуля, и установить пакет:
# emerge --ask --verbose ipset

CentOS / RHEL (используйте EPEL репозиторий):
# yum install ipset

Списки блокировок.
Где брать? Как отформотировать для использования?
Первое - список для удобства должен состоять только из ip адресов - по одному в строке, в любом текстовом файле.
Не так давно писал про Denyhosts, итогом работы является обновляемый список адресов в файле /etc/hosts.deny, выглядит вот так:

...
ALL: 222.186.15.139
ALL: 98.210.197.168
...

Тут надо просто выбросить первые 5 символов, вот так например:
# cat /etc/hosts.deny | sed 's/ALL: //' >> denyhosts_blacklists.list

Если необходимо отсортировать уникальные (удалить дубликаты):
# sort denyhosts_blacklists.list  | uniq > sorted_blacklists.list

Хорошие, обновляемые, готовые к использованию списки можно взять на форуме stopforumspam.com, будьте внимательны - есть суточное ограничение на количество скачиваний.
/..../
Блокируем.
В ipset нет таблиц, а есть set различных типов. Типы позволяют задавать ip адреса из определенной подсети (ipmap тип), связки ip адресов с MAC адресами (macipmap), порты из заданного диапазона (portmap), набор ip адресов или сетей (iphash, nethash), разные комбинации этих set-ов, или даже хранить ip адреса в set только определенное время (iptree). Более подробно советую посмотреть man ipset(8).

Для нашей задачи подходит тип iphash. Создаем (N -new) set с именем blacklist, и смотрим его содержимое:

# ipset -N blacklist iphash
# ipset -L blacklist
Name: blacklist
Type: hash:ip
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16480
References: 0
Members:

Добавляем (A - add) ip адреса blacklist и смотрим (L - list) содержимое set:

# ipset -A blacklist 192.168.0.211
# ipset -A blacklist 10.10.0.23
# ipset -L blacklist
Name: blacklist
Type: hash:ip
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16512
References: 0
Members:
10.10.0.23
192.168.0.222

Удаляем ip адреса из blacklist set (D - delete):

# ipset -D blacklist 192.168.0.222
# ipset -L blacklist
Name: blacklist
Type: hash:ip
Revision: 0
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 16512
References: 0
Members:
10.10.0.23

Проверяем, есть ли ip в blacklist set (T - test):

# ipset -T blacklist 10.10.0.23
10.10.0.23 is in set blacklist.

Удаляем все ip адреса из blacklist set (F - flush):

# ipset -F blacklist

Удаляем сам set (X):

# ipset -X blacklist

*** После создания set нам необходимо пропустить его через какую-либо цепочку фильтра iptables.
Вспомним путь прИхождения внешнего пакета в правилах iptables, сверху вниз:
-t raw PREROUTING
-t mangle PREROUTING
-t nat PREROUTING
-t mangle INPUT
-t filter INPUT

Или смотрим картинку.
Цепочка INPUT обрабатывает запросы, которые идут непосредственно на машину с iptables. Если надо блокировать трафик, который проходит от клиента к серверу через данную машину транзитно, то нужно использовать FORWARD, а то и PREROUTING.
Пример с INPUT:
** Правило не добавляем (-A), а вставляем (-I) в начало цепочки INPUT правил.
# iptables -v -I INPUT -m set --match-set blacklist src -j DROP
DROP  all opt -- in * out *  0.0.0.0/0  -> 0.0.0.0/0   match-set blacklist src

Т.е. мы подключили модуль set (-m set), потом указали какое совпадение set использовать (--match-set blacklist). src - это флаг, который показывает какие ip сравнивать с set, src (source - источник) или dst (destination - назначение) . Если нужно проверить и src и dst, то флаг задается так src,dst.
Посмотрим цепочку INPUT, на предмет нашего правила ipset (часть вывода):
# iptables -L INPUT -n -v --line-numbers
Chain INPUT (policy DROP 189 packets, 12489 bytes)
num   pkts bytes target     prot opt in     out     source               destination        
1        0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set blacklist src
2     108K 2115M ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0          
3    18702 1834K ACCEPT     all  --  eth1   *       0.0.0.0/0            0.0.0.0/0          
4     3266  331K ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
5       23   920 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate INVALID
6        0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:!0x17/0x02 ctstate
...
...

Хорошо видно, что наше правило (1) встало на первое место, т.е. пакеты при прохождении не минуют наш blacklist set.

Пример с FORWARD и логированием:

# iptables -v -I FORWARD -m set --match-set blacklist src -j DROP
DROP  all opt -- in * out *  0.0.0.0/0  -> 0.0.0.0/0   match-set blacklist src
# iptables -v -I FORWARD -m set --match-set blacklist src -j LOG --log-prefix "DROP blacklist entry"
DROP  all opt -- in * out *  0.0.0.0/0  -> 0.0.0.0/0   match-set blacklist src

** Замечание - почему логирование стоит вторым в инсерте правил? .. потому что мы вставляем правило в начало цепочки, и, казуса, что пакет не дошёл до правила с логированием не случится, в случае если правила мы добавляем, то очередность должна быть обратной, всегда смотрите и проверяйте правила прохождения пакетов:
# iptables -L FORWARD -n -v --line-numbers
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination        
1        0     0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set blacklist src LOG flags 0 level 4 prefix "DROP blacklist entry"
2        0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set blacklist src
3      983 55096 TCPMSS     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:0x06/0x02 TCPMSS clamp to PMTU
4    19889 2051K ACCEPT     all  --  eth1   ppp0    0.0.0.0/0            0.0.0.0/0          
5    19375   11M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW,RELATED,ESTABLISHED
6        0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate INVALID

... - все в порядке: сначала в LOG (1), потом в DROP (2), потом остальные правила.
/../
Удалять правила можно вот так:
# iptables -D INPUT -m set --match-set blacklist src -j DROP
# iptables -D FORWARD -m set --match-set blacklist src -j DROP
# iptables -D FORWARD -m set --match-set blacklist src -j LOG --log-prefix "DROP blacklist entry"

** Логи ipset и iptables искать в логах ядра, потому что это "ядерные" модули.
Я использую syslog-ng, kernel логи ловлю в отдельном файле:
Посмотреть через некоторое время, кто попал в сети можно так:

# grep "DROP blacklist entry" /var/log/syslog-ng/kernel.log

Если не знаете где логи можно грубо (может парсить долго!):
# grep -r "DROP blacklist entry" /var/log/*

Все что осталось, - это написать скрипт, который забирает из файла список блокировок.
Каталог для нашего скрипта (можно использовать любой):

# mkdir /etc/blacklist

Скачиваем список ip адресов и распаковываем его (можно автоматизировать, добавив задание в cron):
# cd /etc/blacklist
# wget http://www.stopforumspam.com/downloads/listed_ip_1.zip
# unzip listed_ip_1.zip

Примерный вариант скрипта:
#!/bin/bash
echo -n "Applying blacklist to IPSET..."
ipset -N blacklist iphash
xfile=$(cat /etc/blacklist/listed_ip_1.txt)
for ipaddr in $xfile
do
  ipset -A blacklist $ipaddr
done
echo "...Done"
echo -n "Applying blacklist to Netfilter..."
iptables -v -I INPUT -m set --match-set blacklist src -j DROP
iptables -v -I INPUT -m set --match-set blacklist src -j LOG --log-prefix "DROP blacklist entry"
echo "...Done"

////**** Вариант скрипта предварительный, хотя много нового в нем не будет, но подправлю позже чуть.
...
upd: Замечание по правилу iptables - если firewall настроен как положено, (политики DROP), и открыты только нужные порты, следует указать более конкретное правило, для конкретного порта:
iptables -I INPUT  -m set --match-set blacklist src -p TCP --destination-port 80 -j REJECT
iptables -I INPUT  -m set --match-set blacklist src -p TCP --destination-port 80 -j LOG --log-prefix "DROP blacklist entry INPUT"

... вот что теперь я наблюдаю у себя в логах:
...
/var/log/syslog-ng/kern.log:Sep 28 22:44:02 HGATE kernel: [71402.569028] DROP blacklist entry INPUTIN=ppp0 OUT= MAC= SRC=218.6.12.99 DST=88.87.92.60 LEN=48 TOS=0x08 PREC=0x60 TTL=114 ID=19401 DF PROTO=TCP SPT=61453 DPT=80 WINDOW=8192 RES=0x00 SYN URGP=0
/var/log/syslog-ng/kern.log:Sep 29 01:14:18 HGATE kernel: [80411.694498] DROP blacklist entry INPUTIN=ppp0 OUT= MAC= SRC=79.134.234.200 DST=88.87.92.60 LEN=60 TOS=0x08 PREC=0x60 TTL=57 ID=12861 DF PROTO=TCP SPT=54571 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0
/var/log/syslog-ng/kern.log:Sep 29 01:37:20 HGATE kernel: [81792.214677] DROP blacklist entry INPUTIN=ppp0 OUT= MAC= SRC=176.8.88.134 DST=88.87.92.60 LEN=48 TOS=0x08 PREC=0x60 TTL=123 ID=11004 DF PROTO=TCP SPT=58019 DPT=80 WINDOW=8192 RES=0x00 SYN URGP=0
...

Можно посмотреть сколько заблокированных (посчитаем количество линий в файле) IP адресов:
# wc -l /etc/blacklist/listed_ip_7.txt
68611 /etc/blacklist/listed_ip_7.txt

Посмотреть на наш ipset и его размер:
# ipset --list -terse
Name: blacklist
Type: hash:ip
Revision: 0
Header: family inet hashsize 4096 maxelem 65536
Size in memory: 142800
References: 0

////
Справочное руководство man ipset (англ).
Статья на англ. с графиками тестирования скорости обработки большого количества ip адресов.
Спасибо за внимание.
Похожие материалы:
Аватар zenon

Интересности:
To block IP addresses based on geo location (country) here is a simple shellscript:

#!/bin/sh
ipset -N geoblock nethash
for IP in $(wget -O – http://www.ipdeny.com/ipblocks/data/countries/{cn,kr,pk,tw,sg,hk,pe}.zone)
do
ipset -A geoblock $IP
done
iptables -A INPUT -m set –set geoblock src -j DROP

To auto-timeout a rule (and not generate any message if it already exists):
ipset create test hash:ip timeout 10
ipset add --exists test 91.83.231.25 120 (overwriting the default 10 seconds value)

To auto-learn a MAC address: (and define a range)
ipset create test bitmap:ip,mac range 192.168.0.0/24
ipset add test 192.168.0.1,11:11:22:22:11:11
ipset add test 192.168.0.2 (this one will auto-learn)
Аватар XliN

Привет. У меня на шлюзе старенькая CentOS 5.9. В виду своей старости (стабильности) возник вопрос. Нет у меня ключа ipset --list -terse
Как бы узнать какой он был раньше?
ipset v4.5, protocol version 4.
Kernel module protocol version 4.

Аватар zenon

Ну если стоит, то не проще man ipset?
Хотя вот например man, датирован примерно 2010 годом, и видимо аналога terse нет.
Да, выхлоп кучи адресов вместе с суммарной информацией не удобен.

Аватар anonym

Можно пойти конем и обрезать все кроме первых строк, в случае с 1 сетом достаточно первых 5 строк:

# ipset --list | head -n 5

Аватар zenon

Спамлисты с форума stopforumspam.com реально спасают.
Выключил обновление спамлиста на пару дней, так сразу полез спам.
ASCII капчу научились обходить на раз-два, так что думаю её можно вообще выключить.
Попробовал вообще выключить блокировку ip адресов, результат - за полчаса около сотни спам-постов.

Аватар zenon

В продолжении предыдущего поста, IP адрес до жути активный попался, поставил на посмотреть логирование:

# grep -a "91.200.13.110" /var/log/syslog-ng/kern.log | wc -l
15707

Это за три дня.
Обновление спамлистов у меня происходит каждый в начале первого часа ночи, для загрузки ipset`а требуется время:
# cat test.sh
#!/bin/sh
ipset --flush blacklist-TMP
for ipaddr in $(cat /etc/stopforumspam/blacklist.ip)
  do ipset --add blacklist-TMP $ipaddr
done
# time ./test.sh
./test.sh  1,93s user 14,98s system 25% cpu 1:05,06 total

И за это время на сайт успевает просочиться 4-5 спам комментариев.
Как этого избежать - использовать временный ipset, а потом их менять местами (swap), подсмотреть можно тут.
# Create the new set and add the entries to it
ipset -N new-set ....
ipset -A new-set ....
...
# Swap the old and new sets
ipset -W old-set new-set
# Get rid of the old set, which is now under new-set
ipset -X new-set
Аватар Stas

Здравствуйте. Сделал по инструкции, все работает, но столкнулся вот с чем "ipset v6.11: Hash is full, cannot add more elements" я так понял что он больше прожевать не может, в текстовике есть ххх.ххх.ххх.ххх/8 и ххх.ххх.ххх.ххх/16 и хватает буквально пары штук чтоб вылезла ошибка. Получается все равно есть предел количеству IP в текстовике или что-то нужно подправить. ххх.ххх.ххх.ххх/8 - так закрывался китай, их по одному запарился дропать, закрыл полностью. Как быть? Есть выход из этой ситуации? "ipset v6.11: Hash is full, cannot add more elements"

Аватар zenon

Размер хэша увеличить надо, например:

ipset create blocktest hash:ip hashsize 16777216 maxelem 16777216
Аватар Stas

Разобрался. Дело было в set, надо было выбирать nethash, а я выбрал iphash.

Аватар HorekRediskovich

а iptree нужно применять только когда созданём set-а? Как то так:

ipset -N blacklist iptree --timeout 600

и совсем не понятны данные строчки:

ipset create test hash:ip timeout 10
ipset add --exists test 91.83.231.25 120 (overwriting the default 10 seconds value)

в часности hash:ip описание данного параметра мне не удалось найти, а так же --exists тоже не понятно что выполняет.
Аватар HorekRediskovich

C hash:ip кажись разобрался это получается по старому iphash, я прав? в вот --exists пока не совсем понял

Добавить комментарий