Asterisk: быстрая настройка для небольшого офиса

В этот статье я опишу простую настройку IP телефонии в небольшом офисе. Мы объединим несколько отделов в одну сеть, сделаем доступной статистику разговоров через web-интерфейс, а также организуем запись внешних звонков. Надеюсь, эта статья поможет администраторам, начинающему работать с Asterisk Communications Framework. В сети вроде бы полно подобных статей, но я не встретил подробного описания рабочего решения.

Нам понадобится сервер с Ubuntu, IP телефоны (или софтофоны) на рабочих местах и данные от провайдера для подключения к внешним линиям (транкам).
Список задач следующий:
  1. Настройка сервера и установка Asterisk.
  2. Базовая настройка Asterisk, диалплан (маршрутизация звонков), добавление внутренних номеров, очереди звонков.
  3. Настройка Asterisk CDR Viewer для удобного отображения статистики разговоров
Настройка сервера:

Для сервера телефонии мы будет использовать простой компьютер с 2 сетевыми картами. Одна карта (eth1) подключена в нашу локальную сеть (192.168.0.0/24), другая (eth2) в Интернет или в подсеть провайдера (192.168.168.0/30), через которую он предоставляет доступ к своему серверу телефонии. Будем считать, что вы справились с сетевыми настройками самостоятельно. В нашем случае адрес SIP сервера провайдера 10.222.2.10, он не принадлежит к выделенной подсети, поэтому мы добавили маршрут через шлюз подсети провайдера с помощью команды:
route add -host 10.222.2.10 gw 192.168.168.1 eth2
Добавим в конец /etc/network/interfaces правило для поднятия маршрута при перезагрузке:
# Маршрут для телефонии
post-up route add -host 10.222.2.10 gw 192.168.168.1 eth2
Устанавливаем Астериск:
Ставить будем чистый фреймворк астериск без web интерфейса. Во-первых, если настраивать через браузер в конфигурационных файлах получается каша и в сложных ситуациях с ошибками разобраться в ней становиться трудно. Во вторых, астериск настроенный руками через конфиги работает стабильнее и проще для понимания. Для установки более новой версии, вы можете собрать астериск из исходников, но для наших задач достаточно версии из репозитория.
sudo apt-get update
sudo apt-get install asterisk
Базовая настройка Asterisk
После установки открываем:
nano /etc/asterisk/sip.conf
Этот файл служит для базовой настройки астериска и конфигурации SIP каналов - пиров (peers). Мы внесем туда данные для авторизации внутренних пользователей и внешних номеров. Сразу после установки астериск будет работать с этим файлом без внесения изменений, но мы подправим его под свои нужды. Комментарии в файле обозначаются знаком ; Вот листинг файла:
[general]
realm = callcenter ; используется для аутентификации в SIP, задайте полное доменное имя вашего сервера
context = default ; контекст обработки входящих вызовов в extensions.conf.
allowguest = no ; запрещает гостевые (без аутентификации) подключения
allowoverlap = no ; выключить набор по одной цифре (каждая набранная цифра не будет сразу отправляться в канал)
udpbindaddr = 0.0.0.0 ; адрес на котором Asterisk слушает udp подключения
tcpenable = no ; выключить поддержку TCP транспорта chan_sip Asterisk (нам не нужна)
tcpbindaddr = 0.0.0.0 ; адрес на котором Asterisk слушает TCP подключения
transport = udp ; транспорт по умолчанию
srvlookup = yes ; включаем возможность принимать SIP вызовы на основании доменных имен
pedantic = yes ; включить медленную, педантичную проверку полей Call-ID и всех строк в многострочном заголовке SIP сообщения и кодированных URI заголовков
disallow = all ; запретить использование всех кодеков, чтобы затем разрешить определенные
allow = alaw
allow = ulaw
allow = h263
allow = h263p
language = ru ; код языка, которые определены в файле indications.conf — где определяется язык приветствий и специфичные для каждой страны настройки сигналов телефонов
tonezone = ru
relaxdtmf = yes ; если плохо распознаются DTMF сигналы, включите данную опцию
compactheaders = yes ; использовать компактные SIP заголовки
videosupport = no ; выключаем поддержку видео
callevents = yes ; для генерации информации о SIP событиях в AMI (asterisk manager interface)
authfailureevents = yes ; устанавливает статус пира если не может авторизироваться
alwaysauthreject = yes ; если запрос аутентификации был отклонен, то в ответе НЕ будет написано, что юзер введен неверно, помогает защитится от атаки перебора имен пользователей
sendrpid = yes ; отправлять Remote-Party-ID header
autokill = yes ; при недоступности хоста любое новое соединение будет разорвано при отсутствии подтверждения ACK в течение 2000 мс
rtcachefriends = yes ; кэшируем friends (реалтайм пиры), которые приходят из realtime engine, так же, как если бы они сконфигурированы в sip.conf
rtsavesysname = yes ; сохраняем SystemName в базе данных в режиме реального времени во время регистрации
rtupdate = yes ; обновлять IP-адрес, порт и период регистрации пиров при регистрации
rtautoclear = yes ; завершать регистрацию соединений типа friend созданных на лету до следующей регистрации
rtpkeepalive = 5 ; раз в 5 сек asterisk будет посылать сообщения проверки активности установленного соединения в RTP-потоке
ignoreregexpire = yes ; пир не удаляется из памяти, после истечения регистрации
dtmfmode = rfc2833 ; как клиент обрабатывает сигнализацию DMTF, ставим рекомендуемое значение
canreinvite = no ; не соединять медиа потоки peer-to-peer в обход сервера
limitonpeers = yes ; использовать call-limit только для type=peer
qualifyfreq = 60 ; как часто проверять доступность хоста (в секундах)
qualify = yes ; посылать SIP запросы OPTIONS для проверки доступности устройства (если сами хосты не проверяют регистрацию)
transfer = yes ; при необходимости выполнять переадресацию вызова с целью сократить путь пакета между двумя конечными точками
allowtransfers = yes ; разрешаем любые трансферы, если не переопределено в настройках пира

; далее авторизация у провайдера по 3 телефонным номерам. Это нужно для того, чтобы
; сервер провайдера знал куда отправлять вызовы для нас. То есть, входящие звонки на наши
; номера провайдер будет перенаправлять на IP адрес нашего сервера благодаря этой
; регистрации. При авторизации по IP эти строчки не нужны!
register = 111111:password1@10.222.2.10/1000
register = 222222:password2@10.222.2.10/2000
register = 333333:password3@10.222.2.10/3000

[authentication]

[custom](!)
; в этой секции мы настраиваем шаблон для внутренних номеров
; в дальнейшем мы будем указывать его, чтобы не повторять список настроек
deny=0.0.0.0/0.0.0.0 ; запретить доступ из всех подсетей
permit=192.168.0.0/255.255.255.0 ; разрешаем доступ только из нашей подсети
type=friend ; разрешаем совершать и принимать звонки
qualify=yes ; опрашивать телефон каждые 2 секунды
port=5060 ; порт sip сервера
nat=no ; клиенты НЕ находятся за NATом
host=dynamic ; регистрировать клиента по имени, а не IP адресу для входящего звонка
dtmfmode=rfc2833 ; способ передачи dtmf сигналов, чаще всего rfc2833
; Сначала запрещаем все кодеки
; Потом указываем допустимые кодеки,
; перечисляя их построчно сверху вниз в порядке приоритете
disallow=all
allow=ulaw
allow=alaw
allow=g729
allow=g723
allow=g726
allow=h261
allow=h263
allow=h264
allow=h263p
canreinvite=no ; отключаем re-invite ввиду ненадобности а большинстве случаев

; Далее перечисляем внутренние номера
[100](custom)
defaultuser=100
secret=pass100
callerid=Директор <100>
pickupgroup=1
context=sip
callgroup=1

[101](custom)
defaultuser=101
secret=pass101
callerid=Сотрудник 1 <101>
pickupgroup=2

[102](custom)
defaultuser=102
secret=pass102
callerid=Склад <102>
pickupgroup=1
context=sklad
callgroup=1
В секции [general] мы описали основные настройки программы: запретили гостевые вызовы не авторизованным пользователям, задали контекст по умолчанию, ввели данные для авторизации к транкам провайдера, и т.д. Далее, в секции [authentication] мы создали шаблон для номеров [custom](!) в котором прописали настройки для всех клиентов, которые будем в дальнейшем тиражировать. Далее мы создали 3 внутренних номера (sip аккаунта), задав номер, пароль, имя, контекст и внутреннюю группу вызова (для возможности перехвата звонка внутри этой группы, по умолчанию клавишами *8). Остальные настройки для каждого внутреннего номера подхватились из секции [custom](!). Подробнее ознакомиться со значениями параметром можно здесь.

Рассмотрим подключение к транкам провайдера подробнее:
register = 111111:password1@10.222.2.10/1000
В этой строчке 111111 - это номер телефона предоставленный провайдером, password1 - пароль, 10.222.2.10 - IP адрес сервера провайдера и /1000 - внутренний номер с нашей стороны назначенный для этого транка. При авторизации по IP (когда провайдер не дает имя и пароль для подключения, а только сообщает IP адрес сервера), вместо этой строчки для регистрации номера внутри астериска мы добавляем секции для авторизации ниже строчки [authentication] для каждого номера, примерно таки образом:

[111111]
host=10.222.2.10
port=5060
fromuser=111111
fromdomain=10.222.2.10
type=peer
context=work-in
insecure=port,invite
qualify=yes
nat=no
disallow=all
allow=ulaw,alaw
dtmfmode=rfc2833
defaultuser=111111
outboundproxy=10.222.2.10
Следующий файл для правки: /etc/asterisk/extensions.conf. В нем мы опишем диалплан, то есть маршрутизацию и обработку входящих и исходящих звонков. Открываем /etc/asterisk/extensions.conf и наполняем примерно таким содержимым:

[general]
static=yes
writeprotect=no
clearglobalvars=no

;экстеншн по умолчанию, его принято закрывать в целях безопасности и удобства
[default]
exten => _X.,1,NoOp()
same => n,Busy()
same => n,HangUp()

;экстеншн для IP телефонов, включает в себя 3 других: для внутренних звонков, входящие и исходящие
[sip]
include => local
include => work-out
include => work-in

;экстеншн для IP телефона на складе с отдельным номером, включает внутренние звонки и входящие, исходящие звонки проходят по отдельному выделенному номеру 222222
[sklad]
include => local
include => work-in
exten => _NXX.,1,NoOp()
same => n,Macro(recording,${CALLERID(num)},${EXTEN})
same => n,Dial(SIP/222222/${EXTEN})
same => n,Hangup()

;экстеншн для локальных звонков внутри офиса для 3х значных номеров начинающихся с цифры 1
[local]
exten => _1XX,1,NoOp()
same => n,Dial(SIP/${EXTEN},,Tt)
same => n,Hangup()

;экстеншн для исходящих звонков в город
[work-out]
exten => _NXX.,1,NoOp()
same => n,ChanIsAvail(SIP/111111&SIP/222222&SIP/333333)
same => n,NoOp(Availchan is ${AVAILCHAN})
same => n,NoOp(Availstatus is ${AVAILSTATUS})
same => n,Macro(recording,${CALLERID(num)},${EXTEN})
same => n,Dial(${AVAILCHAN:0:10}/${EXTEN},,Tt)
same => n,Hangup()

;экстеншн для входящих звонков в офис, на каждый номер свои правила
[work-in]
exten => 111111,1,Macro(recording,${CALLERID(num)},${EXTEN})
same => n,NoOp(Incoming Call on 111111)
same => n,Queue(sale,Tt)
same => n,Hangup()

exten => 222222,1,Macro(recording,${CALLERID(num)},${EXTEN})
same => n,NoOp(Incoming Call on 222222)
same => n,Dial(SIP/102,,Tt)
same => n,Hangup()

exten => 333333,1,Macro(recording,${CALLERID(num)},${EXTEN})
same => n,NoOp(Incoming Call on 333333)
same => n,Dial(SIP/104,15,Tt)
same => n,Dial(SIP/100,15,Tt)
same => n,Hangup()

; MixMonitor - макрос для записи звонков
[macro-recording]
exten => s,1,GoToIf($["${RECORDING}" = "1"]?yes:no)
exten => s,n(yes),Set(fname=${UNIQUEID}-${STRFTIME(${EPOCH},,%Y-%m-%d-%H_%M)}-${ARG1}-${ARG2});
exten => s,n,Set(monopt=nice -n 19 /usr/bin/lame -b 32  --silent "${DIR_RECORDS}${fname}.wav"  "${DIR_RECORDS}${fname}.mp3" && rm -f "${DIR_RECORDS}${fname}.wav" && chmod o+r "${DIR_RECORDS}${fname}.mp3");
exten => s,n,Set(CDR(filename)=${fname}.mp3);
exten => s,n,Set(CDR(realdst)=${ARG2});
exten => s,n,MixMonitor(${DIR_RECORDS}${fname}.wav,b,${monopt});
exten => s,n(no),Verbose(Exit record);
Шаблоны номеров начинаются с символа _
Dial - это команда совершения вызова, параметрами к этой команде указываются канал и номер транка на который переводить звонок, также я добавил параметр Tt - для возможности перевода звонков. Подробнее про команду тут, там же можно найти описания других команд.
NoOp - команда для вывода сообщения в консоль, чтобы проще было отлаживать в случае ошибок, параметром передается строка сообщения или необходимые переменные.
Queue - команда переводящая вызов в очередь (имя задается в скобках), дальнейшая логика работы указанной очереди описывается в файле /etc/asterisk/queues.conf
Hangup - команда используется для завершения вызова в конце разговора.
Помимо основных команд мы используем макрос [macro-recording] для записи разговора. Сам макрос описан ниже, взяли мы его из мануала к CDR Viewer.

Регистрируем телефонные аппараты (или софтофоны) пользователей. Указываем в настройках адрес SIP сервера (внутренний IP нашего астериска), трехзначное имя и пароль из файла sip.conf.

Осталось описать очередь, в которую попадает позвонивший на номер 111111. Открываем файл /etc/asterisk/queues.conf и вносим примерно следующее:
[general]
autofill=yes
shared_lastcall=yes
updatecdr=yes
timeout=15

; название очереди
[sale]
; далее просто раскоментируйте строчу в зависимости от того, какой вариант вам подойдет
; перенаправить звонок на канал который самое долгое время не отвечал на звонки
;strategy=leastrecent
; перенаправить звонок на канал следующий после канала который отвечал на прошлый звонок
;strategy=rrmemory
; звонить по каналу который ответил на наименьшее количество звонков из очереди
;strategy=fewestcalls
; звонить по всем доступным каналам пока один не ответит
strategy=ringall

announce-frequency=30 ;сообщать клиенту позицию и/или приблизительное время ожидания обработки его вызова
announce-holdtime=yes ; анонс приблизительное время ожидания обработки вызова абонента (если время более минуты)
setinterfacevar=yes ; включает значение переменной MEMBERINTERFACE, которая хранит номер члена очереди, поднявшего трубку
joinempty=no ; не включать абонентов в очередь если в ней не зарегистрировано ни одного оператора
leavewhenempty=yes ; покинуть очередь если нет доступных операторов
ringinuse=no ; не звенеть оператору если статус InUse.

; тут мы перечисляем номера, которые участвуют в очереди
member => SIP/101
member => SIP/103
member => SIP/105
Подробнее про очереди можете прочитать, например, здесь.
Базовая настройка астериска закончена. Перейдем к статистике.

Статистика и запись звонков

Для сбора статистики нам нужно будет настроить выгрузку логов в mysql. Потом установить
Asterisk CDR Viewer и подцепить к той же базе. Устанавливаем поддержку mysql:
apt-get install asterisk-mysql
Далее логинимся в mysql и создаем базу для звонков примерно так:
mysql -uroot -ppassword
создаем базу:
CREATE DATABASE `voip` CHARACTER SET utf8 COLLATE utf8_general_ci;
создаем пользователя:
CREATE USER 'voip'@'localhost' IDENTIFIED BY 'password';
даем ему полный доступ до базы:
GRANT ALL PRIVILEGES ON voip.* TO 'voip'@'localhost';
обновляем права:
FLUSH PRIVILEGES;
Далее описываем структуру базы как-то так:

USE voip;
CREATE TABLE cdr (
calldate datetime NOT NULL default '0000-00-00 00:00:00',
clid varchar(80) NOT NULL default '',
src varchar(80) NOT NULL default '',
dst varchar(80) NOT NULL default '',
dcontext varchar(80) NOT NULL default '',
channel varchar(80) NOT NULL default '',
dstchannel varchar(80) NOT NULL default '',
lastapp varchar(80) NOT NULL default '',
lastdata varchar(80) NOT NULL default '',
duration int(11) NOT NULL default '0',
billsec int(11) NOT NULL default '0',
disposition varchar(45) NOT NULL default '',
amaflags int(11) NOT NULL default '0',
accountcode varchar(20) NOT NULL default '',
uniqueid varchar(32) NOT NULL default '',
userfield varchar(255) NOT NULL default '',
did varchar(50) NOT NULL default '',
recordingfile varchar(255) NOT NULL default '',
filename varchar(255) NOT NULL default '',
KEY `calldate` (`calldate`),
KEY `dst` (`dst`),
KEY `accountcode` (`accountcode`),
KEY `uniqueid` (`uniqueid`) 
);

ALTER TABLE cdr ADD id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY;
EXIT;
Но лучше импортировать в базу файл mysql_cdr.sql из папки docs архива Asterisk-CDR-Viewer-Mod. Там всегда свежая структура и необходимые триггеры в базе.

Подключать астер к mysql мы будет черед драйвер ODBC. Для этого устанавливаем:
apt-get install unixODBC unixODBC-dev libmyodbc
Смотрим где лежат конфигурационные файлики для ODBC командой:
odbcinst -j
Видим, что конфигурация для MySQL ODBC драйвера выполняется в файле /etc/odbcinst.ini
смотрим updatedb
Ищем, где располагаются файлы:
locate libmyodbc.so
locate libodbcmyS.so
либо так: find / -name 'lib*odbc*.so'
У меня они лежали в /usr/lib/x86_64-linux-gnu/odbc/libmyodbc.so и /usr/lib/x86_64-linux-gnu/odbc/libodbcmyS.so
Соответственно меняем пути в файле /etc/odbcinst.ini
[MySQL]
Description  = ODBC for MySQL
Driver          = /usr/lib/x86_64-linux-gnu/odbc/libmyodbc.so
Setup           = /usr/lib/x86_64-linux-gnu/odbc/libodbcmyS.so
Далее проверяем, включена ли в asterisk поддержка odbc. Если ставили из исходников -должны были собрать с поддержкой, если через apt-get она и так включена. Заходим в астер:
asterisk -rvvv
и выполняем:
odbc show
Следующим шагом будет конфигурация файла /etc/odbc.ini, который используется для создания идентификатора, который Asterisk будет использовать для ссылки на эту конфигурацию, если в будущем решите сменить БД, то следует переконфигурировать это файл. Как-то так:
[MySQL-asterisk]
Driver           = MySQL
Description   = MySQL Connector for Asterisk
Server           = localhost
Port               = 3306
Database       = voip
username      = voip
;password     = password
Option          = 3
Socket          = /var/run/mysqld/mysqld.sock
Charset         = utf8 
проверим:
odbcinst -s -q  
Теперь сконфигурируем Asterisk для работы с БД через ODBC, для этого служит файл /etc/asterisk/res_odbc.conf

nano /etc/asterisk/res_odbc.conf
[ENV]
[asterisk]
enabled => yes
dsn => MySQL-asterisk
username => voip
password => password
pooling => no
pre-connect => yes
Теперь перезапускаем астериск:
service asterisk restart
Заходим в астер и проверяем связь - смотрим и анализируем вывод:
asterisk -rvvvv
cdr show status
odbc show all
Теперь Настраиваем CDR устанавливаем программу для конвертации разговоров в mp3:
apt-get install lame
Далее скачиваем CDR Viewer, распаковываем например, в папку /var/www/html/stat , в файле config.inc.php прописываем доступ к mysql и имя базы (voip) и имя таблицы (cdr), путь до папки с записями разговоров и т.д. . Даем права на папку:
chown www-data:www-data -R /var/www/html/stat
Трудностей там не должно возникнуть. Далее открываем CDR через браузер как-то так:
http://ip-addres-astera/stat
После этого все должно работать. Если что-то не получилось - пишите в комментариях)

UPD 19.06.2019
В статье используется довольно старая версия фреймворка Астериск, для установки последней LTS версии следует ставить программу из исходников по этой статье. Если нужна более актуальная и обширная статья по настройке, или вам необходима помощь с внедрением - пишите в комментариях.

Вот тут цикл статей для более подробной настройке Asterisk.

8 комментариев:

  1. Все записи типа
    exten => _333333,n,Dial(SIP/100,15,Tt)
    exten => _333333,n,Hangup()

    Проще заменить на
    same => n,Dial(SIP/100,15,Tt)
    same => n,Hangup()

    И вот это _333333 шаблоном не является, поэтому знаки подчеркивания для таких exten не нужны.

    ОтветитьУдалить
    Ответы
    1. Спасибо за комментарий. Всё так, можно не использовать шаблоны для известных номеров и сокращать строки используя same вместо exten.

      Удалить
  2. Спасибо за подробный мануал!

    ОтветитьУдалить
    Ответы
    1. Пожалуйста. Рад, что информация оказалось полезной.

      Удалить
  3. ;экстеншн для исходящих звонков в город
    [work-out]
    ...
    ;...проверяются 3 канала на возможность совершения вызова...
    same => n,ChanIsAvail(SIP/111111&SIP/222222&SIP/333333)
    ...
    ;...но вызов совершается по первому каналу без учета проверки...
    same => n,Dial(SIP/111111/${EXTEN},,Tt)

    ...Вместо SIP/111111 должно быть ${AVAILCHAN:0:10} (первые 10 символов AVAILCHAN)

    ОтветитьУдалить
    Ответы
    1. Действительно, все так. Спасибо за комментарий.

      Удалить
  4. Приветствую!
    Помогите с настройкой, есть следующая проблема.
    Когда делаю звонок с сотового на телефон астериска FreePBX 14.0.13.23 (транк мультифон), звонок проходит, все нормально слышко, но потом после завершения звонка в истории звонящего сотового не сохраняется номер астера, пишет неизвестный. Уже все перетыкал что связано с сид.

    ОтветитьУдалить
    Ответы
    1. Добрый день, сложно что-то подсказать не видя вашей конфигурации.

      Удалить