Raspberry Pi + FreeBPX(asterisk) + Mikrotik = АТС мини

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Настройка "с нуля"

  • Введение, начало

  • Установка и настройка FreePBX

  • Подключение GSM модема

  • Настройка fail2ban

  • Настройка Mikrotik

Введение, начало

В небольшой офис потребовалась АТС, для звонков по России и между сотрудниками. Был выбор между использованием готовых решений от SIP провайдеров и самодельной личной АТС. У каждой стороны есть свои плюсы и минусы. Так как офис только запускался, сотрудников мало, тотальный режим экономии, то от провайдеров отказался - абонентская плата за номер (при том что номер дается городской, к которому сложно привязать мессенджеры, мобильные номера от провайдера у клиентов не вызывают доверия и так же имеют проблемы с привязкой к мессенджерам) и тарифы на звонки на мобильные номера клиентов у всех не менее 1,5 руб./мин.

Т.к. в наличии был не используемый Raspberry Pi 3B+, то решено было использовать его в качестве АТС с Asterisk. MicroSD карта, от 8Гб, желательно 16Гб для установки системы. Были попытки установки голой версии с нуля по различным туториалам из сети, но во всех постоянно были какие то проблемы, т.к. они все написаны от 2 до 10 лет назад, на быстрый старт они не сработали.

Так же для автономной работы без зависимости от провайдеров нужен USB модем с поддержкой голосовых вызовов, есть списки нормально работающих модемов (предварительно разблокированных по инструкциям из 4ПДА), у меня работает Huawei E1550.

Для безопасной работы в сети использую Mikrotik RB951G, теоретически можно любой, OS у них одна и та же (основ настройки роутера здесь не будет, подразумевается, что у вас интернет через него уже работает). Так же нужен интернет провайдер с возможностью предоставить статический или динамический IP адрес, что бы можно было подключатся к АТС из любого места сети интернет. Через приватные адреса у меня не получилось работать (через мобильные сети тоже, они все предоставляют адрес, к которому нет подключения снаружи, да же через DDNS сервисы).

Установка и настройка FreePBX

Сохраняем себе в закладки сайт http://www.raspberry-asterisk.org/ , там скачиваем образ системы raspbx-10-10-2020.zip. Распаковываем архив, если для записи используется программа Win32DiskImager, можно без распаковки и использовать для записи, например, BalenaItcher. Записываем на компьютер скачанный образ на карту microSd, записанную карту вставляем в raspberry, подключаем кабели (сеть к роутеру, питание), включаем и ждем загрузки, монитор не нужен. В роутере в разделе ip-dhcp server-leases смотрим назначенный адрес нашей АТС, это нужно будет для подключения по ssh.

В браузере набираем http://raspbx/, проходим первоначальную регистрацию, назначение имени и пароля администратора FreePBX, желательно сложный защищенный, если нет желания все снова настраивать восстанавливать. В конечном итоге после входа должно получится так:

Так же подключаемся через Putty к asterisk, логин и пароль пока стандартные root/raspberry. Меняем пароль по умолчанию:

passwd

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

Так как система основана на Debian, то здесь работают стандартные команды. Обновляем систему, настраиваем свою временную зону, устанавливаем модуль для работы с USB модемом, файловый навигатор MC для удобства:

raspbx-upgrade
configure-timezone
install-dongle
apt install mc

При установке модуля dongle указываем реальный номер, который будет использоваться в модеме (у оператора сотовой связи выбираем тариф с пакетом минут на звонки по России).

Так как у меня не получилось запустить систему на современном драйвере pjsip (в телефонах тишина), то все настройки будут на legacy sip, работает на всех абонентских устройствах.

В браузере переходим по порядку основные настройки:

Пояснение: 3-4 для безопасности, 5 - определение адреса внешнего IP, 6 - настройки вашей локальной сети, 7 - сужаем диапазон портов, так как сотрудников мало, чуть ниже выбираем стандартные кодеки ulaw alaw gsm, 9 - по завершению нажимаем на Apply config, ждем обновления конфигурации.
Пояснение: 3-4 для безопасности, 5 - определение адреса внешнего IP, 6 - настройки вашей локальной сети, 7 - сужаем диапазон портов, так как сотрудников мало, чуть ниже выбираем стандартные кодеки ulaw alaw gsm, 9 - по завершению нажимаем на Apply config, ждем обновления конфигурации.

Переходим на закладку SIP legacy setting:

1,2 -меняем стандартные значения портов на свои, 3 - пусть будет, пригодится, 4 - для безопасности  при подборах паролей.
1,2 -меняем стандартные значения портов на свои, 3 - пусть будет, пригодится, 4 - для безопасности при подборах паролей.

Так же меняем стандартное значение порта на вкладке PJSIP, все сохраняем и применяем ( submit & apply config).

Настраиваем extension (это номера для подключения абонентов):

настроили абонентов, Submit & Apply config.

Настройка trunk для модема:

6 -  максимальное число каналов - ставим 1
6 - максимальное число каналов - ставим 1

Маршрут для входящих звонков:

Маршрут для исходящих звонков:

Шаблоны для набранных номеров, здесь можно ограничить направление звонков, местные, межгород, международные. Звонок будет работать, если совпадет с набранным шаблоном.
Шаблоны для набранных номеров, здесь можно ограничить направление звонков, местные, межгород, международные. Звонок будет работать, если совпадет с набранным шаблоном.

С графической частью закончили. Главное не забывать после каждых действий Submit & Apply Config, что бы внесенные изменения сохранялись.

Подключение GSM модема

Здесь собранная копипаста из разных источников:

Тестированные модемы:
 Предварительно на компьютере перевести GSM-модем в режим работы «только модем» ( AT^U2DIAG=0 ) и в режим только 2G (AT^SYSCFG=13,1,3fffffff,0,0 ) с помощь Putty или любым другим способом, можно и через raspberry, но и так здесь большой объем.

  • E171 - ОК

  • E1550 - ОК

  • E150 - Голос только в одну сторону

  • E3131 - Работают не стабильно, при исходящих голос только в одну сторону

  • E352 - Нет голосовых функций 

    Подключаем GSM-модем HUAWEI E1550, проверяем появились ли устройства ttyUSB*:

ls -al /dev | grep ttyUSB
полученный ответ команды

crw-rw---- 1 root dialout 188, 0 фев 27 11:34 ttyUSB0

crw-rw---- 1 root dialout 188, 1 фев 27 11:33 ttyUSB1

crw-rw---- 1 root dialout 188, 2 фев 27 12:37 ttyUSB2

Чтобы при подключении GSM-модема, Asterisk имел доступ к нему, необходимо устройству назначить владельца asterisk и группу dialout. Создаем файл (nano /etc/udev/rules.d/92-dongle.rules) со следующим содержимым:

KERNEL=="ttyUSB*", MODE="0666", OWNER="asterisk", GROUP="dialout"

Перезагружаем raspberry командой reboot и переподключаемся к атс с помощью Putty.

Настройка файла dongle.conf ( nano /etc/asterisk/dongle.conf ) - проверяем параметр exten и прописываем imei модема, остальное можно не трогать:

initstate=start                 ; specified initial state of device, must be one of 'stop' 'start' 'remote'
                                ;   'remove' same as 'disable=yes'

exten=+79234567890   ; Проверяем наш номер сим карты, он связан с ранее настроенными

dtmf=relax                      ; control of incoming DTMF detection, possible values:
                                ;   off    - off DTMF tones detection, voice data passed to asterisk unalte$
                                ;              use this value for gateways or if not use DTMF for AVR or in$
                                ;   inband - do DTMF tones detection
                                ;   relax  - like inband but with relaxdtmf option
                                ;  default is 'relax' by compatibility reason

; dongle required settings
[dongle0]
audio=/dev/ttyUSB1              ; tty port for audio connection; no default value
data=/dev/ttyUSB2               ; tty port for AT commands; no default value

imei=351234567898991  ; ПРОПИСЫВАЕМ IMEI модема
;imsi=123456789012345

В консоли asterisk, перечитываем конфигурацию chan_dongle и проверяем состояние устройства. Если все было сделано правильно, то подключение к GSM-модему будет осуществлено и устройство будет отображаться в консоли asterisk:

root@raspbx:/# asterisk -rv
raspbx*CLI> dongle show devices

ID           Group State      RSSI Mode Submode Provider Name  Model      Firmware          IMEI             IMSI             Number
dongle0      0     Free       26   3    3       MegaFon        E1550      11.608.14.15.311  35*******  2**************3  Unknown

Настройка файла конфигурации для звонков и смс (настраивал только входящие смс с переадресацией на настроенный extension) :

nano /etc/asterisk/extensions_custom.conf

[from-trunk-dongle]
; можно раскомментировать, тогда смс будут записываться в файл sms.txt, смотреть потом в логах
;exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} ${BASE64_DECODE(${SMS_BASE64})})
;exten => sms,n,Set(FILE(/var/log/asterisk/sms.txt,,,a)=${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLE$
;exten => sms,n,System(echo >> /var/log/asterisk/sms.txt)
;exten => sms,n,Hangup()

exten => _.,1,Set(CALLERID(name)=${CALLERID(num)})
exten => _.,n,Goto(from-trunk,${EXTEN},1)

; закомментировать, если выше было раскомментировано
exten => sms,1,Set(MESSAGE(body)=${BASE64_DECODE(${SMS_BASE64})})
exten => sms,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME} - ${CALLERID(num)}: ${$
exten => sms,n,Set(MESSAGE(from)="${CALLERID(num)}" <${CALLERID(num)}>)
exten => sms,n,Set(CALLERID(name)=${CALLERID(num)})
exten => sms,n,Verbose(1,${MESSAGE(from)})
exten => sms,n,MessageSend(sip:250,${MESSAGE(from)}) ; Прописываем куда пересылать входящие сообщения - на номер 250
exten => sms,n,Hangup()


exten => ussd,1,Verbose(Incoming USSD: ${BASE64_DECODE(${USSD_BASE64})})
exten => ussd,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME}: ${BASE64_DECODE(${US$
exten => ussd,n,Hangup()

Сохраняем, перезагружаем, пробуем звонить (входящие исходящие) и присылать смс. Должно все работать.
Пробовал вариант подключения смартфона по Bluetooth в качестве шлюза, все работало, но качество связи было ужасное, причем на стороне астериска (голос сильно искаженный, скорее всего из-за кодеков блютуза, настроить не смог).

Настраиваем защиту asterisk freepbx с помощью fail2ban (By @UKVoIPForums )

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

install-fail2ban 

Создаем тюрьму:

sudo nano /etc/fail2ban/jail.local
Копируем содержимое в файл jail.local
[DEFAULT]
# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not                          
# ban a host which matches an address in this list. Several addresses can be                             
# defined using space separator.
                                                                         
ignoreip = 127.0.0.1/8 ::1

[asterisk]
enabled   = true
filter    = asterisk
action    = iptables-asterisk[name=asterisk]
logpath   = /var/log/asterisk/security_log
bantime   = 31536000
findtime  = 86400
maxretry  = 3

[sshd]
enabled   = true
port      = ssh
filter    = sshd
logpath   = /var/log/auth.log
banaction = iptables-allports
bantime   = 31536000
findtime  = 86400
maxretry  = 3

[freepbx]
enabled   = true
port      = http,https
filter    = freepbx
logpath   = /var/log/asterisk/freepbx_security.log
bantime   = 31536000
findtime  = 86400
maxretry  = 3

Еще 6 файлов конфигурации нужно создать/изменить, поехали:

sudo nano /etc/fail2ban/filter.d/freepbx.conf
1. Копируем содержимое в файл freepbx.conf
[INCLUDES]
before = common.conf

[Definition]
datepattern = ^\[%%Y-%%b-%%d %%H:%%M:%%S\]

failregex = \[freepbx_security\.NOTICE\]: Authentication failure for .* from <HOST>
sudo nano /etc/fail2ban/filter.d/asterisk.conf
2. Копируем содержимое в файл asterisk.conf, на всякий случай скопируйте существующее содержимое оригинального файла
# Fail2Ban configuration file
#
#
# $Revision: 251 $
#

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf

[Definition]

#_daemon = asterisk

# Option:  failregex
# Notes.:  regex to match the password failures messages in the logfile. The
#          host must be matched by a group named "host". The tag "<HOST>" can
#          be used for standard IP/hostname matching and is only an alias for
#          (?:::f{4,6}:)?(?P<host>\S+)
# Values:  TEXT
#
# Asterisk 1.8 uses Host:Port format which is reflected here

failregex = NOTICE.* .*: Registration from '.*' failed for '<HOST>:.*' - Wrong password
        NOTICE.* .*: Registration from '.*' failed for '<HOST>:.*' - No matching peer found
        NOTICE.* .*: Registration from '.*' failed for '<HOST>:.*' - Username/auth name mismatch
        NOTICE.* .*: Registration from '.*' failed for '<HOST>:.*' - Device does not match ACL
        NOTICE.* .*: Registration from '.*' failed for '<HOST>:.*' - Peer is not supposed to register
        NOTICE.* .*: Registration from '.*' failed for '<HOST>:.*' - ACL error (permit/deny)
        NOTICE.* .*: Registration from '.*' failed for '<HOST>:.*' - Device does not match ACL
        NOTICE.* .*: Registration from '\".*\".*' failed for '<HOST>:.*' - No matching peer found
        NOTICE.* .*: Registration from '\".*\".*' failed for '<HOST>:.*' - Wrong password
        NOTICE.* <HOST> failed to authenticate as '.*'$
        NOTICE.* .*: No registration for peer '.*' \(from <HOST>\)
        NOTICE.* .*: Host <HOST> failed MD5 authentication for '.*' (.*)
        NOTICE.* .*: Failed to authenticate user .*@<HOST>.*
        NOTICE.* .*: <HOST> failed to authenticate as '.*'
        NOTICE.* .*: <HOST> tried  to authenticate with nonexistent user '.*'
        SECURITY.* .*: SecurityEvent="ChallengeSent",.*,.*,Service="(PJ)?SIP",.*,AccountID="<unknown>",.*,.*,RemoteAddress=".*/.*/<HOST>/.*",Challenge=""
        SECURITY.* .*: SecurityEvent="InvalidAccountID",.*,Severity="Error",Service="(PJ)?SIP",.*,.*,.*,.*,RemoteAddress=".*/.*/<HOST>/.*"
        SECURITY.* .*: SecurityEvent="ChallengeResponseFailed",.*,Severity="Error",Service="(PJ)?SIP",.*,.*,.*,.*,RemoteAddress=".*/.*/<HOST>/.*",.*
        SECURITY.* .*: SecurityEvent="InvalidPassword",.*,Severity="Error",Service="(PJ)?SIP",.*,.*,.*,.*,RemoteAddress=".*/.*/<HOST>/.*",.*

# Option:  ignoreregex
# Notes.:  regex to ignore. If this regex matches, the line is ignored.
# Values:  TEXT
#
sudo nano /etc/fail2ban/action.d/iptables-asterisk.conf
3. Копируем содержимое в файл iptables-asterisk.conf (в файле по аналогии добавляем наши кастомные порты, которые мы прописывали в настройках SIP)
# Fail2Ban configuration file
#
# Author: Razvan Turtureanu
#
# $Revision$
#

[Definition]

# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
#
actionstart = iptables -N fail2ban-<name>
          iptables -A fail2ban-<name> -j RETURN
          iptables -I <chain> -p tcp --dport 5061 -j fail2ban-<name>
          iptables -I <chain> -p udp --dport 5060 -j fail2ban-<name>
          iptables -I <chain> -p tcp --dport 5060 -j fail2ban-<name>

# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#
actionstop = iptables -D <chain> -p tcp --dport 5061 -j fail2ban-<name>
         iptables -D <chain> -p udp --dport 5060 -j fail2ban-<name>
         iptables -D <chain> -p tcp --dport 5060 -j fail2ban-<name>
         iptables -F fail2ban-<name>
         iptables -X fail2ban-<name>

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck = iptables -n -L <chain> | grep -q fail2ban-<name>

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP

# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    <ip>  IP address
#          <failures>  number of failures
#          <time>  unix timestamp of the ban time
# Values:  CMD
#
actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP

[Init]

# Defaut name of the chain
#
name = default

# Option:  chain
# Notes    specifies the iptables chain to which the fail2ban rules should be
#          added
# Values:  STRING  Default: INPUT
chain = INPUT
sudo nano /etc/asterisk/logger_logfiles_custom.conf
4. Копируем содержимое в файл logger_logfiles_custom.conf
security_log => SECURITY,NOTICE
sudo nano /etc/logrotate.d/asterisk-security
5. Копируем содержимое в файл asterisk-security
/var/log/asterisk/security_log {
        weekly
        missingok
        rotate 4
        size 2000k
        sharedscripts
        create 0664 asterisk asterisk
        su asterisk asterisk
        postrotate
                /usr/sbin/invoke-rc.d asterisk logger-reload > /dev/null 2> /dev/null
        endscript
}
sudo nano /etc/logrotate.d/freepbx-security
6. Копируем содержимое в файл freepbx-security
/var/log/asterisk/freepbx_security.log {
        weekly
        missingok
        rotate 4
        size 2000k
        sharedscripts
        create 0664 asterisk asterisk
        su asterisk asterisk
        postrotate
                /usr/sbin/invoke-rc.d asterisk logger-reload > /dev/null 2> /dev/null
        endscript
}

Перезагружаемся - reboot

Памятка для работы с fail2ban:

File permissions and owner/group information:

/etc/fail2ban/jail.local = rw-r--r-- root root
/etc/fail2ban/filter.d/freepbx.conf = rw-r--r-- root root
/etc/fail2ban/filter.d/asterisk.conf = rw-r--r-- root root
/etc/fail2ban/action.d/iptables-asterisk.conf = rw-r--r-- root root
/etc/asterisk/logger_logfiles_custom.conf = rw-rw-r-- asterisk asterisk
/etc/logrotate.d/asterisk_security = rw-r--r-- root root

Basic Fail2ban commands:
Start Fail2ban = sudo service fail2ban start
Stop Fail2ban = sudo service fail2ban stop
Restart Fail2ban = sudo service fail2ban restart

Get the current status of an individual jail:
FreePBX status = sudo fail2ban-client status freepbx
Asterisk status = sudo fail2ban-client status asterisk
SSHD status = sudo fail2ban-client status sshd

Ban/Unban IP addresses:
Ban IP = sudo fail2ban-client set *YOURJAILNAMEHERE banip IPADDRESSHERE
Unban IP = sudo fail2ban-client set *YOURJAILNAMEHERE unbanip IPADDRESSHERE

*Replace YOURJAILNAMEHERE with freepbx, asterisk or sshd.
*Replace IPADDRESSHERE with the IP address that you want to ban/unban.

Настройка Mikrotik

Нужно настроить проброс портов и защиту.

IP-Firewall-жмем на плюсик:

Создаем ловушки на стандартных портах и рядом стоящих для сканировщиков и отправляем в бан. Правило копируем и делаем для tcp, так же можно сделать для других стандартных портов 22,3389,8291
Делаем проброс портов во вкладке NAT (номера, которые прописывали в SIP настройках FreePBX), копируем и пишем диапазон RTP по аналогии
Проброс портов для работы абонентов в локальной сети

Так же можно/желательно делать перезагрузку системы в автоматическом режиме по расписанию, для сброса зависшего модема, у меня завис через 5 дней работы, а рядом никого, пришлось ехать и в ручную перегружать.

crontab -e
# пишем в конце файла (перезагрузка каждый день в 5 утра)
0 5 * * * /sbin/reboot

Желательно настроить VPN доступ к микротику из интернета, для разруливания внештатных ситуаций по случайно забаненым адресам абонентов.

Долго собирал из разных источников эту работающую инструкцию. Может кому то и поможет.

Источник: https://habr.com/ru/post/550734/


Интересные статьи

Интересные статьи

Рано или поздно любая задача, над которой ты работал много лет, вложил душу и время, перестает быть актуальной. Руководство перебрасывает бюджет и ресурсы на горячие напр...
Если вам нужны маленькие Arduino-платы для DIY-проектов, эта статья как раз кстати. Вы хотите создать носимый девайс на базе Arduino, но оригинальная плата слишком большая? Или есть на ...
На днях Raspberry Pi Foundation представила новое устройство, которое получило название Raspberry Pi 400, о чем сообщалось на Хабре. В отличие от прошлых систем, это не совсем одноплатн...
Привет, Хабр. Во второй части были рассмотрены 5 способов использования Raspberry Pi. И как оказывается, этот микрокомпьютер способен ещё на большее. Сейчас мы рассмотрим еще 5, надеюсь, не ме...
C++ — язык запутанный, и существенным его недостатком является сложность создания изолированных блоков кода. В типовом проекте всё зависит от всего. Эта статья показывает, как писать высокоизолир...