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