Tuesday, March 2, 2021

Install Asterisk 18 / FreePBX 15 on Ubuntu [Raspberry PI 4]

An updated version of this post is available here.

I use this tutorial to setup RemoSIM.com product for customers.




RasPBX – Asterisk for Raspberry Pi is a cost-effective solution to run Asterisk VoIP server on RaspberryPi. The installation is very simple and the web interface makes it super easy to run a low-cost VoIP solution.

I installed RasPBX and used its chan_dongle module to route calls / SMS from sim cards to VoIP/Telegram. It was a long story described in this post to eventually reach a stable setup. 

The setup I had with RasPBX was constantly resetting usb dongles say every 10-30mins. I decided to switch to Ubuntu for Raspberry Pi and install the latest version of Asterisk 18.x instead of Asterisk 16.x provided in RasPBX/Ubuntu and interestingly enough, it resolved the problem I had with disconnecting USB dongles.

So in this post, I'm going to describe how to install Asterisk 18.x with chan_dongle module, Fail2Ban and FreePBX on Ubuntu for RaspberryPI and use CLI interface to configure it.

 Steps:

Download Ubuntu 20.04 LTS for Raspberry PI and burn the image to your microsd card. 

SSH to your raspberry: ssh ubuntu@raspberry.IP.Address , the default password is ubuntu

# update packages there 
sudo apt-get update && sudo apt-get dist-upgrade -y

#freepbx installation must be run as root, so login to SSH as root :

sudo passwd
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
service sshd restart

# Install required packages, note that freepbx 15 does not support php v7.4 so we have to install php v7.3
add-apt-repository universe

add-apt-repository ppa:ondrej/php

apt-get install -y build-essential linux-headers-`uname -r` openssh-server apache2 mariadb-server mariadb-client bison flex php7.3 php7.3-curl php7.3-cli php7.3-pdo php7.3-mysql php-pear php7.3-gd php7.3-mbstring php7.3-intl php7.3-xml  curl sox libncurses5-dev libssl-dev mpg123 libxml2-dev libnewt-dev sqlite3 libsqlite3-dev pkg-config automake libtool autoconf git unixodbc-dev uuid uuid-dev libasound2-dev libogg-dev libvorbis-dev libicu-dev libcurl4-openssl-dev libical-dev libneon27-dev libsrtp2-dev libspandsp-dev sudo subversion libtool-bin python-dev unixodbc dirmngr sendmail-bin sendmail htop cmake libmariadb-dev-compat

# or on Raspbian OS:
apt-get install -y build-essential raspberrypi-kernel-headers openssh-server apache2 mariadb-server mariadb-client bison flex php7.3 php7.3-curl php7.3-cli php7.3-pdo php7.3-mysql php-pear php7.3-gd php7.3-mbstring php7.3-intl php7.3-xml  curl sox libncurses5-dev libssl-dev mpg123 libxml2-dev libnewt-dev sqlite3 libsqlite3-dev pkg-config automake libtool autoconf git unixodbc-dev uuid uuid-dev libasound2-dev libogg-dev libvorbis-dev libicu-dev libcurl4-openssl-dev libical-dev libneon27-dev libsrtp2-dev libspandsp-dev sudo subversion libtool-bin python-dev unixodbc dirmngr sendmail-bin sendmail htop cmake libmariadb-dev-compat




# install nodejs
curl -sL https://deb.nodesource.com/setup_12.x | bash -
apt-get install -y nodejs
curl https://www.npmjs.com/install.sh | sh

# required pear package
pear install Console_Getopt

#add the following line to [mysqld] section of /etc/mysql/mariadb.conf.d/50-server.cnf
sql_mode=NO_ENGINE_SUBSTITUTION

# restart mariadb
service mariadb restart

# download and install asterisk 18
cd /usr/src
rm -fr asterisk-18-current.tar.gz
wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-18-current.tar.gz
tar xvfz asterisk-18-current.tar.gz
cd asterisk-18.*
contrib/scripts/get_mp3_source.sh
contrib/scripts/install_prereq install
./configure --with-pjproject-bundled --with-jansson-bundled
make menuselect.makeopts
menuselect/menuselect --enable app_macro --enable format_mp3 menuselect.makeopts
make
make install
make config
ldconfig
update-rc.d -f asterisk remove

# correct asterisk permissions
useradd -m asterisk
chown asterisk. /var/run/asterisk
chown -R asterisk. /etc/asterisk
chown -R asterisk. /var/{lib,log,spool}/asterisk
chown -R asterisk. /usr/lib/asterisk
rm -rf /var/www/html
mkdir /var/www/html # freepbx reinstall needed

# update apache config
sed -i 's/\(^upload_max_filesize = \).*/\120M/' /etc/php/7.3/apache2/php.ini
cp /etc/apache2/apache2.conf /etc/apache2/apache2.conf_orig
sed -i 's/^\(User\|Group\).*/\1 asterisk/' /etc/apache2/apache2.conf
sed -i 's/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
a2enmod rewrite
service apache2 restart

cd /usr/src
wget https://dlm.mariadb.com/1489919/Connectors/odbc/connector-odbc-3.1.11/mariadb-connector-odbc-3.1.11-ga-src.tar.gz
tar -zxvf mariadb-connector-odbc-3*
cd mariadb-connector-odbc-3*
mkdir build
cd build
DM_DIR=/usr/lib/aarch64-linux-gnu cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONC_WITH_UNIT_TESTS=Off -DCMAKE_C_FLAGS_RELWITHDEBINFO="-I/usr/include/mariadb -L/usr/lib"
## OR on raspbian:
DM_DIR=/usr/lib/arm-linux-gnueabihf cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCONC_WITH_UNIT_TESTS=Off -DCMAKE_C_FLAGS_RELWITHDEBINFO="-I/usr/include/mariadb -L/usr/lib"
##
make
make install

# Configure ODBC

cat <<EOF > /etc/odbcinst.ini
[MySQL]
Description = ODBC for MySQL (MariaDB)
Driver = /usr/local/lib/mariadb/libmaodbc.so
FileUsage = 1
EOF


cat <<EOF > /etc/odbc.ini
[MySQL-asteriskcdrdb]
Description = MySQL connection to 'asteriskcdrdb' database
Driver = MySQL
Server = localhost
Database = asteriskcdrdb
Port = 3306
Socket = /var/run/mysqld/mysqld.sock
Option = 3
EOF

# Download and install freepbx which requires php7.3
cd /usr/src
wget http://mirror.freepbx.org/modules/packages/freepbx/freepbx-15.0-latest.tgz
tar vxfz freepbx-15.0-latest.tgz
touch /etc/asterisk/{modules,cdr}.conf
cd freepbx
./start_asterisk start
./install -n
fwconsole ma disablerepo commercial
fwconsole ma install callrecording core dashboard customappsreg infoservices logfiles pm2 sipsettings voicemail 
fwconsole reload

# Installing chan_dongle
apt-get install usb-modeswitch -y
cd /usr/src
git clone https://github.com/wdoekes/asterisk-chan-dongle
cd asterisk-chan-dongle
./bootstrap
./configure --with-astversion=18.5 --with-asterisk=/usr/src/asterisk-18.5.0/include

# I have noticed that using SIM memory card as the default storage for SMS causes text messages to not receive every once in a while. So set chan_dongle to use dongle memory for SMS by default:
##sed -i at_command.c -e 's#AT+CPMS=\\"SM\\",\\"SM\\",\\"SM\\"#AT+CPMS=\\"ME\\",\\"ME\\",\\"ME\\"#g'

# fix the "dongle reset dongleX" command for E171 firmware  , this dongle needs to be reset using AT^RESET instead of AT^CFUN=1,1 so we send both commands to the modem

##sed -i at_command.c -e 's#AT+CFUN=1,1\\r#AT+CFUN=1,1\\rAT^RESET\\r#g'

make
make install

# chown ttyUSB* devs to asterisk
echo 'KERNEL=="ttyUSB*", GROUP="dialout", OWNER="asterisk"' > /etc/udev/rules.d/50-udev-default.rules

# Freepbx startup script: /etc/systemd/system/freepbx.service
[Unit]
Description=FreePBX VoIP Server
After=mariadb.service
 
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/fwconsole start -q
ExecStop=/usr/sbin/fwconsole stop -q
 
[Install]
WantedBy=multi-user.target

# Enable swap area on Raspberry 
dd if=/dev/zero of=/swapfile bs=1G count=1
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

# add the following line to /etc/fstab
/swapfile swap swap defaults 0 0

# delete journald folder so that it stores logs to RAM to avoid high disk io
rm -fr /var/log/journal

# compile atinout command 
cd /usr/src
wget http://sourceforge.net/projects/atinout/files/v0.9.1/atinout-0.9.1.tar.gz
tar -zxvf atinout*.gz
cd atinout-0.9.1
sed -i Makefile -e "s,-Werror,,g"
make
make install
mkdir /scripts

# add this script to send commands to all dongles {0..4} , 4 should be replaced by the number of dongles you have minus one
# vi /scripts/at
#!/bin/bash

dataports=`for i in {0..4}; do asterisk -rx "dongle show device state dongle$i" | grep "Device\|USB" | grep Data | awk '{print $3}'; done`

for data in $dataports; do
echo "$1" | timeout 1 atinout - $data - 2>&1
done

# add this script to remove all sms messages from sim cards / dongles : 
# vi /scripts/deletesms.sh
#!/bin/bash
# Storage types: https://www.developershome.com/sms/cpmsCommand.asp
# Reading commands: https://www.developershome.com/sms/cmgrCommand2.asp
# Delivery reports: https://stackoverflow.com/questions/18251903/how-to-handle-delivery-report-in-gsm-modem
/scripts/at AT+CPMS=\"SR\",\"SR\",\"SR\"
/scripts/at AT+CMGD=1,4
/scripts/at AT+CPMS=\"MT\",\"MT\",\"MT\"
/scripts/at AT+CMGD=1,4
/scripts/at AT+CPMS=\"ME\",\"ME\",\"ME\"
/scripts/at AT+CMGD=1,4
/scripts/at AT+CPMS=\"SM\",\"SM\",\"SM\"
/scripts/at AT+CMGD=1,4

# make them executable
chmod +x /scripts/at
chmod +x /scripts/deletesms.sh

# add deletesms.sh to cronjobs to avoid this bug 
# crontab -e
0 */6 * * * /scripts/deletesms.sh

# Adding a useful alias command: s
# typing s in shell would show status of all dongles with a refresh rate of 1 second:
# vi ~/.bashrc
alias s="watch -n1 'asterisk -rx \"dongle show devices\"'

# s alias command: 
# keep an eye on the RSSI column there which shows signal strength. RSSI values above 15 are good.
# a low RSSI below 15 might result in dongle reset and constant "GSM not registered" error


# add the following cron jobs to keep raspberrypi updated
apt-get install rpi-eeprom -y
crontab -e
0 14 * * * apt-get update && apt-get dist-upgrade -y
0 17 * * * /usr/sbin/fwconsole ma refreshsignatures && /usr/sbin/fwconsole ma upgradeall
30 17 * * * /usr/sbin/fwconsole setting SIGNATURECHECK 0
30 17 * * * /usr/sbin/fwconsole setting MODULEADMINEDGE 1
0 18 * * * /usr/bin/rpi-eeprom-update -a


# The following script can be used to monitor your local VPN network (10.0.1.1 here)  and restart the vpn service (wireguard in this example) if the connection was not available. 
# vi /scripts/nointernet.sh
#!/bin/bash
if ping -c 1 10.0.1.1 &> /dev/null
then
  echo 1
else
  echo 0
  /bin/systemctl restart wg-quick@wg0
  /bin/systemctl restart wg-quick@wg1
  sleep 5
  /scripts/tg.py --sender 'System' --dongle 'system' --destination 'system' --message "There was no internet connection, the VPN service was restarted"

fi

# The following scripts can be used to monitor number of dongles you have ( here 5 ) and restart them if they were disconnected
# vi /scripts/down.sh

#!/bin/bash
numdongles=5
downs=$(/usr/sbin/asterisk -rx "dongle show devices" | grep "Not connec\|GSM not\|Unknown" | awk '{print $1}')
now=$(date +'%m-%d-%Y')

# reset down dogles
for down in $downs; do
        /scripts/tg.py --sender 'system' --dongle 'system' --destination 'system' --message "dongle $down was down and it was reset";
        /usr/sbin/asterisk -rx "dongle reset $down";
#        /usr/sbin/asterisk -rx "dongle restart now $down";
done

# need to reset raspberry if all dongles are in Not connected state :
downs=$(/usr/sbin/asterisk -rx "dongle show devices" | grep "Not connec" | awk '{print $1}' | wc -l)
if [ $downs -eq $numdongles ]; then
        /scripts/tg.py --sender 'system' --dongle 'system' --destination 'system' --message "all donlges were down and the pi was restarted";
        /bin/dmesg > /scripts/log/dmesg-$now.txt
        /scripts/restart.sh
fi

# need to reset raspberry if one of dongles is disconnected
notconnected=$(/usr/bin/lsusb | grep Huawei | wc -l)
if [ $notconnected -ne $numdongles ]; then
        /scripts/tg.py --sender 'system' --dongle 'system' --destination 'system' --message "one of dongles was not listed in lsusb and the dongle was restarted";
        /bin/dmesg > /scripts/log/dmesg-$now.txt;
        /scripts/restart.sh
fi

# The following script can be used to restart raspberry  / hubs 
# vi /scripts/restart.sh
#!/bin/bash
# restart raspberry 
#echo b > /proc/sysrq-trigger
# or restart the usb hub
hubs=`uhubctl --vendor 05e3 | grep status | awk '{print $5}'`;
for hub in $hubs;
 do uhubctl --loc $hub --action 2;
done
sleep 5
/usr/sbin/asterisk -rx "dongle discovery"

# Add the above monitoring scripts, and also two cron jobs to remove raspberry logs when they become large
# crontab -e
*/5 * * * * /scripts/nointernet.sh
*/10 * * * * /scripts/down.sh
0 * * * * /usr/bin/find /var/log/ -type f -size +500M  -delete
0 * * * * /usr/bin/find /home/ -type f -size +500M  -delete


# Restart raspberrypi
reboot


# After reboot, set your GSM dongles to Modem-only:
/scripts/at AT^U2DIAG=0
/scripts/at AT^SETPORT=\"A1,A2\;1,3,2,A2\" #for E171 Firmware 21.156.00.00.143 (requires { 0x12d1, 0x1506, { 2, 1, /* 0 */ } }, in pdiscovery.c of asterisk-chan-dongle) , disables cdrom and network adapter

#/scripts/at AT^SETPORT=\"A1,A2\;1,16,3,2,A2\" #for E171 Firmware 21.156.00.00.143 (requires { 0x12d1, 0x1506, { 3, 2, /* 0 */ } }, in pdiscovery.c of asterisk-chan-dongle) , disables cdrom

How to export Apple Health / Google Fit training activity to TCX format

  I own a Xiaomi Smart Band 7, and recently, my Mi Fitness app stopped syncing running activities to Strava. Mi Fitness supports syncing dat...