Содержание
Данный документ содержит описание системы e-Billing -- программного комплекса для подсчёта стоимости пользовательской активности в компьютерной сети и управления доступом пользователей к предоставленным ресурсам. В дополнение к этому даётся исчерпывающий обзор предметной области и смежного/альтернативного ПО.
Настоящая документация распространяется на условиях GNU Free Documentation License версии 1.1.
Каждый имеет право воспроизводить, распространять и/или вносить в неё изменения в соответствии с условиями этой лицензии.
Данный Документ не содержит Неизменяемых разделов; Данный Документ не содержит текста, помещаемого на первой или последней страницах обложки.
Все исходные тексты программ, разработанные в рамках проекта e-Billing, распространяются на условиях GNU General Public License версии 2 или более поздней, по вашему выбору.
Вы отвечаете за то, чтобы ваша работа с документацией и программным обеспечением e-Billing проходила в соответствии с указанными лицензиями, несмотря на то, что вопреки рекомендациям лицензий их полные тексты могут быть не включены в начало соответствующих файлов.
Изменения и добавления, внесённые авторами e-Billing'a в ходе его разработки в другие программные продукты, распространяются не под лицензией e-Billing'a, а под лицензиями этих продуктов.
Биллинг (от англ. «bill» - счёт, закладная) -- это термин, обозначающий следующий процесс:
В компьютерных сетях биллинг необходим поставщикам (провайдерам) различного рода услуг (сервисов), в частности:
Существующие решения могут быть поделены на два типа:
RADIUS расшифровывается как Remote Authentication in Dial-In User Service. Это протокол, специально разработанный для передачи сведений между программами-сервисами (в терминах RADIUS'a - NAS, т.е. Network Access Server) и системой биллинга.
Узлы освобождаются от хранения информации о пользователях, которая собирается на общем для всей сети компьютере. RADIUS-сервер подобен серверам NIS и LDAP, а также контроллерам домена Сети Microsoft в том отношении, что управление авторизацией становится централизованным, но отличается предъявляемыми требованиями и составом дополнительной информации. Например, LDAP в первую очередь предназначен для обработки запросов на чтение из центральной базы, и в последнюю - на запись, SMB не содержит встроенных средств для передачи сигнала от сервера рабочей станции о необходимости прекращении обслуживания, и т.д.
Имеет смысл ещё раз подчеркнуть, что компьютеры и приложения, являющиеся серверами и сервисами с точки зрения пользователя, сами, в свою очередь, с точки зрения RADIUS'a являются RADIUS-клиентами и управляются RADIUS-сервером, который отвечает за проверку подлинности пользователей (authentication), право выполнения операций в сети (authorization) и проверку количества выделенных/потреблённых ресурсов (accounting).
Наиболее популярными современными серверами RADIUS являются:
Как правило, сервер RADIUS, от которого зависит работа NAS'ов, сам, в свою очередь, зависит от следующих компонентов:
Далее приводятся возможные варианты их реализации на примере GNU RADIUS. Так, информацию о пользователях GNU RADIUS умеет читать из следующих источников:
Варианты хранения статистики:
Для программирования всех расширений, включая запись сложных и/или нестандартных правил биллинга, используется интерпретатор языка Scheme (диалект языка Lisp), называемый GUILE (GNU's Ubiquitous Intelligent Language for Extension). Этот интерпретатор имеет вид программной библиотеки, так чтобы разработчик мог легко подключить её к своему приложению.
Большинство типовых сервисов (Sendmail/Postfix SMTP, Apache HTTP server, Squid/Oops HTTP proxy и т.д.) не поддерживают полноценного взаимодействия с RADIUS-сервером. Под неполноценным взаимодействием здесь подразумевается простая проверка паролей и разрешение на доступ, а под полноценным -- подсчёт количества и стоимости данных, полученных клиентом после того, как разрешение на доступ выдано. По-видимому, либо на такую функциональность нет спроса, либо наличие небольшого спроса удовлетворяется закрытыми и/или частными решениями.
RADIUS оперирует сессиями. Из-за этого он хорошо подходит для управления доступом к коммутируемым линиям или (на прикладном уровне) к сервисам наподобие SMB, SMTP, FTP и Telnet. Однако в нём не существует такого понятия, как пользовательская активность вне сессий. По этой причине RADIUS не годится для учёта трафика через маршрутизатор или обращений к прикладным сервисам типа HTTP.
Каким может быть обходное решение? По-видимому, RADIUS-клиент, ответственный за обслуживание маршрутизатора или прикладного сервиса, должен, получив запрос от пользователя, объявить RADIUS-серверу о начале сессии, но не объявлять о её закрытии ещё некоторое время. В течении этого времени все запросы от данного пользователя, будучи по сути никак не связанными, считаются находящимися в одной сессии. Если к сервису попеременно обращается множество пользователей, прикреплённый к нему RADIUS-клиент должен корректно отличать их друг от друга и с точки зрения RADIUS-сервера держать одновременно открытыми соответствующее количество сессий. Реализация такого решения рискует быть громоздкой.
Менее изящное решение для сетевого уровня связано с использованием VPN-туннелей: чтобы получить доступ к сервису, пользователь должен установить с сервером VPN-соединение. VPN-соединение в этой ситуации используется ни для манипуляций с IP-адресами, ни для шифрации содержимого, хотя и то, и другое не запрещено. Причина, по которой оно требуется, состоит в том, что пользовательская активность принимает вид сессий, а значит, снова укладывается в понятия RADIUS'a.
Последнее решение демонстрируется в e-Billing'e, причём в качестве VPN-протокола выбран PPTP (Point-to-Point Tunneling Protocol). Выбор обусловлен тем, что его реализация включена в состав Windows, начиная с NT4 и '98 (для Win95 -- в отдельном пакете MS DUN, начиная с версии 1.3). Соответственно, на Линукс-сервере инсталлируется пакет pptpd.
Portslave -- это утилита, совмещающая в себе свойства драйвера терминала и проверки пароля.
Как драйвер терминала, Portslave может запускаться из /etc/inittab для работы с последовательным портом ttySn. В этом варианте он выступает как альтернатива mgetty. Во-вторых, его можно запустить из любого приложения, которое установит сетевое соединение и перенаправит его на stdin/stdout, как это делают inetd и vpnd.
После запуска (во втором варианте) или установки соединения (в первом) Portslave узнаёт у удалённого пользователя имя/пароль, связывается с RADIUS-сервером и передаёт ему введённые данные для проверки. На этом этапе Portslave заменяет стандартную утилиту login.
После успешной авторизации Portslave запускает PPP-демон. ???
Если RADIUS является сетевой системой авторизации, то есть позволяет с одного компьютера управлять процессом подключения пользователей к сервисам на других компьютерах, то PAM (Pluggable Authentication Modules, Подключаемые Модули Аутентификации) централизует управление подключениями к разным сервисам в пределах одного-единственного компьютера. Сервисы, обязанные заниматься авторизацией пользователей (/bin/login, удалённый терминал, PPP, FTP и т.д.), будучи соответствующим образом переписаны, теперь вызывают функции библиотеки PAM, а та, в свою очередь, запускает модули, указанные в составленной для сервиса политике (policy).
Такая схема не только разгружает исходный текст сервиса, поскольку вызов PAM-функций проще самостоятельных проверок, но и расширяет возможности администратора:
Как и многие другие удачные идеи, PAM был разработан фирмой Sun Microsystems для операционной системы Solaris. По документации на интерфейс администратора, интерфейс разработчика PAM-зависимых сервисов и интерфейс разработчика PAM-модулей фирма RedHat разработала реализацию PAM для Линукса, полностью совместимую для разработчиков сервисов с вариантом от Sun. Пользовательский интерфейс для администраторов и программный интерфейс для разработчиков модулей совместимы не стопроцентно, но в значительной степени.
В итоге средства PAM на сегодняшний день включены во все основные дистрибутивы Линукса, а поддержка PAM реализована в подавляющем большинстве сервисных приложений.
Когда говорят о такой поддержке, имеют в виду (не всегда внятно) одно из двух:
Первое позволяет подключать сервис к уже существующей системе авторизации, а второе -- делать сервис ядром новой системы авторизации в существующей вычислительной среде.
Для многих сетевых протоколов имеется и то, и другое. Например, сервер Samba обращается к PAM'у, если исходные тексты сконфигурированы с опцией --with-pam, в smb.conf разрешён приём из сети незашифрованных паролей (опция encrypt passwords = no) и включён флаг obey pam restrictions = yes.
В то же время Samba содержит несколько PAM-модулей для использования другими сервисами: тривиальный pam_smbpass (ключ конфигурации исходников --with-pam_smbpass) проверяет пароли по локальному файлу smbpasswd, и сложный пакет WinBind, PAM-модуль которого авторизует пользователей, обращаясь через сеть к серверу Samba или контроллеру домена под управлением Windows.
Точно таким же образом обстоит дело и с протоколом RADIUS: все современные RADIUS-серверы способны проверять пользователей через PAM, а для PAM существует несколько модулей, способных обращаться для проверки к удалённому RADIUS-серверу. Один из них, pam_radius, имеет минимальные возможности и входит (входил?) в состав базовых исходных текстов PAM от RedHat, а второй, более совершенный, написан в рамках проекта FreeRADIUS и называется pam_radius_auth.
Для сетевой авторизации, в том числе через PAM, существуют два важных препятствия.
Во-первых, некоторые сетевые протоколы предусматривают, что пароль от клиента сервису передаётся в уже зашифрованном виде, без возможности точной обратной дешифрации. Если этот шифр отличается от того, который использует расположенная под PAM'ом система авторизации, использовать PAM под данным сервисом нельзя.
Описанная проблема наиболее актуальна для Самбы: чтобы Самба могла использовать PAM, на рабочих станциях Windows требуется отключать шифрование передаваемых паролей через MD4, чтобы сервис получал их в виде plain text.
Во-вторых, для того, чтобы успешно авторизованный пользователь мог обращаться к файловым и прочим ресурсам на компьютере, на который он получил доступ через сервис, операционная система должна назначить ему персональный идентификатор UID и идентификатор группы GID. Требуется одно из двух.
Либо значения UID/GID придётся хранить на сервере авторизации и читать их на сервисный компьютер, при условии, что сетевой протокол, через который PAM-модуль связывается с системой авторизации, вообще допускает подобный вариант (SMB-протокол не допускает, хотя последние версии WinBind способны читать данные, которые не "влезают" в формат SMB-сообщений, напрямую с LDAP-сервера, на котором хранятся пользовательские записи домена, минуя при этом контроллер домена, также обращающийся к записям через LDAP -- образчик параноидальной находчивости в параноидальном мире современного сетевого ПО). В таком случае необходимо следить, чтобы глобальные значения не пересеклись со значениями в учётных записями локальных пользователей. Этот вариант допустим и рекомендуется, в частности, для протокола LDAP.
В худшем же случае сидящему под PAM'ом модулю потребуется динамически выделять новым сетевым пользователям локальные UID/GID из специально отведённого (чтобы избежать конфликтов с обычными способами регистрации) диапазона и запоминать соответствия между ними для дальнейшего использования. Так поступает WinBind -- PAM-модуль из состава Самбы. Потеря таблицы соответствий равносильна потере /etc/passwd.
Поддержка RADIUS'a (и его менее совершенного предшественника, называемого TACACS) в маршрутизаторах и коммутаторах Cisco реализована полностью, потому что Cisco не только использует их, но и является основным разработчиком.
В частности, для протокола TACACS Cisco свободно распространяла исходные тексты сервера. Для протокола RADIUS такой необходимости нет, потому что существует достаточное число качественных реализаций от других разработчиков.
В документации FreeRADIUS имеется файл ./doc/cisco, в котором приведены сведения по настройке Cisco IOS для подключения к RADIUS-серверу. Там же имеется пока непроверенная нами ссылка на фирменную документацию.
На данный момент дистрибутив e-Billing'a не содержит никаких средств для поддержки Cisco, равно как и их описаний. Это вызвано тем, что для организации сервера удалённого доступа мы используем другое оборудование: контроллеры фирмы Cyclade для T1 и ISDN, вставляемые в стандартный PCI-слот непосредственно на PC-совместимом компьютере, на котором работают Линукс, биллинговая система и всё остальное ПО.
Не вызывает сомнений, что поддержка Cisco будет добавлена в e-Billing сразу же, как только у кого-то возникнет потребность в подобном симбиозе.
Как уже говорилось выше, основными пользователями биллинговых систем являются провайдеры коммутируемых или постоянных подключений к Интернету. Вот основные отличия между ними:
Коммутируемый доступ | Выделенный доступ | |
---|---|---|
Сервис, предоставляющий доступ и осуществляющий учёт | PPP-демон | Пакетный фильтр |
Единица авторизации | Сеанс | IP-пакеты |
Единица измерения | Время в секундах | Трафик в байтах |
Данные для авторизации клиента | Имя/пароль | IP-адрес (*) |
Отключение клиента | w | grep $USER_ID && kill $PPP_ID; usermod -L $USER_ID | В таблицу пакетной фильтрации добавляется блокирующее правило для данного IP-адреса |
И пакетные фильтры, и прикладные сервисы в состоянии выдавать более-менее подробную статистику своей работы в текстовом виде: первые, как правило, по запросу, вторые - постоянно, причём, как правило, через syslog. От простой системы биллинга требуется произвести синтаксический разбор выдаваемого текста (для этого идеально подходит Perl или libRegExp), извлечь из него нужные данные, привести к реляционному виду и поместить в базу на SQL-сервере.
Примером такого «решения» может служить «Простая система учёта трафика», написанная Вадимом Фёдоровым.
Расчёт стоимости также производится несложной процедурой на Perl'e. Доступ к системе осуществляется через Веб-интерфейс. Дополнительно «ваяется» генерация отчётов в MS Office или Delphi. Отключение пользователей в случае перерасхода производится вручную или (по мере накопления опыта отключений) чем-то, написанным на колене.
Следует заметить, что подобные «наколенные» разработки порой вырастают в нечто вполне зрелое, документированное, удобное и коммерчески успешное.
Сетевая служба Ident (RFC 1413) работает по следующей схеме:
Его применение оправдано в следующей ситуации:
В настоящее время данный сервис практически никем не использутся, хотя его поддержка всё ещё присутствует в таких программах, как xinetd.
Если соблюдаются следующие условия:
... то неплохой потенциальной альтернативой Ident'y является опрос сервера авторизации: «назови мне имя пользователя, который зашёл в сеть с компьютера, имеющего данный IP-адрес».
При поиске в Интернете существующих свободных решений оказалось, что все они написаны на территории бывшего СССР. Из этого следуют два вывода:
???
Характеристика | LANBilling | NetAMS | NetUP UserTrafManager | KravNet Admin | NIBS | OSt | radacct |
---|---|---|---|---|---|---|---|
Предмет учёта | |||||||
Решение писать собственную систему учёта, кроме естественного для программиста писать собственные программы вместо настройки чужих, не имело под собой почти никаких причин.
Ни одна из существующих систем биллинга не позволяет назначать стоимость нескольких услуг в нескольких неконвертируемых друг в друга денежных единицах. Рассмотрим пример, когда такая возможность необходима настолько, что оправдывает разработку наподобие нашей.
Имеется учебный центр с выходом в Интернет и модемным пулом. Учащиеся имеют возможность звонить на сервер за файлами, заданиям, почтой и т.д. Стоимость телефонного доступа во внутреннюю сеть либо невелика, либо заложена в общую стоимость обучения. Однако предоставляемое время доступа в первом варианте, скорее всего, фиксировано, а во втором - фиксировано обязательно.
Кроме того, у слушателей существует возможность скачивать из Интернета определённый объём данных. Таким образом, у каждого из них имеются две квоты, но запрещается разменивать одно в другое, то есть нельзя, например, провести на линии 50 часов вместо 100 положенных, но выкачать из Интернета 11 мегабайт вместо разрешённых 10.
Описанной проблемы не существует для Интернет-провайдера -- провайдер считает только время на линии. Её не существует и для Интернет-клуба -- в клубе время работы и Интернет-трафик складываются в общий счёт. Многие домашние сети не только не взимают оплату за внутренний трафик, но даже не считают его. Однако учебный центр (такой, как УЦВТ), с одной стороны, должен предоставлять слушателям гарантированный доступ из дома к собственным базам, сайтам и т.д. (возможно, бесплатно, но с ограничением по времени), а с другой -- давать им по этой же линии выходить в Интернет, но уже за дополнительную плату.
Решением служит введение нескольких денежных единиц, одна из которых служит для «оплаты» времени на линии, а вторая -- для оплаты Интернет-трафика. Если и та, и другая услуги являются платными (оставаясь при этом не размениваемыми одна в другую), то вычисление суммарной стоимости должно производиться не в ядре биллинговой системы, ответственном за управление сервисами (NAS'ами), а дальше, в системе расчёта платежей. Ядро же должно манипулировать сервисами, расчитывая стоимость их использования независимо, не смешивая в общий котёл.
Возможный обходной вариант без программирования мог бы состоять в инсталляции нескольких биллинговых систем (по количеству ортогональных денежных единиц) с общей базой пользователей, например, в LDAP. При этом каждая система будет отвечать за обслуживание своего набора сервисов: первая -- за PPP на телефонном входе во внутреннюю сеть, вторая -- за пакетный фильтр на Интернет-шлюзе и т.д.
Для расчёта стоимости в e-Billing'e служит оригинальная функция, подключаемая к системе в двух точках:
Как ни удивительно это может показаться, RADIUS-протокол не предусматривает разрыва связи с клиентом в случае исчерпания кредита. В начале и конце сессии NAS посылает RADIUS-серверу соответствующие уведомления, но между этими двумя моментами они никак не взаимодействуют: NAS не сообщает, что клиент ещё работает, а RADIUS-сервер не перерасчитывает кредит и не приказывает оборвать связь, если кредит закончился. Такой механизм в протоколе RADIUS, насколько мы сумели понять, отсутствует по определению.
Вне всяких сомнений, многие Интернет-провайдеры как-то обходят это ограничение, потому что их узлы удалённого доступа умеют разрывать связь с точностью до минуты. Остальные же, наверное, предпочитают обслуживать клиента в кредит, либо дожидаясь следующего платежа, либо производя отключение вручную (естественно, не с точностью до минуты).
Для управления NAS'ами в течение сессии в рамках проекта e-Billing разработана утилита-демон acctd (Accounting Daemon). Её работа происходит по следующей схеме:
acctd управляет сервисами через внешние модули собственного формата. Пока существует два модуля.
Первый из них называется by_command. Этот модуль читает команды управления из конфигурационного файла и передаёт их системному интерпретатору команд. Например, конфигурационный файл для управления Portslave-зависимым соединением на локальном компьютере выглядит примерно следующим образом:
<?xml version = "1.0"?> <options> <local-portslave temp="/sbin/route -n | /bin/fgrep %{Client-IP} | /usr/bin/awk {'print $8'}" stat_cmd="/bin/fgrep %{Temp} /proc/net/dev | /usr/bin/awk {'print $2 " $10'}" kill_cmd="kill -s TERM `/bin/cat /var/run/%{Temp}.pid`" /> </options>
Модульная конструкция acctd и возможность использовать произвольные системные команды через модуль by_command в переспективе позволят легко добавлять в acctd поддержку абсолютно любых устройств, включая сервисы на других компьютерах, специализированные маршрутизаторы и т.д.
Второй модуль служит для управления сервисом MPD (Multi-link PPP daemon), входящим в состав FreeBSD. В отличие от классического PPP-демона, который запускается заново для проведения каждой сессии, MPD работает постоянно и обслуживает все подключения, будучи запущенным в единственном экземпляре. Для управления им существует т.н. MPD-консоль, т.е. вспомогательная утилита, работающая как в интерактивном, так и в пакетном режиме (clear Unix way ;-). К MPD-консоли можно подключаться через TCP-сокет.
Удалённо опрашивать и управлять MPD через MPD-консоль значительно удобнее, чем PPPD, так как опрос нескольких копий PPPD на удалённом узле через SSH потребовал бы либо установки нескольких SSH-соединений, либо громоздкой оптимизации. В случае с MPD сессия между агентом и консолью создаётся ровно один раз, когда наступает момент вызова агентов acctd-демоном.
Примечание: внутренняя структура acctd такова, что агент определённого типа создаётся в единственном экземпляре независимо от того, сколько узлов и сессий он обслуживает. В частности, один экземпляр (instance) mpd-агента обслуживает все MPD на всех узлах. То есть, внутри объекта типа «агент» хранится пул объектов типа «сессия»
У MPD-консоли существует два недостатка. Во-первых, она ведёт себя нестабильно при длительной работе. По этой причине MPD-агент разрывает соединение с консолью в конце каждого цикла опроса и открывает его заново в начале следующего. Во-вторых, сетевая сессия с консолью не шифруется, то есть и данные, и даже пароль передаются через сеть открытым текстом! Решить эту проблему предполагается с помощью утилиты stunnel.
Приведённый рисунок вряд ли нуждается в дополнительных комментариях:
Направления на стрелках указывают, в какую сторону передаются данные.
Разработка и эксплуатация системы происходит под операционной системой ALT Linux Master (в данный момент используется версия 2.2 «Orange»). Соответственно, форматом распространения является RPM для i586.
Все вспомогательные пакеты (MySQL и т.д.) были пересобраны, причём далее в описании каждого из них указывается, был ли Spec-файл для него оставлен без изменений, изменён или написан с нуля.
FreeRADIUS выбран по следующим причинам:
Отличия нашей нынешней сборки от входящей в состав ALM 2.2 заключаются в следующем:
Вероятнее всего, первое изменение со временем окажется на freeradius.org, а второе -- в Сизифусе.
Выше рассказывалось, какая роль отводится PPTP-протоколу в общей схеме. Чтобы ей соответствовать, стандартный ALT'овский пакет пересобран с опцией --with-pppd-ip-alloc. Собранный с ней PPTP-демон не назначает новому сетевому соединению IP-адрес, а перекладывает эту обязанность на запускаемый им сервис (как правило, это сервис PPP). В свою очередь, запускаемый в нашем случае вместо PPP portslave назначит IP-адрес, значение которого ему сообщит RADIUS-сервер. Таким образом, у Accounting-демона и прочих компонентов биллинговой системы появляется возможность по IP-адресу отличать соединения, требующие или не требующие их контроля.
Кроме того, из зависимостей для сборки убраны autoconf и automake, так как исходные тексты принято поставлять с файлами Makefile и Configure, уже сгенерированными этими утилитами.
Небольшой дополнительный пакет pptpd-portslave содержит сценарий для запуска portslave для соединений, устанавливаемых PPTP-сервисом, и образцы изменений, которые надо внести для его поддержки в файлы конфигурации PPTPd.
Когда пользователь обращается к своей учётной записи и статистике через Web-браузер, Web-сервер Apache должен иметь возможность проверить пользовательские имя и пароль в общей учётной базе. Для этого к Apache подключается дополнительный внешний модуль mod_auth_radius, который производит их проверку на указанном RADIUS-сервере. Для того, чтобы этот модуль вызывался при загрузке пользовательского интерфейса, в каталоге на сервере помещается файл .htaccess с достаточно красноречивым содержимым:
AuthName "E-Billing user authentication" AuthType Basic AuthAuthoritative off AuthRadiusAuthoritative on require valid-user
Примечание: сам пользовательский интерфейс и .htaccess находятся в пакете ebilling-user.
Spec-файл для mod_auth_radius написан с нуля. Конфигурационный файл для Apache, включённый в пакет, после установки требует исправления вручную.
Не исключено, что в дальнейшем вместо непосредственной авторизации Apache на RADIUS-сервере окажется удобнее включить в Apache стандартную авторизацию через PAM и настроить pam_radius_auth.
Единственное отличие нашей сборки MySQL от соответствующего пакета ALT Linux состоит в использовании InnoDB -- драйвера, дающего MySQL поддержку транзакций (и не только). Сейчас нами используется версия 3.23. Поскольку в версии 4.0 поддержка InnoDB включена по умолчанию, необходимость собирать собственный пакет отпадёт. Однако даже сейчас вы можете использовать вместо него пакет от ALT Linux'a, пересобрав его следующей командой:
rpmbuild --rebuild --with=innodb MySQL-3.23*.src.rpm
Как минимум один компонент e-Billing'a в настоящий момент жёстко привязан к связке MySQL/InnoDB и не позволяет заменить её на другой SQL-сервер. Это сценарий configs/tables.sql, создающий структуру таблиц, используемых для сохранения текущей отчётности.
Библиотеки не нуждаются в настройке, однако для каждой из них здесь приводится краткое описание. Если у вас настроены на наш репозитарий утилиты APT или YUM, а описания не интересны, вы можете полностью пропустить этот раздел.
Все библиотеки -- как входящие в состав e-Billing'a, так и используемые его частями прямо или косвенно -- строятся и используются в режиме динамической компоновки.
Для доступа к SQL-базам используется библиотека dbstep. dbstep служит промежуточным слоем между пользовательским приложением на Си++ и клиентской библиотекой для связи с SQL-сервером конкретной марки (сейчас поддерживаются библиотеки MySQL и PostgreSQL). Достоинств у такой схемы два:
Для сборки требуются библиотеки mysqlclient (в ALT Linux Master должен быть установлен виртуальный пакет MySQL-devel, предоставляемый физическим libMySQL-devel с динамической компоновкой или libMySQL-devel-static для статической) и pq3 (RPM-пакеты postgresql-devel и libecpg3/libpq3(-devel) ).
Spec-файл написан с нуля.
Boost -- это библиотека для Си++, назначение которой заключается в трёх вещах.
Во-первых, Boost содержит наборы классов для областей, которые не охвачены стандартной библиотекой Си++:
Помимо этого, Boost содержит несколько десятков мелких модулей, посвящённых самым разным вещам: преобразованию дат, smart-указателям, манипуляциям битами и т.д.
Первоначально проект Boost был основан отдельными членами Комитета по стандартизации Си++ как полигон для обкатки новых функций. С увеличением числа разработчиков Boost стал развиваться независимо, однако избегая дублирования или конфликтов со стандартной библиотекой.
Во-вторых, Boost восполняет некоторые пробелы, имеющиеся в стандартной библиотеке. Например, встроенные массивы Си++ не обладают полезными свойствами библиотечных контейнеров, в частности, не поддерживают перебора (enumeration). В то же время библиотечные контейнеры несут в себе не всегда нужную, но дорогую универсальность, например, способность увеличивать размер за счёт размещения данных в динамической памяти. В Boost'e предложено компромиссное решение -- контейнер фиксированного размера, который удобнее, чем встроенный массив, и быстрее, чем контейнер из стандартной библиотеки.
В-третьих, исходные тексты Boost, сопровождающая их документация и общий стиль управления проектом являются прекрасным пособием для тех, кто желает усовершенствовать свои навыки программирования на Си++.
Сборка для ALT Linux Master 2.2 разбита на 10 пакетов:
В настоящий момент e-Billing использует boost-regex и boost-lexical_cast из состава ALM в коде агентов и RLM.
Библиотека libDaemon содержит набор функций-оболочек, облегчающих разработчику приложения следующие задачи:
Использование libDaemon облегчает не только разработку демона для одной конкретной платформы, но и (потенциально) перенос на другие плаформы, так как все перечисленные вопросы в разных Юникс-совместимых ОС решены по-разному. В настоящий момент libDaemon поддерживает только Линукс. Первая и единственная пока версия с номером 0.1 вышла в июле 2003 года.
Spec-файл написан с нуля.
У классического Си-интерфейса к функциям TCP/IP есть два недостатка:
/* Initiate incoming connections at server side */ int s; struct sockaddr_in my_addr; if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { /* 0 = IP proto */ perror("socket"); return -1; } memset(<my_addr, 0, sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_addr.s_addr = INADDR_ANY; my_addr.sin_port = htons(port); if (bind(s, (struct sockaddr *)<my_addr, sizeof(my_addr)) < 0) { perror("bind"); return -1; } if (listen(s, MAX_INCOMING_CONNECTIONS) < 0) { perror("listen"); return -1; }
По этим причинам все высокоуровневые языки предоставляют набор классов-обёрток, упрощающих типовые задачи до одной-двух строк. Например, в Яве аналогичный код будет выглядеть так:
ServerSocket listeningSocket = new ServerSocket(port);
В состав стандартной библиотеки Си++ средства для работы с сетью не входят, а библиотеки сторонних разработчиков получить должного распространения не сумели. Тем не менее, libNetxx является попыткой предоставить программисту на Си++ компактный, удобный и универсальный интерфейс.
Предоставляется только поддержка системных вызовов, без прикладных протоколов типа SMTP или HTTP, но с заявленной кроссплаформенностью, поддержкой SSL/TLS и интеграцией в стандартный механизм потоков ввода-вывода Си++.
e-Billing использует libNetxx только для связи mpd-агента с mpd-консолью, так что, возможно, вместо привлечения дополнительной библиотеки всё же имело смысл вручную написать священную последовательность классических вызовов установки связи на Си.
Spec-файл написан с нуля.
Xerces представляет из себя XML-парсер, то есть библиотеку, которая отвечает за чтение XML-файлов, их синтаксический разбор в соответствии с заданной схемой и представление в одном из двух видов: в виде дерева классов (DOM-парсер), либо вызывая подпрограммы, которые главный программный модуль ассоциирует с тегами XML-файла (SAX-парсер).
Существует две реализации Xerces: базовая, написанная на Яве, и повторяющая её реализация на/для Си++. Очевидно, что e-Billing использует вторую.
В XML-виде e-Billing хранит все настройки в каталоге /etc/ebilling.
Имевшийся spec-файл изменён в соответствии с соглашениями ALT Linux Team, кроме того, из devel-пакета вынесены примеры.
Поскольку Веб-страница представляет из себя совокупность данных (content), внешнего вида (presentation) и поведения (application logic), её составлением могут быть заняты до трёх категорий разработчиков: автор, дизайнер (template designer) и программист. Прикладываются значительные усилия, чтобы сделать их труд максимально независимым друг от друга.
Существуют ситуации, когда отделить оформление от вычислений довольно трудно. Например, пусть следует прочесть массив строк из текстового файла и поместить их в блок <SELECT>...</SELECT>, раскрасив разными цветами чётные и нечётные строки. Если использовать для решения этой задачи такой язык, как PHP, конструкции HTML и PHP окажутся неразрывно переплетены. Для работы с ними либо дизайнеру придётся стать программистом, либо программисту - дизайнером.
Следовательно, возникает необходимость в инструменте, который позволит (а) вынести из HTML-шаблона описываемую программистом логику генерации HTML-фрагментов в отдельный файл, (б) многократно использовать готовые логические определения, а также (в) создавать свои собственные. Синтаксис вызываемых дизайнером определений должен быть проще, чем синтаксис универсального языка программирования.
Программу, которая обрабатывает текстовый поток, отыскивая в нём имена определений и заменяя их на тела определений, называют макропроцессор, а определения, соответственно, макросами. Из программ данного класса наиболее широко употребляются встроенный препроцессор языка Си и универсальный макропроцессор m4.
Smarty является макропроцессором для HTML и сам, в свою очередь, написан на PHP. Пользователь по-прежнему видит в Web-браузере страницы с расширением .php, при обращении к которым Web-сервер динамически генерирует HTML, но теперь php-файлы выглядят примерно так:
// This is application logic part, created by developer include('Smarty.class.php'); // create object $smarty = new Smarty; // assign some content // This is content part, created by data producer // Normally this part should be readed from database $smarty->assign('name', 'Amid Vosur'); $smarty->assign('address', 'Guantanamo'); $smarty->assign('id', array(1,2,3,4,5)); $smarty->assign('names', array('ali','akbar','moustafa','maghomed','ahmad')); // display it $smarty->display('index.tpl');
Шаблоны страниц в файлах с расширением .tpl имеют вид подобный следующему:
//This is presentation part, created by designer {include file="header.tpl" title="User Info"} <p>Prisoner Information:<p> Name: {$name|capitalize}<br> Address: {$address|escape}<br> <p>Select best friend:</p> <select name=friend> {html_options values=$id output=$names selected="5"} </select> {include file="footer.tpl"}
Таким образом, дизайнер (а) практически полностью избавляется от программирования, (б) получает в своё распоряжение богатую библиотеку готовых макросов (в примере выше их используется три - include, html_options и $ для подстановки значения переменной), а также (в) потенциальную возможность поручать разработчику создание специализированных макросов под собственные нужды.
Ниже приведён список вводной информации по Smarty:
Практически для всех языков программирования, используемых для генерации Веб-страниц, дополнительно существуют написанные на них макропроцессоры, призванные упростить жизнь дизайнеру-составителю шаблонов. Примером может служить макропроцессор Velocity, написанный на языке Java.
Сейчас файлов настройки в каталоге /etc/ebilling три:
Все три настроечных conf-файла имеют XML-формат. Каждый из них (кроме agents.conf, который имеет произвольные имена тэгов для описания агентов by_command) сопровождается обязательным файлом с таким же именем и расширением .xsd, в котором хранится описание структуры первого файла, записанное на языке XML Scheme (соответственно, xsd расшифровывается как XML Scheme Definition). Второй файл нужен программе для синтаксического разбора первого. Примеры конфигураций находятся в файлах .conf.sample, которые сейчас устанавливаются в каталог с документацией.
В настоящий момент часть настроек из conf-файлов дублируется в SQL-таблицах. Зачем это сделано? Казалось бы, скорость доступа и компактность хранения, которые предоставляет хранение в СУБД, не дают преимуществ для работы с настройками, потому что настройки имеют небольшой размер и редко пишутся-читаются. Кроме того, текстовый файл легче редактировать вручную, чем таблицу в базе данных.
С другой стороны, система должна предоставлять пользователям возможность работать с настройками через Веб-интерфейс. В этом случае СУБД способна предоставить более высокую степень защиты и более гибкие права доступа к данным, чем файловая система без использования дополнительных инструментов (jail, RSBAC и т.д.).
По этой причине, начиная с одной из следующих версий, из сonf-файлов останется только server.conf, так как в нём находятся сведения, необходимые для подключения к СУБД. Сейчас acctd и RLM читают настройки как из файлов, так и из БД, а Web-компоненты -- только из БД.
<?xml version = "1.0"?> <options> <db dbtype = "тип_СУБД" <!-- MySQL, PostgreSQL, FireBird или SQLite --> dbname = "имя_БД" host = "IP-адрес_или_DNS-имя_сервера_БД" user = "имя_пользователя_для_подключения_к_серверу_БД" password = "пароль_пользователя" /> <ip_pool ip_2part = "A.B" /> <!-- начальная часть назначаемых клиентам IP-адресов --> <policies file = "поный_путь_файла_с_описаниями_пользовательских_политик" xsd = "полный_путь_файла_со_схемой_парсинга" /> <radius_auth retries = "количество_попыток_повтора" timeout = "вещественное_число_секунд_между_попытками" /> <acctd check_interval = "целое_число_секунд-интервал_опроса_NASов_accounting-демоном" agents_file = "полный_путь_файла_с_описаниями_агентов_управления_NASами" agent_modules_path = "полный_путь_каталога_с_агентами" /> <hostname_resolver cache_mode = "режим_кэширования_DNS_имён_в_RLM" /> <!-- "cache" или "" --> </options>
Пример:
<?xml version = "1.0"?» <options> <db dbtype = "MySQL" dbname = "ebilling" host = "127.0.0.1" user = "ebilling" password = "some_very_secure_password" /> <ip_pool ip_2part = "10.30" /> <policies file = "/etc/ebilling/policies.conf" xsd = "/etc/ebilling/policies.xsd" /> <radius_auth retries = "5" timeout = "1.0" /> <acctd check_interval = "300" agents_file = "/etc/ebilling/agents.conf" agent_modules_path = "/usr/lib/ebilling-acctd" /> <hostname_resolver cache_mode = "cache" /> </options>
RADIUS-протокол предусматривает, что при желании администратора IP-адрес, назначаемый NAS'ом подключившемуся клиенту, не генерируется NAS'ом самостоятельно, а сообщается ему RADIUS-сервером, который, в свою очередь, может получать его от RLM-модуля. RLM из состава e-Billing формирует IP-адрес следующим образом:
Такая схема предоставляет следующие возможности:
<?xml version = "1.0"?> <options> <!-- Список единиц стоимости --> <money name="название"> <!-- Список образующих единицу стоимости денежных единиц --> <currency name="имя-денежной-единицы" count="вещественное-количество-денежных-единиц-в-единице-стоимости" /> ... </money> <!-- Список тарифов на услуги --> <tariff name="название-тарифа" out_traffic="единица-стоимости-трафика-от-NAS-к-клиенту" in_traffic="единица-стоимости-трафика-от-клиента-к-NAS" time="единица-стоимости-времени" /> <!-- Список тарифных планов --> <policy name="имя-тарифного-плана" comment="выразительный-комментарий" simultaneous_connections="макс.количество" daily="единица-стоимости-взымаемая-ежедневно" profit_interval="период-начисления-кредита-в-сутках" profit="единица-стоимости-содержащая-размер-кредита" profit_mode="что-делать-с-неистраченным-кредитом"> <!-- значения: add=добавлять-к-старому, replace=обнулять-старый-перед-добавлением, none=не-начислять-кредит --> <!-- Список временных интервалов --> <interval begin_day="день-недели" begin_hour="час" end_day="день-недели" end_hour="час" name="all_week"> <!-- Список типов устройств --> <nas_type name="название-типа" tariff="название-применяемого-тарифа" ppp_filter="значение-атрибута-Framed-Filter-ID-отправляемое-NAS'у" /> </interval> </policy> </options>
Пример:
<?xml version = "1.0"?> <options> <money name="none"> <currency name="USD" count="0" /> </money> <money name="deny" /> <money name="rich"> <currency name="USD" count="1" /> </money> <money name="my_daily"> <currency name="USD" count="0.6666" /> </money> <money name="monthly_limit"> <!-- Way to implement monthly, daily, weekly... any limits --> <currency name="USD" count="40" /> </money> <tariff name="rich_tariff" out_traffic="none" in_traffic="none" time="rich" /> <policy name="my_policy" comment="Full access 7x24 - too rich" simultaneous_connections="2" daily="my_daily" profit_interval="30" profit="monthly_limit" profit_mode="replace"> <!-- values: add, replace, none --> <interval begin_day="0" begin_hour="0" end_day="6" end_hour="23" name="all_week"> <nas_type name="VPN" tariff="rich_tariff" ppp_filter="strict" /> <nas_type name="Cisco" tariff="rich_tariff" /> </interval> </policy> </options>
<?xml version = "1.0"?> <options> <!-- Данные для агента by_command --> <произвольно-выразительное-имя temp="команда_выполняемая_перед_командами_stat_и_kill" stat_cmd="команда_получения_данных_от_NASа" kill_cmd="команда_принудительного_завершения_работы_NASa" /> <!-- ... ещё данные для агентов by_command --> <!-- Данные для агента mpd --> <mpd host="IP-адрес-или-DNS-имя" >!-- это поле необязательно и по умолчанию равно NAS-IP --> port="номер-IP-порта" password="пароль-для-подключения-к-MPD-консоли" timeout="таймаут-в-секундах" /> <!-- ... ещё данные для агентов mpd --> </options>
Пример:
<?xml version = "1.0"?> <options> <local-portslave <-- по IP-адресу клиента узнаём имя логического сетевого устройства, через которое клиент подключён, и сохраняем имя устройства в %{Temp} --> temp="/sbin/route -n | /bin/fgrep %{Client-IP} | /usr/bin/awk {'print $8'}" <-- узнаём количество байт, принятых и переданных через сетевое устройство --> stat_cmd="/bin/fgrep %{Temp} /proc/net/dev | /usr/bin/awk {'print $2 " $10'}" <-- уничтожаем демона, обеспечивающего работу сетевого устройства --> kill_cmd="kill -s TERM `/bin/cat /var/run/%{Temp}.pid`" /> <mpd host="10.10.1.1" port="7992" password="mynameisebill" timeout="1" /> </options>
В agents.conf допустимо использование следующих переменных в форме %{имя_переменной}:
Состав перемененных планируется расширять по мере необходимости. В данный момент в него в основном включены сведения, которые e-Billing использует при генерации статистики.
При инсталляции e-Billing'a в каталог с настройками помещаются два сценария: интерактивный для bash'a, который создаёт базу данных MySQL, и сценарий для SQL-консоли, который создаёт таблицы. Второй сценарий вызывается из первого, поэтому вызывать его вручную не нужно. Кроме того, шелл-сценарий создаёт server.conf.
В приведённом ниже списке термином «номер» для краткости обозначаются автоинкрементные счётчики, служащие по совместительству первичными ключами и используемые для связей между таблицами.
Текущий состав таблиц:
Все таблицы, кроме Sessions и IP_Pool, являются настроечными. Sessions хранит текущее состояние и архивную статистику. IP_Pool также хранит текущее состояние и, в принципе, могла бы быть поглощена Sessions, но оставлена отдельной для повышения быстродействия.
С самого начала было поставлено условие, что требуется независимо считать доступ во внутреннюю сеть (через PPP-сервис) и в Интернет, т.е. требуются неконвертируемые деньги за непересекающиеся услуги. Доступ в Интернет планировалось разрешать не на системном уровне (через NAT/фильтр/...), а только на прикладном -- через Squid и Postfix.
О том, что такое RADIUS, мы, естественно, знали, но поскольку его поддержка существовала только в PPP-сервисе, то в тот момент было решено не добавлять её в Squid и Postfix, а составлять полностью новый протокол.
Кроме того, у нас сложилось впечатление, что без серьёзных дополнительных усилий ни один существующий RADIUS-сервер не сможет оперировать несколькими денежными единицами одновременно. Первое впечатление оказалось не вполне верным: добавить поддержку подобной системы расчётов в современный RADIUS-сервер гораздо проще, чем проектировать вокруг неё оригинальное окружение.
Тем не менее, оригинальное ядро биллинговой системы было написано и получило название «Центр принятия решений», сокращённо «ЦПР», по-английски, соответственно, «e-Billing Decree Centre» или «EDC». Это был первый компонент системы.
К моменту начала работ в УЦВТ имелось несколько несвязанных доменов сети Microsoft, плюс сеть Lotus Notes/Domino со своей собственной базой пользователей и системой авторизации. Создавать ещё одну пользовательскую базу не хотелось, поэтому ЦПР должен был уметь извлекать информацию из существующих баз, причём нескольких, причём разнотипных.
За выполнение этой задачи отвечал второй компонент, названный «distauth» (от «distributed authorization», т.е. «распределённая авторизация»). Подразумевалось, что имя пользователя будет включать в себя имя домена (например, vasya@lotus), а distauth на основании настроек оттранслирует имя домена в адрес компьютера, тип протокола и прочие параметры соединения (например, https://www.ucvt.ru/?login).
Более смелые замыслы включали в себя:
Первая версия distauth должна была поддерживать минимальный набор: явную авторизацию по имени/паролю с проверкой через LDAP, SMB, PAM и HTTPS. Так и не была написана.
Третьим компонентом являлись т.н. агенты управления сервисами, каждый из которых отвечал за получение статистики от соответствующего сервиса, передачу её ЦПР и манипулирование сервисом по полученным от ЦПР указаниям.
Были написаны два агента: для управления PPPD и для управления Сквидом.
Управление сервисом могло предусматривать такие вещи, как загрузку таблицы пакетной фильтрации для PPP-интерфейса после того, как на нём авторизовался пользователь (например, пользователю newuser разрешается маршрутизация только к серверу register.ucvt.ru), сброс пакетного фильтра после разъединения, загрузку в Squid назначенной для данной группы таблицы URL, которые должны быть заблокированы или перенаправлены, и т.д.
Агенты связывались с ЦПР через сеть, вызывая его подпрограммы по протоколу SOAP (Simple Objects Access Protocol), для чего использовалась библиотека gSOAP. Для конфигурационных файлов и передачи структур через SOAP уже тогда был выбран формат XML, для синтаксического разбора которого использовался парсер Xerces-C.
Каждый агент должен был быть выполнен в виде PAM-модуля и взаимодействовать с управляемым сервисом исключительно через PAM-интерфейс. Для этого при необходимости предполагалось модифицировать исходные коды сервиса, по мере возможности не выходя за рамки PAM. Такой подход имел два достоинства:
На практике ограничения PAM заставили переносить часть агента в тело сервиса и использовать для связи между частями агента в PAM-модуле и сервисе дополнительные средства (именованные каналы и т.д.).
Четвёртым компонентом должна была стать система сбора статистики, развитие logrider'a, которая должна была делать следующее:
Система статистики была признана не имеющей отношения к биллинговому ПО, так как биллинговое ПО нуждается в оперативном доступе к ограниченному количеству информации, а система статистики в заявленном виде предоставляет информации больше, чем нужно, но с задержкой. Её разработка, к сожалению, так и не была начата.
Последним, пятым компонентом проекта являлась так же ненаписанная консоль оператора на Delphi или PHP+HTML.
Во-первых, по мере развития проекта стало ясно, что разместить всю требуемую от PPP- или Squid-агента функциональность внутри PAM-модуля не удастся. Например, для чтения таблицы фильтрации Веб-адресов Squid использует свой собственный примитивный программный интерфейс, использование в котором PAM-вызовов выглядело бы совершенно противоестественным.
Таким образом, функциональность агента потребовалось бы распределять между PAM-модулем и плагинами к сервису. Для связи сервисов и плагинов потребовалось бы разрабатывать интерфейс, причём некоторые его части в принципе не могут быть реализованы PAM-вызовами. Например, через PAM невозможно послать сигнал сервису со стороны PAM-модуля.
Перечисленные причины сделали невозможным унифицировать интерфейс между агентами и сервисами.
Во-вторых, отказ от собственного интерфейса между ЦПР и агентами был вызван тем, что было добавлено условие управлять dialup-сервисом на базе Cisco. Хотя "кошкой" можно управлять через rsh, а снимать с неё статистику через IP accounting, в тот момент половина разработчиков не участвовала в обсуждении, а оставшаяся половина разработчиков этого не знала. Поскольку в результате RADIUS выглядел единственным возможным средством управления, то место ЦПР занял FreeRADIUS с ebilling-специфичными модулями. Кстати, требование поддерживать Cisco впоследствии было снято под нашим давлением. Наши аргументы были следующими:
Хорошее название, с одной стороны обязано не быть бесцветным, так как на фоне многочисленных и проверенных временем NetAMS'ов, MABill'ов, UTM'ов и NIBS'ов новый продукт неизбежно затеряется. С другой стороны, название не должно быть оторванным от предметной области. Обоим требованиям первоначальный вариант -- е-Вilling -- соответствовал едва ли не идеально. Стоит лишь произнести его вслух, и редкого русскоязычного читателя заставишь заучивать его дважды. Увы, соображения маркетинга заставили нас пожертвовать им в пользу уныло-безликого TBS, что расшифровывается как TBS Billing System. В стороне остался и такой несомненно удачный вариант, как d'Вilling.
Впрочем, не всё ещё потеряно. Работа над системой продолжается!