Как заблокировать 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 not 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)... any
  • Есть ли видимое решение для управления такой проблемой в производстве.
  • Я знаю, что лучшим решением является Block the IPs at the firewall level, есть ли способ прагматически добавить/удалить IP-адрес в брандмауэре

Наконец

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

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

Обновление

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

Ответ 1

Что-то, что вы можете попробовать, это сохранить список IP-адресов, которые вы хотите заблокировать, в текстовом файле или преобразовать его в dbm хэш файл, затем используйте mod_rewrite RewriteMap. Вы должны установить это в своей конфигурации 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)... any

Используя карту перезаписи, вы можете использовать тип карты prg для запуска внешней программы для типа сопоставления. Затем вы можете написать perl, php и т.д. script, чтобы поговорить с базой данных, чтобы найти 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 из запрета список. Я никогда не использовал его раньше, поэтому я не могу много говорить об этом. Управление плоским текстовым файлом будет намного сложнее, особенно если вы планируете удалять записи, а не просто добавлять к нему. Вне использования базы данных и apache 2.4 mod_dbd, я не думаю, что какое-либо из этих решений готово или готово. Он будет нуждаться в специальной работе.

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

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

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

Ответ, к сожалению, есть: Нет.

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

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

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

Ответ 2

ДРУГИЕ ПЕРСПЕКТИВЫ

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

ТЕОРИЯ

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

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

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

  2^32 = 4 Giga Bits 

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

 4Gb/8 = 512 Mega Bytes

распределения. Ouch! Но не волнуйтесь, мы не собираемся блокировать все в 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), затем записывает его обратно, чтобы отметить сайт как заблокированный.

Когда его script видит 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

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

Он ищет IP-номера :

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.

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

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

Script выполняется расчет для младшего 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 для проверки блоков версии bitplane-bitplane (8K 8K):

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

<?
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);
}

?> 

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

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

Ответ 3

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

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

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

Ответ 4

Заблокируйте трафик до того, как он достигнет 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 оказывает незначительное влияние на мою систему (~ 45000 записей). В сети есть несколько тестов. Память для набора является ограничивающим фактором.

Ответ 5

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

Ответ 6

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

http://ipset.netfilter.org/

Ответ 7

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

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

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

Ответ 8

Я знаю способ
его по 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 - это поиск темы для просмотра содержимого.
так что у меня появилась идея, и я вас так пропустил.

используя одну точку входа.

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

сначала с помощью простого 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'];

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

Ответ 9

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

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

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

script будет выглядеть примерно так (где [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]

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

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

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

Бест,

Ответ 10

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

Ответ 11

IMHO, есть несколько углов, из которых этот вопрос можно считать

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

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

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

.htaccess будет моим вторым выбором.