DoS своими силами: К чему приводит бесконтрольный рост таблиц в базе данных



    Биллинг — сложная система, одним из важнейших компонентов которой является база данных. В ней содержится множество разнообразных таблиц, которые со временем разрастаются до больших размеров.

    Для того, чтобы это рост не замедлял работу базы, в Oracle, PostgreSQL и других СУБД существует эффективный механизм секционирования (partitioning) — однако его не всегда можно применять. К примеру, он отсутствует в относительно бюджетной редакции системы Oracle Standard Edition.

    Исторически сложилось так, что в нашем биллинге для операторов связи «Гидра» мы не реализовывали собственный механизм секционирования, ограничившись созданием руководства для клиентов, в котором были описаны шаги для отслеживания роста таблиц и минимизации возможных проблем с быстродействием системы. Как выяснилось в дальнейшем, этого было явно недостаточно.

    Предыстория


    Биллинг «Гидра» используют операторы связи, а значит, одной из самых важных и крупных таблиц в базе является та, которая отвечает за хранение данных о PPP-сессиях и CDR абонентов провайдеров интернета или телефонии. В этой таблице хранятся данные о начале, завершении и времени ее последнего обновления, а также информация об абоненте, инициировавшем ее, например, IP-адресе, с которого он установил соединение.

    Чем больше в базе оператора связи абонентов, тем быстрее растет таблица с данными о сессиях. Самым простым решением в этой ситуации выглядит простое удаление старых записей время от времени. Однако по российскому законодательству провайдеры обязаны хранить данные о доступе абонентов в сеть, которые могут понадобиться правоохранительным органам, на протяжении трех лет.

    При разработке биллинга еще много лет назад мы учли этот момент и допустили, что объем таблицы с сессиями может достигать 15 млн строк. По достижению этой планки должен был запускаться скрипт выгрузки и архивации данных.

    Тем не менее, необходимость мониторинга размеров таблицы сохранялась — если совсем не обращать на это внимания, она могла вырасти и до значительно больших размеров. Что в конечном итоге и произошло у одного из наших клиентов.

    Все плохо


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

    Таблица росла, причем об этом не знала ни компания, в чьей базе все происходило, ни наша служба поддержки. В конечном итоге ее размер составил 36 млн строк, что более чем в два раза превысило максимальный лимит.

    У этого конкретного оператора абоненты попадали в сеть по VPN, а значит, им нужно было получить доступ к VPN-серверу, который уже по протоколу RADIUS обращался к базе данных.

    В результате в день, когда в сети оператора случился всплеск активности абонентов вследствие перезагрзуки одного из магистральных коммутаторов, RADIUS-сервер биллинга не смог своевременно авторизовать нахлынувший поток абонентов.
    Произошло вот что — производительность выборок из таблицы сессий в определенный момент серьезно упала. Это привело к тому, что процесс авторизации абонентов не успевал завершиться в отведенное для этого время. В результате пользователи стали получать отказы доступа в сеть и повторно отправлять запросы на авторизацию, которые слились в настоящую лавину, захлестнувшую систему.

    При таймауте обработки запроса VPN-сервер посылает еще пару запросов на авторизацию — и для их обработки опять необходимо прочитать данные из той самой огромной таблицы. И только после этого абонент увидит сообщение об отказе в доступе. Естественно, что получив такое сообщение, пользователь пытается установить соединение повторно, и все повторяется вновь. Таким образом возникла ситуация с настоящим DoS сервера RADIUS.

    Что делать


    Столкнувшись с серьезными проблемами авторизации собственных абонентов, провайдер обратился в нашу службу поддержки. Причину проблем удалось установить очень быстро — слишком уж большой была таблица с сессиями, кроме того в топе запросов по IOWAIT находились те самые запросы на чтение из таблицы сессий. Исправить сложившуюся ситуацию можно было единственным способом — очисткой строк таблицы в БД. При этом данные потерять было нельзя, так что их предварительно нужно было куда-то скопировать.

    Поэтому мы выгрузили записи в CSV-файлы, тем самым уменьшив объём таблицы. Чтобы пустить в сеть часть абонентов в процессе этих работ, был также активирован автономный режим работы RADIUS-сервера. Он заключается в использовании механизма кэширования — в кэше сохраняются данные о результате последней авторизации абонента, так, чтобы при потере связи с биллингом сервер мог провести авторизацию пользователей с использованием этих данных.

    Механизм автономного режима на тот момент был довольно прост и имел свои недостатки — например, данные в кэше не находились в постоянной репликации с базой данных, поэтому в них могли быть расхождения. Это значит, что если пользователь последний раз авторизовался месяц назад, а затем не платил за интернет и должен был быть заблокирован, то при работе в автономном режиме на основе сохраненной записи месячной давности система его авторизует — или не авторизует того, кто имеет право на доступ, но был заблокирован в последнюю попытку соединения. Но в сложившейся ситуацией это было меньшее из зол.

    В итоге, когда таблица с сессиями сократилась в размере до 30 млн строк, авторизация абонентов вновь заработала, и мы смогли отключить автономный режим работы RADIUS-сервера.

    Уроки


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

    Теперь при обнаружении потенциально опасной ситуации, подобной описанное выше, создается заявка в службу поддержки «Латеры», с которой мы детально разбираемся.

    Кроме того, мы изменили и подход к организации работы с данными и процесса кэширования RADIUS-сервера. Все это вылилось в создание механима архивации в новой версии биллинга — он предусматривает архивирование данных постоянно растущих таблиц в отдельную схему Oracle. Этот процесс идет в фоновом режиме и не влияет на работу биллинга. Причем для клиента этот механизм работает таким образом, что при создании отчетов по сессиям, он получает одновременно данные из основных и таблиц из схемы с архивом, без «плясок с бубном».

    Также мы сделали полностью автономный RADIUS-сервер с локальной базой данных, которая выступает не в качестве кэша, а локально хранит реплицированную из основной базы биллинга информацию, необходимую для авторизации абонентов. Таким образом была решена проблема возможного предоставления услуг связи тем, абонентам, которые должны быть заблокированы, и отказ в доступе для тех, кого нужно авторизовать. В новом варианте RADIUS-сервера реализованы механизмы по максимально безболезненному выходу из ситуации при разрыве связи с биллингом. Мы описывали их в статье про обеспечение отказоустойчивости биллинга.


    Если описывать схему кратко, то каждый сервер доступа состоит из нескольких компонентов:

    • База данных с профилями абонентов и данными о потребленных услугах.
    • Наше приложение под кодовым названием HARD. Оно отвечает на HTTP-запросы, которые идут от следующего компонента.
    • FreeRADIUS — непосредственно сервер, реализующий стандартный AAA-протокол — RADIUS. Он непосредственно общается с абонентской сетью и переводит запросы из бинарного формата в обычный HTTP+JSON для HARD.

    Базы данных всех AAA-серверов (это MongoDB) объединены в группу с одним основным узлом (master) и двумя подчиненными (slave). Все запросы из абонентской сети идут на один AAA-сервер, при этом необязательно и даже нежелательно, чтобы им был сервер с основной БД.

    Если что-то пойдет не так, и один из компонентов откажет, то, абоненты не потеряют доступ к услугам. Скорее всего, они вообще ничего не заметят.

    Кроме того, мы продолжаем следить за размером таблиц, в том числе отслеживаем объём таблицы сессий — никто не застрахован от роста данных в неархивный период.

    На сегодня все, спасибо за внимание! Не забывайте подписываться на наш блог.
    Латера Софтвер 49,78
    Компания
    Поделиться публикацией
    Комментарии 4
    • +1
      С чем связан выбор MongoDB?
      • +3
        Какой то непонятный подход. Одна и та же таблица выступает как хранилище и транзакционная таблица.

        А надо просто иметь 2 таблицы. Одна, маленькая, для авторизации с актульными последними данными и архивная.
        • +2
          Думаю, тут надо оптимизировать логическую структуру базы данных (в смысле правильного выбора чисто сущностных понятий), эффективно её администрировать (строить индексы, статистику и т.д.) и писать эффективные запросы. При том, что сессии абонентов в реальном времени только добавляются, любой человек, хранящий в базе данных телеметрию, скажет вам, что 36 млн строк – это ни о чём.
          • 0
            1) Выбор MongoDB был сделан в 2010 году, когда выбор NoSQL-решений был ограничен. С тех пор желания поменять ее не возникало: работает быстро, взаимодействовать удобно. У нее есть проблемы с надежностью — несколько раз за несколько лет эксплуатации на сотне клиентов боевая БД повреждалась или внезапно разрушалась. Но поскольку первичные данные в Mongo не хранятся, то ее БД можно быстро пересоздать с нуля из основной базы.

            2) Совершенно верно, что таблица сессий должна состоять из двух частей — актуальной и архивной. Примерно так и было, только тогда перенос данных из актуальной таблицы в архивную не был автоматизирован (это довольно редкая операция) и запускался вручную по сигналу с мониторинга. Однажды сигнал не поступил и все пошло вразнос.

            3) 36 млн строк — это, конечно, немного для телеметрии или мониторинга. Но с нашей таблицей сессий есть отличия — сессии не только добавляются, но и обновляются в течение своего срока жизни, а этот срок может достигать, например, месяца. И выборки/индексы/гистограммы бесконечно оптимизировать невозможно, нужны были архитектурные решения. Поэтому по итогам разбора полетов мы таки сделали автоматическую архивацию, чтобы не было человеческого фактора, а позже вообще убрали авторизационные запросы к таблице сессий, переделав механизмы работы с кэшем.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое