SER - добавляем авторизацию и работу с базой данных MySQL
Что мы делаем в этом конфигурационном файле ser.cfg:
- Добавляем авторизацию IP с использованием данных, хранящихся в базе данных Mysql.
- Контактную информацию о зарегистрированных клиентах также сохраняем в MySQL.
Теперь, когда Вы протестировали простейшую конфигурацию SIP сервера, мы можем добавить дополнительные функциональные возможности. В этом разделе речь пойдет об авторизации.
В нормальных обстоятельствах, мы должны ограничить использование SIP сервера в пределах тех SIP телефонов (т.е., SIP клиентов), которые нам нужны. Авторизация позволяет нам обслуживать только те SIP телефоны, которым мы назначили пароль и позволили использовать наш SIP сервис.
Для поддержки авторизации, мы нуждаемся в хранилище информации, данные в которой не будут потерянны, когда мы останавливаем или перегружаем SIP сервер. Самое распространенное хранилище - это база данных, а самая популярная из них - MySQL, поскольку она содержится почти во всех дистрибутивах Линукс. Существует поддержка и других баз данных, например, PostgreSQL, но в этом документе мы сосредоточим свое внимание только на MySQL.
Для добавления поддержки MySQL, Вы должны вернуться к исходным кодам сервера и изменить несколько параметров. В главе посвященной поддержке базы данных MySQL описывается, как это сделать, после этого, Вам понадобиться повторно инсталлировать исполняемые файлы сервера. После обновления сервера SER, Вы должны внести изменения в файл ser.cfg, как это показано ниже.
Листинг файла ser.cfg, с поддержкой MySQL.
Приведенный ниже, файл конфигурации SIP прокси сервера, основан на простейшем файле конфигурации SIP маршрутизатора, рассмотренного в разделе: Простейшая конфигурация ser.cfg.
debug=3 fork=no log_stderror=yes listen=192.0.2.13 # ЗДЕСЬ ДОЛЖЕН БЫТЬ ВАШ IP АДРЕС port=5060 children=4 dns=no rev_dns=no fifo="/tmp/ser_fifo" fifo_db_url="mysql://ser:heslo@localhost/ser" # Директива Fifo_db_url включена для подавления предупреждающих сообщений при запуске сервера, # которые появились бы, при добавлении поддержки MySQL. Мы не используем явно значение # параметра fifo_db_url указанного в файле ser.cfg, однако, другие дополнительные инструменты такие, # как утилита serctl использует это значение, для добавления пользователей в базу данных loadmodule "/usr/local/lib/ser/modules/mysql.so" # Включение поддержки базы данных MySQL производиться простым включением модуля mysql.so # в секции loadmodule. Стоит отметить очень важную вещь - это то, что модуль mysql.so должен быть # загружен до загрузки всех остальных модулей. Причина заключается в том, что модуль mysql.so # не содержит зависимостей от других модулей, однако, другие модули, например, uri_db, # содержат зависимости от модуля mysql.so. loadmodule "/usr/local/lib/ser/modules/sl.so" loadmodule "/usr/local/lib/ser/modules/tm.so" loadmodule "/usr/local/lib/ser/modules/rr.so" loadmodule "/usr/local/lib/ser/modules/maxfwd.so" loadmodule "/usr/local/lib/ser/modules/usrloc.so" loadmodule "/usr/local/lib/ser/modules/registrar.so" loadmodule "/usr/local/lib/ser/modules/auth.so" # Модуль auth.so не используется непосредственно в ser.cfg, однако он необходим для включения поддержки # функциональности, связанной с авторизацией. Основная функциональность авторизации пользователей # в данном файле ser.cfg обеспечивается модулями auth.so и auth_db.so. loadmodule "/usr/local/lib/ser/modules/auth_db.so" # Модуль Auth_db.so - непосредственно используется нами в данной конфигурации. # Он взаимодействует с модулем auth.so для выполнения своих функций. loadmodule "/usr/local/lib/ser/modules/uri_db.so" # Модуль Uri_db.so предоставляет нам некоторые функции для авторизации, которые мы будем использовать # в этом файле ser.cfg, а именно - check_to (). modparam("auth_db|uri_db|usrloc", "db_url", "mysql://ser:heslo@localhost/ser") # Модуль Auth_db использует параметр db_url для того, чтобы указать серверу SER, как подключиться # к базе данных MySQL, которая используется для авторизации пользователей. Как можно заметить, # мы одновременно включили имена модулей auth_db, uri_db, и usrloc в одну директиву modparam, # используя символ вертикальной черты. Это сделано для уменьшения числа директив, # однако это вполне согласуется с синтаксисом, используемым для сервера SER modparam("auth_db", "calculate_ha1", 1) # Параметр модуля Auth_db - calculate_ha1 указывает серверу SER, нужно ли использовать зашифрованные пароли # в таблице пользователей базы данных MySQL. Для рабочих систем Вам, скорее всего потребуется # установить этот параметр в ноль, но в нашем примере мы будем использовать незашифрованные пароли, # следовательно, установим значение этого параметра в единицу. modparam("auth_db", "password_column", "password") # В модуле Auth_db, по умолчанию, поле для хранения паролей имеет имя - ha1, однако в нашем случае, # это поле в MySQL базе имеет имя 'password',. Поэтому мы должны сообщить серверу SER, что имя этого # столбца отличается от значения по умолчанию. modparam("usrloc", "db_mode", 2) # Параметр db_mode модуля usrloc мы должны изменить с нулевого значения, которое мы использовали в первой # простейшей конфигурации, на значение 2, в данном примере, для указания серверу SER, что он должен # использовать MySQL для хранения контактной информации пользователей и данных авторизации. modparam("rr", "enable_full_lr", 1) route { # ----------------------------------------------------------------- # Sanity Check Section # Секция проверки параметров. # ----------------------------------------------------------------- if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); break; }; if (msg:len > max_len) { sl_send_reply("513", "Message Overflow"); break; }; # ----------------------------------------------------------------- # Record Route Section # Секция записи маршрутов. # ----------------------------------------------------------------- if (method!="REGISTER") { record_route(); }; # ----------------------------------------------------------------- # Loose Route Section # Секция свободной маршрутизации. # ----------------------------------------------------------------- if (loose_route()) { route(1); break; }; # ----------------------------------------------------------------- # Call Type Processing Section # Секция, обрабатывающая различные типы вызовов. # ----------------------------------------------------------------- if (uri!=myself) { route(1); break; }; if (method=="ACK") { route(1); break; } if (method=="INVITE") { #Теперь для обработки сообщений INVITE мы определим отдельный блок маршрутизации - route[3]. # Он будет отвечать за процесс установления соединения. route(3); # Передаем управление блоку маршрутизации route[3] для всех INVITE сообщений, # которые не были обработаны блоком свободной маршрутизации. INVITE сообщения, # которые подходят под это сравнение - это только оригинальные сообщения INIVITE, # а не повторно переданные. После того, как сообщение INVITE будет обработано, # мы заканчиваем обработку сообщения директивой break. break; } else if (method=="REGISTER") { route(2); break; }; lookup("aliases"); if (uri!=myself) { route(1); break; }; if (!lookup("location")) { sl_send_reply("404", "User Not Found"); break; }; route(1); } # ----------------------------------------------------------------- # Default Message Handler # Обработчик SIP сообщений, по умолчанию. # ----------------------------------------------------------------- route[1] { if (!t_relay()) { sl_reply_error(); }; } # ----------------------------------------------------------------- # REGISTER Message Handler # Обработчик SIP сообщений REGISTER. # ---------------------------------------------------------------- route[2] { sl_send_reply("100", "Trying"); # При получении SIP сообщения REGISTER, мы немедленно отвечаем сообщением "100 Trying", # SIP клиенту, пославшему данное сообщение, для предотвращения повторных отправок сообщений REGISTER. # Вследствии того, что SER базируется на транспортном протоколе UDP, то нет никакой гарантии доставки # SIP сообщения адресату и, следовательно, если отправитель не получит довольно быстрого ответа # на свое сообщение, то он будет пытаться отправить его повторно. # # Отвечая ему сообщением "100 Trying", мы говорим SIP клиенту, что мы обрабатываем его запрос. if (!www_authorize("","subscriber")) { # Функция www_authorize () используется для проверки данных авторизации, предоставленных пользователем, # и данных, которые имеются в таблице пользователей MySQL. Если представленные данные правильные, # то функция вернет TRUE, иначе - FALSE. # # Если в полученном SIP сообщении не представлены данные для авторизации, то эта функция вернет FALSE. # # Первый параметр определяет значение для realm (имя домена), для проверки подлинности данных пользователя. # Так как у нас не используется realm, то мы будем использовать пустую строку для этого параметра. # Вы можете представить realm, так же, как и realm (имя домена), которое используется WEB сервером. # # Второе значение указывает серверу SER, какое имя MySQL таблицы использовать для поиска учетной # записи пользователя. В данном случае - это таблица с именем 'subscriber'. www_challenge("","0"); # Тут мы отправляем SIP клиенту сообщение "401 Unauthorized", заставляя его повторно послать # запрос на регистрацию, в который должна быть включена дайджест авторизация. # # Функция www_challenge() имеет два аргумента. Первый - это realm, значение которого будет помещено # в заголовочное поле WWW-Authorize сообщения, которое SER отправит SIP клиенту. # Если Вы укажите значение для этого аргумента, то realm будет использоваться SIP клиентом, # при выборе соответствующих данных для авторитизации. # # Второй аргумент управляет включением параметра qop в запрос для дайджест аворизации. # Рекомендуется сохранять значение этого параметра равного 1. См: RFC2617 для полного описания # дайджест авторизации. Стоит отметить, что некоторые IP телефоны не поддерживают qop авторизацию. # Вы можете пробовать установить значение этого параметра в 0, если у Вас существуют проблемы # с неправильным паролем, в том случае, когда Вы точно знаете, что он правильный. break; # Т.к. мы уже отправили SIP клиенту сообщение с кодом ошибки 401 предыдущей командой, # нам больше не нужно обрабатывать это сообщение. Поэтому мы используем команду break, # для возврата в основной блок маршрутизации. }; if (!check_to()) { # При работе в качестве SIP прокси сервера, Вы должны быть уверены, что правильные учетные # записи пользователей, которые были успешно зарегистрированы на сервере, не могли бы использоваться # неавторизированными пользователями. По этой причине в конфигурацию сервера SER включена # функция check_to(). # # Мы вызываем функцию check_to() до проверки сообщения REGISTER. Это заставляет сервер SER проверить, # представленное в сообщении, заголовочное поле To: на предмет соответствия его с данными указанными # для дайджест авторизации. Если оно не соответствуют, тогда мы должны отклонить это сообщение REGISTER # и возвратить сообщение об ошибке. sl_send_reply("401", "Unauthorized"); # Если функция check_to() возвратит FALSE, тогда мы отвечаем SIP клиенту сообщением "401 Unauthorized". # Потом выполняем команду break для возврата в основной блок маршрутизации. break; }; consume_credentials(); # Мы не хотим рисковать, и предотвращаем отправку данных дайджест авторизации вышестоящему # или нижестоящему серверу, путем удаления всех заголовочных полей сообщения: WWW-Authorize # или Proxy-Authorize до того, как мы передадим это сообщение далее. if (!save("location")) { # Если обработка сообщения дошла до этой точки файла ser.cfg, то это означает, что данные SIP пользователя # были успешно проверены по данным из таблицы пользователей MySQL, теперь мы используем функцию # save(location), для добавления данных о местонахождении SIP клиента в базу данных MySQL. # Т.к. мы сохраняем эту контактную информацию в MySQL, мы можем безболезненно перезапустить # сервер SER, не потеряв информацию о местонахождении зарегистрированных SIP клиентов. sl_reply_error(); }; } # ----------------------------------------------------------------- # INVITE Message Handler # Обработчик SIP сообщений INVITE. # ----------------------------------------------------------------- route[3] { # Маршрутизационный блок Route[3], который тут рассматривается, предназначен для обработки # сообщений INVITE. if (!proxy_authorize("","subscriber")) { # Мы используем функцию proxy_authorize(), т.к. наш сервер не является публичным прокси сервером # для SIP коммуникаций. # Proxy_authorize() требует, чтобы запрос INVITE содержал в себе информацию с дайджест авторизацией. # Если она присутствует, то функция попытается найти клиента в таблице подписчиков, для проверки # правильности представленных данных авторизации. # # Как и функция www_authorize(), proxy_authorize () также имеет два параметра. # Первое - это realm, второе - имя таблицы MySQL, # по которой будут сверяться данные клиентов. В нашем случае это - MySQL таблица с именем "subscriber". proxy_challenge("","0"); # Если достоверность данных пользователя не подтверждается сервером SER, тогда он отвечает клиенту SIP # сообщением "401 Unauthorized". Функция Proxy_challenge() имеет два параметра, назначение которых # идентично параметрам функции www_challenge (), а именно, realm и спецификатор qop. break; # Теперь, когда мы ответили сообщением с кодом ошибки 401, мы вызываем команду break # для прерывания работы и возврата в основной блок маршрутизации. } else if (!check_from()) { # Тут мы используем функцию check_from() для проверки того, что полученные данные дайджест авторизации # не были подделаны в сообщении INVITE. Эта функция проверяет соответствие имени пользователя с данными # дайджест авторизации, дабы удостовериться в их подлинности. sl_send_reply("403", "Use From=ID"); # Если функция check_from() возвратит FALSE, тогда мы отвечаем SIP клиенту сообщением "401 Unauthorized" # и возвращаем управление в основной блок маршрутизации. break; }; consume_credentials(); # Мы не хотим рисковать, и предотвращаем отправку данных дайджест авторизации вышестоящему # или нижестоящему серверу, путем удаления всех заголовочных полей сообщения: WWW-Authorize или # Proxy-Authorize до того, как мы передадим это сообщение далее. lookup("aliases"); # Здесь мы производим поиск псевдонимов, которые могут быть связанны с набранным номером. # Если набранный номер является псевдонимом и его доменная часть не принадлежит нашей местной сети, # то только в этом случае передаем это сообщение далее. if (uri!=myself) { route(1); break; }; if (!lookup("location")) { # Теперь, когда были проделаны все преобразования над URI запроса, мы можем попытаться найти # правильную контактную информацию в таблице местоположений базы данных MySQL. # Если запись AOR ("address of record" адрес клиента) невозможно найти, тогда мы отвечаем # сообщением с кодом ошибки 404. sl_send_reply("404", "User Not Found"); break; }; route(1); # И, наконец, если обработка сообщения дошла до этой точки, то это означает, что вызывающий # абонент был успешно авторизирован и мы можем безопасно передать далее это INVITE сообщение. }
Использование ser.cfg с включенным в нем режима авторизации на прокси сервере.
Прежде, чем Вы сможете использовать эту новую конфигурацию SIP маршрутизатора, Вы должны сконфигурировать учетные записи SIP пользователей, используя скрипт serctl. Как и в предыдущей простейшей конфигурации, продолжим, что у нас есть пользователи с номерами 1000 и 1001. В Простейшей конфигурации мы регистрировали любого пользователя, отправившего нам SIP сообщение REGISTER, без всякой проверки.
Теперь нам требуется их авторизировать. Итак, откроем терминальное окно и выполним следующие две команды:
- serctl add 1000 password1 user1 в nowhere.com
- serctl add 1001 password2 user2 в nowhere.com
Вы получите запрос на ввод пароля. Это должен быть тот пароль, под которым Ваш пользователь, от которого сервер SER обращается к базе данных MySQL. Обратитесь к руководству по базе данных MySQL для получения инструкций, как создать пользовательский аккаунт для доступа к базе данных MySQL.
Как только будут созданы учетные записи для Ваших пользователей, Вам понадобиться обновить настройку Ваших SIP телефонов, изменив пароли на те, что Вы использовали в команде serctl. Теперь, после перезагрузки Ваших SIP телефонов, Вы должны иметь возможность совершать с них телефонные вызовы.
Использованные материалы: http://siprouter.onsip.org/doc/gettingstarted/ch07.html