Рулим трафиком в Linux. Аккаунтинг, сбор статистики

    Каждый админ должен в своей жизни настроить сендмейл, написать биллинг и обругать маздай :)
    © bash.org.ru

    Сендмейл мы настраивать не будем, как и ругать детище Билли, а попробуем создать что-то вроде биллинга, т.к. этот вопрос рано или поздно встает практически перед всеми системными администраторами.

    Статья ориентирована на новичков в администрировании и опытным спецам навряд ли будет интересна.

    И так, что мы имеем? Сеть из 50-200 компьютеров, выделенный интернет-канал (ADSL, Ethernet, etc..), linux-роутер. Требуется считать интернет-трафик пользователей (по разным направлениям), ограничивать скорость, определять лимиты трафика в месяц. В дополнение, хочется управлять всем этим через какую-нибудь веб-морду.

    За основу возьмем дистрибутив ubuntu-server 8.04.

    Чтобы однозначно идентифицировать пользователя и обезопасить кражи трафика при помощи подмены айпи-адресов, настроим VPN-сеть, используя PPTPD.

    Практически все действия в консоли будем выполнять от рута, поэтому чтобы не писать каждый раз sudo, пишем:
    rustam@srv:~$ sudo -i

    Настройка PPTP-сервера


    Устанавливаем:
    root@srv:~# apt-get install pptpd


    Открываем конфиг /etc/pptpd.conf и добавляем строчки:
    localip 10.1.0.1
    remoteip 10.1.0.2-254

    определяем адрес сервера и диапазон адресов пользователей в виртуальной сети.

    По-умолчанию pptpd хранит аккаунты пользователей в файле /etc/ppp/chap-secrets, добавляем тестовые аккаунты:
    # Secrets for authentication using CHAP
    # client server secret IP addresses
    user1   pptpd   123   10.1.0.2
    user2   pptpd   567   10.1.0.3


    обратите внимание на айпи-адреса, они должны быть из диапазона, указанного в параметре remoteip файла /etc/pptpd.conf!

    Перезапускаем сервис pptpd:
    root@srv:~# /etc/init.d/pptpd restart


    Разрешаем подключения к нашему сервису:
    root@srv:~# iptables -A INPUT -s 192.168.0.0/24 -p tcp -m state --state NEW -m tcp --dport 1723 -j ACCEPT


    Вместо 192.168.0.0/24 подставьте адрес своей локальной сети. Пробуем подключиться к нашей виртуальной сети, для этого создаем на машине с Win XP новое VPN — соединение («Создание нового подключения» — «Подключение к сети на рабочем месте» -«Подключение к виртуальной частной сети»). Если все получилось двигаемся дальше.

    Настройка ulog-acctd


    Для сбора статистики о трафике будем использовать ulog-acctd, устанавливаем:
    root@srv:~# apt-get install ulog-acctd


    Открываем конфиг /etc/ulog-acctd.conf. Нас интересуют параметры accounting format и fdelay.

    Параметр accounting format отвечает за формат записи лога, подробное описание каждого параметра есть в комментариях, так что подробно расписывать не буду. Нам нужен такой формат:
    # время отправитель получатель трафик
    accounting format="%t\t%s\t%d\t%b\n"


    Параметр fdelay определяет через какой промежуток времени ulog-acctd будет сбрасывать накопленную статистику в лог, выставим его в 10 секунд.
    fdelay=10


    Перезапускаем ulog-acctd.

    Настройка iptables


    Чтобы подключившиеся пользователи могли выходить в Сеть, настроим NAT для виртуальной сети:
    root@srv:~# iptables -t nat -A POSTROUTING -s 10.1.0.0/24 -j MASQUERADE


    Этим правилом сообщаем ulog'у какой трафик логировать:
    root@srv:~# iptables -A FORWARD -d 10.1.0.0/24 -j ULOG --ulog-cprange 48 --ulog-qthreshold 50


    Нас интересует транзитный трафик, идущий в VPN — сеть.

    Теперь протестируем получившуюся связку, подключитесь к сети и попробуйте что-нибудь скачать, в логе /var/log/ulog-acctd/account.log должны появится такие записи:
    1224187495 208.67.222.222 10.1.0.2 126
    1224187534 205.188.9.192 10.1.0.2 40
    1224187556 62.213.122.2 10.1.0.2 10660
    1224187594 205.188.9.192 10.1.0.2 40
    1224187599 62.213.122.2 10.1.0.2 22130
    1224187615 62.213.122.2 10.1.0.2 1037
    ...

    Первое значение — время в формате unix, второе — адрес отправителя, третье — адрес получателя (пользователя), четвертое — количество байт.

    MySQL и схема БД


    root@srv:~# apt-get install mysql-server

    Создаем в MySQL пользователя ulog и импортируем схему БД:
    CREATE DATABASE `ulogdb` DEFAULT CHARSET utf8;

    USE `ulogdb`;

    CREATE TABLE `data` (
        `id` int(11) NOT NULL auto_increment,
        `id_user` int(11) NOT NULL,
        `ts` int(11) NOT NULL,
        `bytes` int(11) NOT NULL,
        PRIMARY KEY (`id`)
    ) ENGINE=MyISAM DEFAULT CHARSET utf8;

    CREATE TABLE `users` (
        `id` int(11) NOT NULL auto_increment,
        `login` varchar(32) NOT NULL,
        `password` varchar(64) NOT NULL,
        `ip` varchar(15) NOT NULL,
        PRIMARY KEY (`id`),
        KEY `ip` (`ip`)
    ) ENGINE=MyISAM DEFAULT CHARSET utf8;


    Это упрощенная схема, позже мы ее модернизируем. В таблице data храним агрегированную статистику по трафику, в users информация о пользователях. Добавьте в таблицу users пользователей, которых указали в /etc/ppp/chap-secrets.
    INSERT INTO `users` (`login`, `password`, `ip`) VALUES
    ('user1', '123', '10.1.0.2');
    INSERT INTO `users` (`login`, `password`, `ip`) VALUES
    ('user2', '567', '10.1.0.3');


    Парсим лог


    Писать будем на perl, поэтому необходимо доустановить модуль для работы с MySQL:
    root@srv:~# apt-get install libdbd-mysql-perl


    Привожу полный текст срипта:
    #!/usr/bin/perl
       
    use DBI;

    # определяем имя БД, пользователя и пароль
    my $db_name = "ulogdb";
    my $db_user = "ulog";
    my $db_pass = "1234";

    # путь к лог-файлу
    $account_log = "/var/log/ulog-acctd/account.log";

    # подключаемся к нашей базе
    my $DBH = DBI->connect("DBI:mysql:$db_name:localhost",$db_user,$db_pass) or die "Error connecting to database";

    # получаем список пользователей в связке ip+id_user
    my $STH = $DBH->prepare("select ip,id from users");
    $STH->execute;
    while (@tmp = $STH->fetchrow_array()) {
        $users{$tmp[0]} = $tmp[1];
    }
    $STH->finish;

    # делаем временную копию лога и очищаем оригинальный файл
    system "cp $account_log /tmp/ulog-parser.tmp && cat /dev/null > $account_log";
    open LOGFILE,"< /tmp/ulog-parser.tmp";
    while (<LOGFILE>) {
        chomp;
       
        # переменную $saddr пока не используем,
        # она пригодится позже

        ($ts,$saddr,$daddr,$bytes) = split /\t/;

        # создаем новую временную метку, необходимо для агрегирования
        # статистки пользователя за определенный интервал времени
        # в одну запись. интервалом будем считать 1 минуту

        $ts = $ts - $ts % 60;

        # сопоставляем айпи из лога со списком пользователей
        # если айпи имеется в базе - наш клиент
        # массив со статистикой имеет древовидную структуру:
        # метка времени -> id пользователя -> полученный трафик

        if (exists($users{$daddr})) {
           $data{$ts}{$users{$daddr}} += $bytes;
        }
    }
    close LOGFILE;
    unlink("/tmp/ulog-parser.tmp");

    # подготавливаем запросы к БД
    my $STH_CHK = $DBH->prepare("select id_user,id from data where ts=?");
    my $STH_ADD = $DBH->prepare("insert into data (id_user,ts,bytes) values(?,?,?)");
    my $STH_UPD = $DBH->prepare("update data set bytes=bytes+? where id=?");

    # проходим по всему массиву статистики вложенным циклом
    #
    for $ts (keys %data) {
        # проверяем, если ли в базе записи с такой меткой времени
        $STH_CHK->execute($ts);
        %ex_data = ();
        while (@tmp = $STH_CHK->fetchrow_array()) {
           $ex_data{$tmp[0]} = $tmp[1];
        }
        $STH_CHK->finish;

        # выбираем данные о пользователях в текущем периоде
        for $id_user (keys %{$data{$ts}}) {
           if (exists($ex_data{$id_user})) {
              # если запись уже есть, то просто обновляем значение поля bytes
              $STH_UPD->execute($data{$ts}{$id_user},$ex_data{$id_user});
              $STH_UPD->finish;
           } else {
              # иначе добавляем новую запись
              $STH_ADD->execute($id_user,$ts,$data{$ts}{$id_user});
              $STH_ADD->finish;
           }
        }
    }
    # отключаемся от БД
    $DBH->disconnect;


    Сохраним скрипт как /usr/bin/ulog-parser.pl и установим атрибуты:
    root@srv:~# chmod 700 /usr/bin/ulog-parser.pl


    Пробуем запустить, и смотрим, появились ли данные в таблице data:
    mysql> select count(id) from data;
    +-----------+
    | count(id) |
    +-----------+
    |       118 |
    +-----------+

    При минимальных знаниях PHP и SQL сделать вывод статистики не составит труда ;)

    На этом я пока закончу, статья и так слишком большая получилась. Если данная тема будет Вам интересна — напишу продолжение (группировка по трафика по направлениям, ограничение скорости).

    P.S.: Спасибо ISVir и thestorm за карму. Первый пост.

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 61

      +1
      Очень заинтересован в подобной информации. Особенно было бы интересно услышать о решениях без ВПН =)
        +2
        Без впн можно, просто пропускаете пункт с настройкой PPTPD и везде заменяете сеть 10.1.0.0/24 на вашу локальную.
          +2
          не в тему, но если нужно NAT'ить VPN трафик, то не забудьте сделать:

          /sbin/modprobe ip_nat_pptp
            0
            Мне вот тоже интересно, особенно было бы интересно услышать о решениях без vpn, и с авторизацией через Active Directory. =) Заранее спасибо, всенепременнейше ждём. =)
              0
              Решается использованием сквиды.
            0
            Огромное спасибо! :) Добавлю в закладки, чуть позже точно пригодится.
              +3
              Несколько замечаний по поводу mysql и perl

              1)Для мускля. inet_aton('123.124.123.123') сохраняем ипишник как int usnigned
              2)Делаем таблицу data
              traf_date DATE, from_ip INT UNSIGNED, to_ip INT UNSIGNED, traf INT UNSIGNED. индекс по всем полям кроме traf
              Сюда не нужно складывать ид пользователя. Пуской он живет в отдельной таблице.
              3)Грузим данные через LOAD DATA INFILE в темповую таблицу от туда INSERT INTO… ON DUPLICATE KEY UPDATE traf+VALUES(traf).
              4)Опционально создаем еще одну табличку где просто дата, ип пользователя и кол-во трафика. Так как это будет самый востребованный отчет.
                +1
                1) я патчил ulog-acctd для вывода айпи сразу в десятичной форме, прирост производительности был довольно существенный. Не стал пока рассматривать, чтобы не захламлять статью.

                2) дело в том, что если хранить from_ip и to_ip в базе, то она довольно быстро разрастается до неприличных размеров, особенно, если пользователей под сотню, думаю, достаточно преопределить интересующие направления в отдельной таблице и группировать трафик по ним. Напишу в следующей статье.

                3) Спасибо за идею, думаю, попробую :)
                4) Честно говоря, не вижу в этом смысла. Любые отчеты можно вытащить из data.
                  0
                  2) Там только инт поля + одно с датой, кроме этого в течении дня у нас данные по направлению суммируются. Даже с парой миллионов запесей такая таблица будет небольшая. Тем более хранить вряд ли актуально данные больше чем на месяц.

                  4) Только за какое время :) использование sum(blabla) с GROUP BY не использует индекс. То есть сначала по условиям в where будет создана таблица потом по ней будет filesort
                +2
                Перенес в «Linux для всех»
                  +1
                  Жду продолжения с нетерпением
                    0
                    Отличный пост. Понравилось. Пиши ещё и подробнее, информация действительно очень полезна для начинающих.
                      0
                      Есть ли варианты отброса парсера логов и напрямую с ulog кидать статистику в MySQL?
                        0
                        Есть, AFAIK, такое умеет ulogd, но объем базы уже через сутки будет космическим, к тому же записывать каждую запись в БД довольно накладно по производительности.
                          0
                          Т. е. гибкой настройки легирования не добиться? Я не имел опыта работы с ulogd, интересны минусы и плюсы.
                            0
                            минусы — объем базы. ulogd нужен только для анализа атак и invalid packets.
                            0
                            Не знаю деталей лога, но думаю при помощи тригера возможно отсеять мусор.
                              –1
                              Ну если делать тупо INSERT каждый раз, то да. а если граматно UPDATE организовать, то объемы не такие будут :)
                                0
                                ulogd-mysql делает как раз «тупо INSERT»
                              +1
                              в принципе — да

                              ulogd-mysql — MySQL extension to ulogd
                              ulogd-pgsql — PostgreSQL extension to ulogd
                              ulogd-sqlite3 — SQLite 3 extension to ulogd

                              Но это дело логи просто напрямую в базу пишет.
                                +1
                                Всем спасибо, оказывается такая штука существует:

                                # aptitude search ulog
                                p fprobe-ulog — export captured traffic to remote NetFlow Collector (ULOG version)
                                p ulog-acctd — Accounting daemon for Linux 2.4+ netfilter
                                p ulogd — The Netfilter Userspace Logging Daemon
                                p ulogd-mysql — MySQL extension to ulogd
                                p ulogd-pcap — pcap extension to ulogd
                                p ulogd-pgsql — PostgreSQL extension to ulogd
                                p ulogd-sqlite3 — SQLite 3 extension to ulogd
                                  0
                                  Штука существует, но она крайне неполезна. При более-менее существенном объеме трафика биллинг благополучно умрет. Лучше формировать из ULOG netflow-поток, который затем и складывать в базу. А еще лучше, если база и сервер доступа будут жить на разных машинах.
                                0
                                я бы заменил мускул на постгрес, тем более что есть пакеть ulog-pgsql.
                                Тем более, что в постгресе есть специальный тип данных для адресов и там очень удобно потом делать детализацию по направлениям — трафик внутри сети провайдера, трафик внешний.

                                Где-то у меня даже валялась схемка такой базы (с активным использование хранимых процедур для автоматизации работы) и скрипт для создания итоговых значений по дням с удалением детализации каждого пакета (точнее скрипт просто дергал хранимки.)
                                  0
                                  Спорный вопрос, что будет быстрее, парсер или хранимки постгреса.
                                    0
                                    скажем так —
                                    20 пользователей. Машинка семпрон 2200+ (еще которая на сокете А), полгига оперативки. Помимо всего прочего файлопомойка с кучей одинэсных баз.
                                    Нагрузки от прогона хранимок была не больше 5% ресурсов процессора. И то я тогда стормозил и с индексами не заморачивался.
                                      0
                                      Часто на сервере уже используется мускул для других целей, специально ставить еще и постгрес нет желания
                                        0
                                        А у меня наоборот :)
                                    –1
                                    Отличная статья!
                                      –1
                                      а если необходимо сделать это в масштабе офиса с виндовыми машиными чтоб авторизация шла через samba AD и на выходе биллинга трафик разделялся и считался по типам сетей (бесплатные-городские-внешние) можете такую инструкцию написать?
                                        +1
                                        Прикрутите к PPTPD Freeradius, а к нему — авторизацию в домене (не знаю, возможно ли). Детализацию трафика по типа сетей озвучу в следующей статье.
                                          +1
                                          да, действительно. Радиус, кибериос-авторизацию и для удобства сразу netams ;-)
                                            0
                                            это уже другая статья. хотите написать? ;)
                                              0
                                              я кстати не понимаю, для чего писать статьи для систем, у которых вполне таки внятная документация?

                                              Единственный минус — у такого рода систем достаточно высокий порог вхождения, и они предназначены явно не для той целевой аудитории, для которой была написана эта статья.
                                          0
                                          кто знает готовую софтину для анализа этих логов? с рисованием графиков и воборкой временных интервалов?
                                          да, можно и самому написать, но боюсь, что придется свой велосипед изобретать.
                                            +3
                                            Статья простая и хорошая — вот хорошо было бы с нее начать цикл статей, в котором модернизировать решение по функциональности и скорости.
                                              +2
                                              так и задумано
                                              +1
                                              Все отлично, но вкралась опечатка в последнем слове: «Сохраним скрипт скрипт как /usr/bin/ulog-parser.pl и установим атриьуты:»
                                                +1
                                                Сенк, исправил.
                                                +1
                                                Спасибо за статью. Интересно. С нетерпением жду продолжения.

                                                Было бы неплохо осветить такие моменты:
                                                1. Использование cacti, а еще лучше кастомный скриптик на rrdtool для визуальной отрисовки загрузки общего канала, а также загрузки каждой пользовательской машины.
                                                2. Оповещение по email при наличии подозрительного исходящего трафика на одной из пользовательских машин (такая активность бывает когда юзверь подхватил какойнть вирь или червь, очень удобно для админа)
                                                3. открытие/закрытие определенных сервисов для всех сразу и для определенного юзера в часности (smpt, pop3, ftp, torrent и т. п.)
                                                4. Проброс портов для возможности доступа извне на внутреннюю пользовательскую машину.
                                                  –1
                                                  спасибо
                                                    –1
                                                    Очередное изобретение велосипеда с квадратными колесами.
                                                      –2
                                                      +1
                                                        0
                                                        согласен.
                                                        перечислите предыдущие велосипеды, пожалуйста.
                                                          0
                                                          Мой личный велосипед:
                                                          code.google.com/p/cakebilling/

                                                          Я лично осуществлял порядка 5 установок. Работает на автопилоте уже пару лет там, пить есть не просит.
                                                            0
                                                            Цель статьи — не создание «велосипеда», а разъяснение принципов работы этих самых велосипедов на конкретном примере.

                                                            П. С.: раз уж на то пошло, то мой «велосипед» назывался SubBilling, я его года полтора назад пустил в «свободное плаванье», люди подхватили, правили баги, дополняли. Ссылку дать не могу, т. к. «официальный сайт» раз 5 переезжал и сейчас вообще непонятно где.
                                                              +1
                                                              Ваш велосипед плох тем, что использует не самые лучшие колеса. Использование ulog это не сильно хорошая идея. Оно конечно есть, но лучше не использовать. Лучше использовать ipcad и собственно любые другие шутки которые учитывают трафик не через firewall а при помощи libpcap или при помощи счетчиков на интерфейсе (pppd имею ввиду) или с netflow. Это исключает проблему не попадания трафика в учет.
                                                                0
                                                                У ipcad точно такой же формат вывода лога, прикрутить его вместо ulog'a не составляет проблемы.
                                                                  0
                                                                  Тем более.
                                                                  0
                                                                  ipcad позволяет снимать трафик с помощью нескольких разных методов, в т. ч. и ULOG.

                                                                  А использовать ULOG можно только если трафик небольшой. Когда-то несколько лет назад и я писал свой велосипед :) Так вот на больших загрузках ULOG начинал нещадно врать в меньшую сторону на радость клиентам-халявщикам. В итоге перешел на IPQueue и все стало ок.
                                                                    0
                                                                    По возможности лучше не использовать сьем трафика с фаервола.
                                                                0
                                                                посмотрел — респект за документацию!
                                                            0
                                                            Классная статья, для новичков типо меня очень полезна. Планирую опробовать на днях.
                                                              0
                                                              Да, статья полезная. Спасибо автору.
                                                              Хотелось бы так же почитать, каким образом можно лимитировать трафик пользователей в определенный отрезок времени (день/месяц).
                                                                0
                                                                Некоторые замечания

                                                                >iptables -A INPUT -s 192.168.0.0/24 -p tcp -m state --state NEW -m tcp --dport 1723 -j ACCEPT

                                                                пакеты в цепочке INPUT, насколько я помню, имеют state NEW по-умолчанию. Можно сократить до:
                                                                iptables -A INPUT -s 192.168.0.0/24 -p gre -j ACCEPT
                                                                {-p gre} = {-p tcp --dport 1723}

                                                                >iptables -t nat -A POSTROUTING -s 10.1.0.0/24 -j MASQUERADE
                                                                Маскарад не всегда гибкое решение. Ибо гонит траф в дефолтный интерфейс

                                                                Жизненный вариант:
                                                                iptables -t nat -A POSTROUTING -s 10.1.0.0/24 -o eth0 -j SNAT --to xxx.xxx.xxx.xxx
                                                                (тут eth0 — интерфейс в интеренет, а xxx.xxx.xxx.xxx его адрес. Иногда бывает несколько ip на внешнем интерфейсе, тут можно выбрать от какого будут юзеры ходить наружу)

                                                                И еще по моему стоило бы добавить:
                                                                iptables -A FORWARD -i ppp+ -s 10.1.0.0/24 -j ACCEPT
                                                                И если у вас этого не было
                                                                iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
                                                                  0
                                                                  flow-tools рулит
                                                                    0
                                                                    в какойто момент хватает SAMS
                                                                      0
                                                                      если автор собирается продолжить тему (а тема очень актуальна, спасибо)
                                                                      хотелось бы, простого мануала с примерами «как грамотно настроить шейпер»:
                                                                      1. как правильно разделить трафик по приоритетам с помощью iproute2 т. е в первую очередь пускать пакеты с DNS, открывающие и закрывающие tcp соединения, потом http, а затем уже фтп и пиринг
                                                                      2. как настроить шейпер htb (желательно, что б он делил канал динамически в зависимости от подключенных пользователей: т. е. сидит 1 чел. — канал весь его, 2 чел, канал делиться пополам, но при этом не по кол-ву сессий, а по ip, потому что пользователь может поставить на закачку торент и при этом еще серфить)
                                                                      3. как настроить авторизацию и самый простой учет трафика для небольшой сети 5-10 машин
                                                                        0
                                                                        Тоже как-то такую штуку писал, если кому-то интересно можно посмотреть здесь http://sourceforge.net/projects/usimstat/. Все вполне работоспособно, но нет документации пока что :)
                                                                          0
                                                                          Не пойму, почему в account.log ничего не пишется :(
                                                                            0
                                                                            При установке на Ubuntu из родных репозиториев, в account.log пишется следующая строчка:

                                                                            *** invalid %N$ use detected ***

                                                                            Решением проблемы является установка формата вывода вида:

                                                                            accountingformat="%h\t%t\t%p\t%s\t%S\t%d\t%D\t%P\t%b\t\"%i\"\t\"%o\"\t\"%f\"\t%x\n"

                                                                            Но если такой вариант Вас не устраивает и вы хотите установить формат, указанный в статье или свой, то необходимо установить ulog-acctd из deb пакета со страницы Debian Packages.

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

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