Как заблокировать 100 000+ индивидуальных IP-адресов

Введение

Как заблокировать большое количество IP address от вашего веб-приложения / сервера. Очевидно, что это можно легко сделать на PHP или на любом языке программирования

 $ipList = []; // array list or from database if (in_array(getIP(), $ipList)) { // Log IP & Access information header("https://www.google.com.ng/search?q=fool"); // redirect exit(); // exit } 

Или Использование htaccess

 order allow,deny deny from 123.45.6.7 deny from 012.34.5. # .... the list continues allow from all 

Проблемы

  • Я пытаюсь заблокировать целых 100k plus individual IPs не subnets
  • Я пытаюсь избежать доступа пользователя к PHP перед блокировкой такого IP-адреса
  • 100000+ – более 1,5 МБ, и это очень много, если информация загружается в htaccess все время
  • База данных ИС все еще растет … и им было бы удобно динамически добавлять больше значений
  • Чтобы установить запреты в iptables для 100000+, просто смешно (может быть, неправильно)

Глупые идеи

 order allow,deny deny from database <-------- Not sure if this is possible allow from all 

Вопрос

  • Возможно ли, что htaccess получит список из базы данных (Redis, Crunchbase, Mongo, MySQL или даже Sqlite) … любой
  • Существует ли видимое решение для управления такой проблемой в производстве
  • Я знаю, что лучшим решением является Block the IPs at the firewall level есть ли способ прагматически добавить / удалить IP-адрес в брандмауэре

в заключение

Мой подход может быть совершенно неправильным … все, что я хочу, является видимым решением, поскольку спамеры и ботнеты растут …

Пожалуйста, это не имеет никакого отношения к DOS атаке, это просто … get lost response

Обновить

  • Брандмауэр: Cisco PIX 515UR

Что-то, что вы можете попробовать, это сохранить список IP-адресов, которые вы хотите заблокировать в текстовом файле, или преобразовать их в хэш-файл dbm , а затем использовать RewriteMap mod_rewrite. Вы должны установить это в своей конфигурации server / vhost. Вы не можете инициализировать карту в файле htaccess .

 RewriteEngine On RewriteMap deny_ips txt:/path/to/deny_ips.txt RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0 RewriteRule ^ - [L,F] 

Файл /path/to/deny_ips.txt будет выглядеть примерно так:

 12.34.56.78 1 11.22.33.44 1 etc. 

По сути, IP, который вы хотите отрицать, и пробел, а затем «1». Любой IP-адрес в этом текстовом файле приведет к тому, что сервер вернет 403 Forbidden . Чтобы немного ускорить работу, вы можете использовать httxt2dbm для генерации хэша dbm, а затем вы определяете отображение следующим образом:

 RewriteMap deny_ips dbm:/path/to/deny_ips.dbm 

Я не уверен, что для производительности используется mod_rewrite, как это, с большим количеством IP-адресов, но быстрый тестовый тест на apache 2.2, работающий на 3Ghz i686 под Linux, разница между 5 IP-адресами в списке по сравнению с 102418 ничтожно мала. Согласно выводам ab , они почти идентичны.


Решение конкретных вопросов:

Возможно ли, что htaccess получит список из базы данных (Redis, Crunchbase, Mongo, MySQL или даже Sqlite) … любой

Используя карту перезаписи, вы можете использовать тип карты « prg » для запуска внешней программы для типа отображения. Затем вы можете написать скрипт perl, php и т. Д., Чтобы поговорить с базой данных, чтобы найти IP-адрес. Также обратите внимание, что оговорки перечислены в разделе «Предостережение». Затем вы использовали бы эту карту, как и любую другую карту ( RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0 ). Это по существу создало бы узкое место для всех запросов. Не лучшее решение для общения с базой данных.

Однако в apache 2.4 существует тип карты dbd / fastdbd , который позволяет создавать запросы через mod_dbd . Это намного лучший вариант, и модуль mod_dbd управляет соединениями с базой данных, соединениями с пулами и т. Д. Таким образом, определение карты будет выглядеть примерно так:

 RewriteMap deny_ips "fastdbd:SELECT active FROM deny_ips WHERE source = %s" 

Предположим, что у вас есть таблица « deny_ips » с 2 столбцами « источник » (IP-адрес) и « активная » (1 для активных, 0 для неактивных).

Существует ли видимое решение для управления такой проблемой в производстве

Если вы храните все заблокированные IP-адреса в базе данных, это вопрос управления содержимым вашей таблицы базы данных. Если вы используете тип карты dbm, я знаю, что по крайней мере perl имеет DBI для управления файлами dbm, поэтому вы можете использовать это для добавления / удаления записей IP из списка запретов. Я никогда не использовал его раньше, поэтому я не могу много говорить об этом. Управление плоским текстовым файлом будет намного сложнее, особенно если вы планируете удалять записи, а не просто добавлять к нему. Вне использования базы данных и mod_dbd apache 2.4 я не думаю, что любое из этих решений готово или готово. Это потребует специальной работы.

Я знаю, что лучшим решением является блокирование IP-адресов на уровне брандмауэра, есть ли способ прагматически добавить / удалить IP-адрес в брандмауэре

Для IPtables существует интерфейс perl , отмеченный как Бета, но я никогда не использовал его раньше. Есть libiptc, но в соответствии с faq netfilter :

Существует ли C / C ++ API для добавления / удаления правил?

Ответ, к сожалению, следующий: Нет.

Теперь вы можете подумать: «а как насчет libiptc?». Как неоднократно отмечалось в списке рассылки, libiptc NEVER предназначался для использования в качестве открытого интерфейса. Мы не гарантируем стабильный интерфейс, и его планируется удалить в следующем воплощении фильтрации пакетов linux. libiptc слишком низкоуровневый, чтобы использовать его в любом случае.

Нам хорошо известно, что для такого API существует фундаментальный недостаток, и мы работаем над улучшением этой ситуации. До тех пор рекомендуется либо использовать system (), либо открыть трубу в stdin iptables-restore. Последний даст вам лучшую производительность.

Поэтому я не знаю, насколько жизнеспособным решением libiptc является отсутствие стабильности API.

ДРУГОЙ ПЕРСПЕКТИВ

Здравствуйте. Вы можете проверить, заблокирован ли адрес или нет, путем доступа к двум байтам в двух блоках данных длиной 8 КБ. Да, я серьезно … Пожалуйста, будьте терпеливы, потому что требуется немного времени, чтобы объяснить это.

ТЕОРИЯ

IP-адрес – это адрес, фактически 4-байтовый номер.

Вопрос в том, что, если мы сделаем это, чтобы рассмотреть битовые позиции ?.

Ответ: Хорошо, у нас будет

  2^32 = 4 Giga Bits 

адресации, и это займет

  4Gb/8 = 512 Mega Bytes 

распределения. Ой! Но не беспокойтесь, мы не собираемся блокировать все в ipverse, а 512MB – преувеличение.

Это может открыть нам путь к решению.

Лилипутский случай

Подумайте о лилипутском мире, в котором существуют только IP-адреса от 0 до 65535. Таким образом, адреса выглядят как 0,1 или 42,42 до 255,255.

Теперь король этого мира хочет заблокировать несколько адресов L-IP (lilliput ip).

Сначала он создает виртуальную двумерную битовую карту длиной 256 * 256 бит, которая занимает:

  64 K Bits = 8 K Bytes. 

Он решает заблокировать этот неприятный сайт «революции», который он ненавидит, потому что он король, например, адрес 56,28.

 Address = (56 * 256) + 28 = 14364.(bit position in whole map) Byte in map = floor(14364 / 8) = 1795. Bit position= 14364 % 8 = 4.(modulus) 

Он открывает файл карты, обращается к 1795-му байту и устанавливает бит 4 (на | 16), а затем записывает его, чтобы отметить сайт как заблокированный.

Когда его сценарий видит 56.28, он выполняет тот же расчет и смотрит на бит, и если он установлен, блокирует адрес.

Теперь, что такое мораль этой истории? Хорошо, мы можем использовать эту лилипутскую структуру.

ПРАКТИКА

Случай реального мира

Мы можем применить дело Лиллипута к реальному миру с помощью «использовать его, когда вам нужно», поскольку выделение 512 МБ-файла не является хорошим выбором.

Подумайте о таблице базы данных с именем BLOCKS с такими элементами:

 IpHead(key): unsigned 16 bit integer, Map : 8KB BLOB(fixed size), EntryCount : unsigned 16 bit integer. 

И еще одна таблица с одной записью со структурой ниже названной BASE

 Map : 8KB BLOB(fixed size). 

Теперь скажем, у вас есть входящий адрес 56.28.10.2

Скрипт обращается к таблице BASE и получает карту.

Он просматривает номера IP более высокого порядка 56.28:

 Address = (56 * 256) + 28 = 14364.(bit position in whole map) Byte in map = floor(14364 / 8) = 1795. Bit position= 14364 % 8 = 4.(modulus) 

Посмотрите на байт 1795 бит 4 на карте.

Если бит не установлен, дальнейшая операция не требуется, поскольку нет заблокированного IP-адреса в диапазоне 56.28.0.0 – 56.28.255.255.

Если бит установлен, скрипт обращается к таблице BLOCKS.

IP-номера более высокого порядка составляли 56,28, что дает 14364, поэтому скрипт запрашивает таблицу BLOCKS с индексом IpHead = 14364. Выбирает запись. Запись должна существовать, так как она отмечена на BASE.

Сценарий выполняет расчет для IP-адреса более низкого порядка

 Address = (10 * 256) + 2 = 2562.(bit position in whole map) Byte in map = floor(2562 / 8) = 320. Bit position= 2562 % 8 = 2.(modulus) 

Затем он проверяет, заблокирован ли адрес, просмотрев бит 2 байта 320 карты поля.

Работа выполнена!

Q1: Почему мы вообще используем BASE, мы можем напрямую запросить BLOCKS с 14364.

A1: Да, мы могли бы, но поиск в BASE-карте будет быстрее, чем BTREE-поиск любого сервера базы данных.

Q2: Что такое поле EntryCount в таблице BLOCKS?

A2: количество IP-адресов заблокировано в поле карты в той же записи. Поэтому, если мы разблокируем ip и EntryCount, достигнем 0, запись BLOCKS станет ненужной. Его можно стереть, а соответствующий бит на карте BASE будет удален.

ИМХО этот подход будет молниеносно. Также для выделения блоба 8K на запись. Поскольку серверы db сохраняют капли в отдельных файлах, файловые системы с 4K, 8K или кратные 4K-страницей будут реагировать быстро.

В случае, если заблокированные адреса слишком разбросаны

Ну, это проблема, которая заставит таблицу базы данных БЛОКИ расти излишне.

Но для таких случаев альтернативой является использование 256 * 256 * 256-битного куба длиной 16777216 бит, равным 2097152 байтам = 2 МБ.

В нашем предыдущем примере более высокое разрешение Ip:

 (56 * 65536)+(28 * 256)+10 

Таким образом, BASE станет 2MB-файлом вместо записи таблицы db, которая будет открыта (fopen и т. Д.), И бит будет рассмотрен с помощью поиска (например, fseek, никогда не читает содержимое всего файла, не нужно), затем обращайтесь к таблице BLOCKS со структурой ниже :

 IpHead(key): unsigned 32 bit integer, (only 24 bit is used) Map : 32 unsigned 8 bit integers(char maybe),(256 bit fixed) EntryCount : unsigned 8 bit integer. 

Вот пример кода PHP для проверки блоков битовой плоскости (8K 8K):

Боковое примечание: этот скрипт можно оптимизировать далее, устраняя несколько вызовов и т. Д. Но написано так, чтобы это было легко понять.

 <? define('BLOCK_ON_ERROR', true); // WARNING if true errors block everyone $shost = 'hosturl'; $suser = 'username'; $spass = 'password'; $sdbip = 'database'; $slink = null; $slink = mysqli_connect($shost, $suser, $spass, $sdbip); if (! $slink) { $blocked = BLOCK_ON_ERROR; } else { $blocked = isBlocked(); mysqli_close($slink); // clean, tidy... } if ($blocked) { // do what ever you want when blocked } else { // do what ever you want when not blocked } exit(0); function getUserIp() { $st = array( 'HTTP_CLIENT_IP', 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR' ); foreach ( $st as $v ) if (! empty($_SERVER[$v])) return ($_SERVER[$v]); return (""); } function ipToArray($ip) { $ip = explode('.', $ip); foreach ( $ip as $k => $v ) $ip[$k] = intval($v); return ($ip); } function calculateBitPos($IpH, $IpL) { $BitAdr = ($IpH * 256) + $IpL; $BytAdr = floor($BitAdr / 8); $BitOfs = $BitAdr % 8; $BitMask = 1; $BitMask = $BitMask << $BitOfs; return (array( 'bytePos' => $BytAdr, 'bitMask' => $BitMask )); } function getBaseMap($link) { $q = 'SELECT * FROM BASE WHERE id = 0'; $r = mysqli_query($link, $q); if (! $r) return (null); $m = mysqli_fetch_assoc($r); mysqli_free_result($r); return ($m['map']); } function getBlocksMap($link, $IpHead) { $q = "SELECT * FROM BLOCKS WHERE IpHead = $IpHead"; $r = mysqli_query($link, $q); if (! $r) return (null); $m = mysqli_fetch_assoc($r); mysqli_free_result($r); return ($m['map']); } function isBlocked() { global $slink; $ip = getUserIp(); if($ip == "") return (BLOCK_ON_ERROR); $ip = ipToArray($ip); // here you can embed preliminary checks like ip[0] = 10 exit(0) // for unblocking or blocking address range 10 or 192 or 127 etc.... // Look at base table base record. // map is a php string, which in fact is a good byte array $map = getBaseMap($slink); if (! $map) return (BLOCK_ON_ERROR); $p = calculateBitPos($ip[0], $ip[1]); $c = ord($map[$p['bytePos']]); if (($c & $p['bitMask']) == 0) return (false); // No address blocked // Look at blocks table related record $map = getBlocksMap($slink, $p[0]); if (! $map) return (BLOCK_ON_ERROR); $p = calculateBitPos($ip[2], $ip[3]); $c = ord($map[$p['bytePos']]); return (($c & $p['bitMask']) != 0); } ?> 

Надеюсь, это поможет.

Если у вас есть вопросы по деталям, я с удовольствием отвечу.

Вам нужно сделать это с помощью внешнего брандмауэра, а не на PHP. Я рекомендую pfSense или PF . Я использовал его раньше, и он очень прост в использовании, очень интуитивно понятен и чрезвычайно эффективен. Это выбор лучших системных администраторов. Я запускаю его на FreeBSD, но он отлично работает и на OpenBSD. Я парень Linux, поэтому мне больно это говорить, но не пытайтесь запускать его в Linux. BSD легко, и вы можете быстро разобраться.

Огромной возможностью для pfSense является возможность конфигурирования с использованием сценариев и ограничение доступа к конфигурации для одного сетевого интерфейса (так что только в локальной сети могут его настроить). Он также имеет несколько функций уровня ID10T, чтобы вы не могли случайно отключить свой доступ.

Вы также должны знать, что многие спамеры могут быстро переключать IP-адреса, используя такие вещи, как Tor . Чтобы исправить это, вы должны включить в свой список блоков адреса, которые известны с помощью выходных узлов (этот список доступен из разных мест).

Блокируйте трафик, прежде чем он достигнет сервера www, используя iptables и ipset.

Поймайте черный IP-трафик в таблице фильтров цепи INPUT, предполагая, что ваш веб-сервер находится на одной машине. Если вы блокируете IP-адреса на маршрутизаторе, вам понадобится цепочка FORWARD.

Сначала создайте ipset:

 ipset create ip_blacklist hash:ip 

IP могут быть добавлены через:

 ipset add ip_blacklist xxx.xxx.xxx.xxx 

Добавьте правило соответствия ipset в ваш iptables (DROP все пакеты соответствуют ipset):

 iptables --table filter --insert INPUT --match set --match-set ip_blacklist src -j DROP 

Это остановит черный список перед сервером www.

Изменить: у меня была возможность просмотреть максимальный размер по умолчанию, и это 65536, поэтому вам нужно будет настроить его для поддержки 100000+ записей:

 ipset create ip_blacklist hash:ip maxelem 120000 

Вы также можете настроить размер хэша:

ipset create ip_blacklist hash:ip maxelem 120000 hashsize 16384 (должен быть 2)

Мой опыт ipset lookup оказывает незначительное влияние на мою систему (~ 45000 записей). В сети есть несколько тестов. Память для множества является ограничивающим фактором.

Если вы хотите, чтобы добавить / удалить с помощью кода, взгляните на denyhosts . Вы можете либо сохранить список IP через код, либо исправить источник для чтения из любого места, которое вы хотите.

Существует проект с netfilter для того, что называется ipset, поэтому вы можете добавить или удалить ip в список, и вам просто нужно создать правило против этого списка

http://ipset.netfilter.org/

Если вы блокируете IP-адреса, вы действительно должны делать это на уровне брандмауэра (вы не хотите, чтобы пользователи с нежелательных IP-адресов попадали очень далеко в вашу систему). Таким образом, я предлагаю написать сценарий bash, который запрашивает базу данных и соответствующим образом изменяет ваш конфигурационный файл брандмауэра (предполагается, что вы хотите, чтобы решение, которое использует IP-адреса, хранящиеся в вашей веб-базе данных, очень хорошо могло бы стать лучшим местом для хранения такой информации ).

EDIT: Если вы хотите добавить IP-адреса в черный список на уровне PHP, как предложил @Populus, вот руководство по использованию системных вызовов в PHP: http://php.net/manual/en/function.system .php

И вот команды, которые вам нужно использовать для добавления IP-адреса в черный список, если вы используете iptables: http://www.cyberciti.biz/faq/linux-iptables-drop/

Я знаю способ
его по php. как вы упомянули в самом начале этого вопроса.

 $ipList = []; // array list or from database if (in_array(getIP(), $ipList)) { // Log IP & Access information header("https://www.google.com.ng/search?q=fool"); // redirect exit(); // exit } 

Я читал то, что вы написали. но подождите минутку.
почему вы хотите это сделать. Не важно. Но почему в этой ситуации.
Я имею в виду, почему вы хотите избежать доступа к php из-за его скорости или просто из-за предотвращения и из-за того, что так сложно вызвать функцию на всех страницах? если единственная причина избежать того, что какой-либо ip от доступа к php is.avoiding theme, чтобы увидеть содержимое.
так что у меня появилась идея, и я вас так пропустил.
используя одну точку входа.

Я работал с этим решением.

сначала с помощью простого htaccess вы отправляете все запросы на одну страницу, называемую точкой входа (например, index.php)
с простым правилом перезаписи я дам его вам. поэтому, когда пользователь запрашивает

 mysite.com/some/path/page.php or anything 

htaccess выполнит что-то вроде следующего без изменения URL-адреса. поэтому пользователь ничего не почувствует.

 mysite.com/index.php?r=some/path/page.php 

поэтому каждый запрос стал одним запросом с разными параметрами $ _GET ['r']. поэтому для запроса evey будет выполнен index.php. и теперь мы можем сделать что-то подобное в index.php

 $ipList = []; // array list or from database if (in_array(getIP(), $ipList)) { // Log IP & Access information header("https://www.google.com.ng/search?q=fool"); // redirect exit(); // exit } //if after this execute means the IP is not banned //now we can include file that $_GET['r'] points to include $_GET['r']; 

его так просто. И его реальный настолько сложный. Но основная идея такая же. что вы думаете?

Похоже, что большинство из нас согласны блокировать уровень брандмауэра .

У вас может быть программа, которая прослушивает ваш сайт для ips для блокировки и создания сценария:

 ip = getNextIpToBlock() an = increment_unique_alphanum_generator() script = generate_script(ip, an) 

скрипт будет выглядеть примерно так (где [an] – буквенно-цифровое значение, а [ip] – блокировка ip):

 en [enter] *password* [enter] conf t [enter] access-list [an] deny ip [ip] 0.0.0.0 any [enter] access-group [an] in interface outside [enter] 

Затем вы загружаете этот скрипт в другую программу, которая выполняет удаленные вызовы telnet или ssh в ваш FW CLI.

Не забудьте выйти из системы и, возможно, каждые 100 ips вы скопируете текущую конфигурацию, чтобы начать настройку.

Я не знаю, но теперь вы можете узнать, каковы ограничения для вашего брандмауэра.

Лучший,

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

ИМХО, есть несколько углов, из которых этот вопрос можно рассмотреть

  1. У вас очень большой набор уникальных IP-адресов для блокировки. Чем раньше вы блокируете их на своем сервере, тем меньше вычислительной мощности вы будете тратить на них. Вы можете добавлять / удалять IP-адреса в своем брандмауэре с помощью соответствующих системных вызовов с PHP.

Учитывая вышеприведенный вариант, единственными актуальными вопросами являются:

  • Часто ли они посещают? => Если да, то решение (1) – ваш лучший выбор.
  • Может ли ваш брандмауэр эффективно обрабатывать его? => Если нет, вы можете рассмотреть другое решение.

Я мог бы выбрать второй вариант .htaccess .