Настройка Asterisk

Введение

Asterisk - это открытый сервер IP-телефонии для Unix-подобных операционных систем. Широко распространен и пользуется популярностью у компаний различного уровня. Настройка Asterisk на различных операционных системах не отличается, поэтому в статье будет рассказываться о настройке в целом, без привязки к какой либо операционной системе. Так же в статье не будет затронут вопрос установки Asterisk, только настройка. Приведенные в статье выдержки из конфигурации протестированы используются в рабочих сервера телефонии. Итак, приступим.

Краткие сведения о настройке

Как правило настройка Asterisk сводится к настройке двух конфигурационных файлов: sip.conf и extensions.conf, расположенных в директории /etc/asterisk. Не всегда, конечно, этого будет достаточно, но для большинства случаев подойдет. Если в двух словах говорить об этих файлах, то в файле sip.conf настраиваются номера телефонов и прописываются внешние линии (транки), в файле extensions.conf - диалплан (маршрутизация звонков). Маршрутизация звонков осуществляется по контекстам. Контекст - это часть диалплана, представляющая из себя группу абонентов. Контексты используются для разграничения телефонных номеров, выполняющих различные функции. Номера в разных контекстах полностью изолированы друг от друга, если администратором не разрешено иное. Наименование контекста заключается в квадратные скобки, например [context]. В начале диалплана должны присутствовать два обязательных контекста [general] и [globals], в них вписываются настройки самого диалплана и глобальные переменные. К контексту относится информация вписанная между названием контекста и названием следующего контекста.

Астериском можно управлять через интерфейс CLI. Для того что бы в него войти нужно выполнить команду:

[root@hostname ~]# asterisk -r

После изменения конфигурационных файлов в CLI Asterisk необходимо выполнить:

CLI> core reload

Для просмотра подключенных пользователей и транков:

CLI> sip show peers

Настройка sip.conf

Внутренние номера телефонов можно создавать прописывая конфигурацию для каждого номера отдельно или создать шаблон и привязывать каждый номер к этому шаблону. Если номеров много, то наиболее целесообразно использовать второй вариант. Если мало - то без разницы. При настройке я всегда переименовываю или удаляю стандартные sip.conf и extensions.conf и создаю свои, советую делать так же, это избавит конфигурацию от лишнего мусора.

Перед созданием самих номеров в файл sip.conf необходимо добавить следующую информацию:

[general]
language=ru
context=default
allowoverlap=no
udpbindaddr=0.0.0.0
transport=udp
srvlookup=yes
allowguest=no
[authentication]

Создание внутренних телефонов

Далее создаем шаблон для внутренних телефонов с контекстом local-phone. Назовем его local:

[local](!)
secret=113355
type=friend
context=local-phone
host=dynamic
nat=no
qualify=yes
canreinvite=no
callgroup=1
pickupgroup=1
call-limit=2
disallow=all
allow=alaw
allow=ulaw

В моем примере параметр call-limit имеет значение 2. Это означает, что с одного телефона можно одновременно вести два вызова, один активный, другой на удержании. Если вам этого не нужно, то можно указать call-limit=1, но в этом случае могут быть проблемы с сопровождаемым переводом вызова, так как при постановке активного звонка на удержание и наборе нового телефон выдаст ошибку связанную с call-limit.

Теперь создаем номера внутренних абонентов по шаблону local:

[101](local)
callerid="Абонент 1" <101>[102](local)
callerid="Абонент 2" <102>[103](local)
callerid="Абонент 3" <103>

И так далее, количество абонентов не ограничено.  Так же необязательно использовать трехзначные номера вида: 101, 102, 103.... Можно использовать любые значения. Вот в принципе и все, для создания внутренних телефонных номеров этого достаточно. Для регистрации абонентов на сервере в настройках телефонного аппарата нужно прописать адрес сервера, ID абонента, он же номер (101, 102, 103) и пароль, в примере это 113355 (secret=113355).

Регистрация внешних линий

Внешние линии в IP-телефонии называют SIP-транками. Для регистрации такой линии в Asterisk нужно заключить договор с провайдером IP-телефонии или же иметь аналоговую городскую линию и подключить ее например через FXO шлюз. Пример подключения внешней линии от провайдера IP-телефонии:

[Voip-line]
type=friend
defaultexpiry=120
host=login.test.ru
defaultuser=919617648048376
fromuser=919617648048376
secret=H6nnbbb&&fAqk4
context=default
dtmfmode=rfc2833
disallow=all
allow=alaw
allow=ulaw
insecure=invite,port
nat=force_rport,comedia

Пример подключения внешней линии через FXO-шлюз:

[2423467]
host=dynamic
context=fxo
type=friend
username=2423467
secret=12345
nat=no
qualify=yes
port=5060
directmedia=no
call-limit=1

Обратите внимание, что для подключения городских линий через FXO-шлюз, требуется дополнительная настройка самого шлюза. В двух словах мы прописываем вышеприведенную информацию в Asterisk (файле sip.conf), затем в шлюзе прописываем IP-адрес сервера, логин (2423467) и пароль (12345). Логин и пароль задается произвольный. Я для удобства использую логин, совпадающий с внешним номером телефона. После сохранения данных настроек регистрация номера должна пройти успешно. На этом с настройкой sip.conf пока все, перейдем к более сложной части настойки Asterisk - к диалплану.

Настройка extensions.conf

Как я уже писал выше диалплан отвечает за маршрутизацию звонков и настраивается в файле extensions.conf. Если в файле sip.conf настраиваются устройства, которые будут работать с телефонной станцией, то в extensions.conf настраивается поведение этой станции. Все маршруты содержаться в контекстах. Как я уже писал выше в начале диалплана должны присутствовать два обязательных контекста [general] и [globals]. После них уже прописываются пользовательские контексты. Маршрутизация звонка осуществляется между номерами одного контекста.

Звонки на внутренние номера

Например для того, что бы можно было совершать звонки на внутренние номера, которые мы создали в файле sip.conf нужно добавить маршрут в контекст к которому относятся внутренние номера:

[local-phone]

exten => _XXX,1,Dial(SIP/${EXTEN},30)

Все маршруты диалплана начинаются с exten =>, далее идет номер, который пользователь набрал на своем телефоне, в примере _XXX означает любой трехзначный номер. Цифра 1 обозначает приоритет маршрута. Dial - это приложение которые будет использоваться, в данном примере это означает звонить. В скобках SIP/${EXTEN} означает звонить по протоколу SIP на переменную ${EXTEN}, которая является набранным номером. Далее цифра 30 отвечает за длительность звонка, если через 30 секунд трубку никто не снимет, звонок будет сброшен. Этот маршрут с использованием переменных является как бы динамическим, т.е. в нем не прописано явно куда звонить, маршрут определятся набранным абонентом номером телефона. В некоторых случаях есть необходимость настраивать статические маршруты. Например статический маршрут для звонка на номер 101 будет выглядеть следующим образом:

exten => 101,1,Dial(SIP/101,30)

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

exten => 10,1,Dial(SIP/110,30)

Если же вызов нужно направить в другой контекст можно использовать приложение Goto, например для вызова на номер 200, который расположен в контексте local-phone нужно прописать следующий маршрут:

exten => 200,1,Goto(local-phone,200,1)

По другому это можно записать так:

exten => 200,1,Dial(Local/200@local-phone)

Смысл остается тот же, только уже используется приложение Dial.

Звонки на внешние номера

Для того, что бы осуществить вызов через внешнюю линию с вымышленным номером 2423467, который мы подключили к станции в примеры выше нужно вписать в контекст, в котором находятся внутренние номера следующее:

exten => _XXX.,1,Dial(SIP/${EXTEN}@2423467)

Это будет означать, что вызов  на любой номер, который длиннее трех цифр (_XXX.) будет осуществляться с приоритетом 1 на набранный номер через транк 2423467. За то что звонок будет на номер любой длинны больше трех цифр отвечает точка после _XXX.

Распределение входящих звонков с внешнего номера

Для получения звонков на внутренние телефоны с внешнего номера нужно настроить контекст к которому относится это номер. В моем примере с номером 2423467 это контекст fxo. После маршрутов контекста внутренних номеров local-phone вписываем контекст внешних номеров fxo с маршрутом:

[fxo]

exten => 2423467,1,Dial(SIP/101&SIP/102&SIP/103)

В данном примере при звонке номер 2423467 звонок поступит с приоритетом 1 на три внутренних номера: 101, 102 и 103. В скобках можно указать любое количество внутренних телефонов.

Распределение звонков с задержкой

Бывают случаи когда необходимо сделать так, что бы при поступлении звонка на определенный номер сначала начинал звонить один телефон, а через какое то время, если никто не ответил, другой. Например нужно, что бы при звонке на номер 2423467 сначала начинал звонить только номер 101, а через 15 секунд, если никто не снял трубку, номера 102 и 103. Для того что бы это реализовать нужно создать еще один контекст, отвечающий за ожидание в 15 секунд. Назовем его, к примеру wait. Добавляем в диалплан следующее:

[wait]
exten => _X.,1,NOCDR
exten => _X.,n,Wait(15)
exten => _X.,n,Dial(SIP/${EXTEN})
exten => _X.,n,Hangup()

Затем в контекст к fxo добавляем маршрут:

exten => 2423467,1,Dial(SIP/101&Local/102@wait&Local/103@wait)

В примере ожидание задано в 15 секунд, на деле же можно указать любое значение, которое вам необходимо. Так же можно создать несколько контекстов ожидания и настроить так что через 15 секунд к звонку подключиться номер 102, а через 20 - номер 103.

Запись разговоров

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

exten => 2423467,1,Set(fname=${STRFTIME(${EPOCH},,%Y%m%d%H%M)}-${CALLERID(number)}-${EXTEN})
exten => 2423467,n,MixMonitor(/hdd/records/${fname}.wav)
exten => 2423467,n,Dial(SIP/101&Local/102@wait&Local/103@wait)

В данном примере /hdd/records/ это путь к каталогу куда будут сохранены аудиофайлы с разговорами. Сами файлы будут иметь название вида: 202003051326-101-80000000000.wav. Из названия видно что звонок был совершен 5 марта 2020 года в 13:26 с номера 101 на номер 80000000000. Формат аудиофайлов - wav. Если же такой формат вам не подходит, то можно переконвертировать его в mp3, который занимает гораздо меньше места на диске. Есть несколько способов сделать это. Например можно используя пакет lame прописать данные правила в диалплане. Я предпочитаю так не делать и использую скрипт для конвертации с помощью того же lame. Работает это так, скрипт добавлен в планировщик, время запуска, допустим, задано на 3 часа ночи, когда отсутствует активность пользователей. В течении дня все разговоры сохраняются в формате wav, затем ночью запускается скрипт, конвертирует wav в mp3 и удаляет wav. Для этого я написал небольшой скрипт на Python:

import os
cmd = """find . -iname "*.wav" -exec lame '{}' ';' ; rm -f *.wav"""
os.system (cmd)

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

Для добавления скрипта в планировщик нужно в файл /etc/crontab добавить например такую строку:

* 3 * * * root (cd /hdd/records && python3 /hdd/scripts/mp3_convert.py)

С такой настройкой скрипт mp3_convert.py, находящийся в каталоге /hdd/scripts/ будет выполнен от имени пользователя root в каталоге /hdd/records. Выполнение скрипта от имени root приведено в качестве примера и не рекомендуется в рабочем исполнении.после внесения изменений в crontab, службу crond необходимо перезапустить:

[root@hostname ~]# systemctl restart crond

Переадресация в зависимости от времени суток и дня недели

Часто нужно что бы распределение вызовов осуществлялось в зависимости от времени суток или дня недели. Например надо что бы в рабочее время с понедельника по пятницу с 8:00 до 17:00 звонил один телефон, а в остальные время - другой. Для того что бы сделать это, нужно добавить в контекст следующее:

exten => 101,1,GotoIfTime(08:00-17:00|mon-fri|*|*?101,1)
exten => 101,n,Dial(SIP/102)

Теперь при звонке на номер 101 в рабочее время звонок поступит на номер 101, в остальное же время - на номер 102.

IVR меню

Для создания IVR меню нужно создать контекст, который будет отвечать за меню, назовем его ivr. В контекст вставляем следующее:

[ivr]
exten => 200,1,Answer()
same => n,Background(intro)
exten => 1200,n,WaitExten(7) ;Время ожидания ввода номера после проигрывания IVR меню
exten => 1,1,Goto(local-phone,101,1) ;При вводе цифры 1 идет звонок на номер 113 в контексте local-phone
exten => 2,1,Goto(local-phone,102,1) ;При вводе цифры 1 идет звонок на номер 114 в контексте local-phone
exten => 3,1,Goto(local-phone,103,1)
exten => 4,1,Goto(local-phone,104,1)
exten => _XXX,1,Dial(SIP/${EXTEN}) ;Набор внетренего номера телефона в IVR меню
exten => #,1,Goto(ivr,200,1) ;При нажатии # повторное воспроизведение IVR меню
exten => i,1,Goto(ivr,200,1) ;При нажатии неверного номера начинается повторное воспроизведение IVR меню
exten => t,1,Goto(local-phone,101,1) ;Срабатывается после ожидания WaitExten если не одна цифра не набрана
exten => s,n,Hangup()

Теперь в контексте наших номеров необходимо сделать как бы ссылку на контекст ivr:

[local-phone]

exten => 200,1,Goto(ivr,200,1)

Теперь при наборе номера 200 звонок пойдет на номер 200 контекста ivr, на котором мы и создали голосовое меню. При поступлении звонка на номер 200 контекста ivr запустится приложение Answer, отвечающее за ответ на звонок. Далее запускается приложение Background, проигрывающее аудиозапись. В скобках название проигрываемого wav файла расположенного в /var/lib/asterisk/sound. WaitExten задает время ожидания ввода номера после проигрывания IVR меню. При вводе цифр от 1 до 4 вызов поступит на внутренние номера 101, 102, 103 и 104 соответственно. При вводе трехзначного номера звонок поступит на набранный внутренний номер. При вводе # и неверной кнопки меню будет воспроизведено повторно. Если ничего не ввести, то после ожидания WaitExten звонок поступит на номер 101.

Соединение двух Asterisk

Для того что бы совершать звонки между абонентами двух разных серверов Asterisk их необходимо зарегистрировать между собой. Делается это в файле sip.conf. Допустим адрес локального сервера  192.168.0.5, удаленного 192.168.0.3. В секции  general добавляем:

[general]
register => localasterisk:12345@192.168.0.3/remoteasterisk

В этой строке localasterisk:12345 означают логин и пароль заданный на удаленном сервере. Затем в конце файла вставляем следующее:

[remoteasterisk]
type=friend
secret=12345
context=local-phone
host=dynamic
qualify=300

На удаленно сервере добавляем:

[general]
register => remoteasterisk:12345@192.168.0.5/localasterisk[localasterisk]
type=friend
secret=12345
context=local-phone
host=dynamic
qualify=300

После этого два сервера должны соединиться между собой. В этом можно убедиться выполнив sip show peers в CLI. Осталось только добавить маршрут в диалплан. Обратите внимание, что номера удаленного и локального астериска не должны совпадать. Например номера локального сервера начинаются на цифру 1, а удаленного на цифру 3. Вставляем в нужный контекст (в моем случае это все тот же local-phone) строку:

exten => _3XX,1,Dial(SIP/${EXTEN}@remoteasterisk,30)

На удаленном соответственно:

exten => _1XX,1,Dial(SIP/${EXTEN}@localasterisk,30)

Если все сделано правильно можно совершать звонки между серверами.

Пример конфигурации

Подведем итог того, что у нас получилось. Со всеми приведенными настройками файл sip.conf выглядит так:

[general]

register => remoteasterisk:12345@192.168.0.5/localasterisk
language=ru
context=default
allowoverlap=no
udpbindaddr=0.0.0.0
transport=udp
srvlookup=yes
allowguest=no
[authentication]

[local](!)
secret=113355
type=friend
context=local-phone
host=dynamic
nat=no
qualify=yes
canreinvite=no
callgroup=1
pickupgroup=1
call-limit=2
disallow=all
allow=alaw
allow=ulaw

[101](local)
callerid="Абонент 1" <101>

[102](local)
callerid="Абонент 2" <102>

[103](local)
callerid="Абонент 3" <103>

[2423467]
host=dynamic
context=fxo
type=friend
username=2423467
secret=12345
nat=no
qualify=yes
port=5060
directmedia=no
call-limit=1

[localasterisk]
type=friend
secret=12345
context=local-phone
host=dynamic
qualify=300

Конфигурация extensions.conf:

[general]
static=yes
writeprotect=no
[globals][wait]
exten => _X.,1,NOCDR
exten => _X.,n,Wait(15)
exten => _X.,n,Dial(SIP/${EXTEN})
exten => _X.,n,Hangup()[ivr]
exten => 200,1,Answer()
same => n,Background(intro)
exten => 1200,n,WaitExten(7) ;Время ожидания ввода номера после проигрывания IVR меню
exten => 1,1,Goto(local-phone,101,1) ;При вводе цифры 1 идет звонок на номер 113 в контексте local-phone
exten => 2,1,Goto(local-phone,102,1) ;При вводе цифры 1 идет звонок на номер 114 в контексте local-phone
exten => 3,1,Goto(local-phone,103,1)
exten => 4,1,Goto(local-phone,104,1)
exten => _XXX,1,Dial(SIP/${EXTEN}) ;Набор внетренего номера телефона в IVR меню
exten => #,1,Goto(ivr,200,1) ;При нажатии # повторное воспроизведение IVR меню
exten => i,1,Goto(ivr,200,1) ;При нажатии неверного номера начинается повторное воспроизведение IVR меню
exten => t,1,Goto(local-phone,101,1) ;Срабатывается после ожидания WaitExten если не одна цифра не набрана
exten => s,n,Hangup()[local-phone]exten => 200,1,Goto(ivr,200,1)exten => _3XX,1,Dial(SIP/${EXTEN}@remoteasterisk,30);Звонки начинающие с цифры 3 идут на удаленный астерискexten => _1XX,1,Dial(SIP/${EXTEN},30); Звонки на внутренние номера, начинающиеся с цифры 1

exten => _XXX.,1,Dial(SIP/${EXTEN}@2423467); Звонки на внешние номера

exten => _X.,n,Hangup()

[fxo]

exten => 2423466,1,Goto(local-phone,200,1); Входящие звонки  с внешнего номера на IVR меню

exten => 2423467,1,Dial(SIP/101&SIP/102&SIP/103); Входящие звонки  с внешнего номера без записи разговора

exten => 2423468,1,Set(fname=${STRFTIME(${EPOCH},,%Y%m%d%H%M)}-${CALLERID(number)}-${EXTEN})
exten => 2423468,n,MixMonitor(/hdd/records/${fname}.wav)

exten => 2423468,n,Dial(SIP/101&Local/102@wait&Local/103@wait); Входящие звонки  с внешнего номера с распределением по времени и записью разговора

Заключение

На этом все. Статья затрагивает наиболее востребованный функционал ip-телефонии, по крайней мере по моей собственной практике. Если остались вопросы - пишите в комментариях.

Читайте также:

Оставить комментарий