📜 ⬆️ ⬇️

Automatic configuration of Cisco voice gateways and Eltex Asterisk provisioning

In the company where I work, it was decided to transport all our stores, and there are almost 500 of them, to IP telephony. The central and regional offices have long been using Panasonic solutions for this purpose. At the headquarters, this is the KX-NS1000, in the regional offices, mainly the KX-NS500.

But for the transfer of stores to ip-telephony, it was decided to use Asterisk.

If you are interested in how to implement “Asterisk provisioning” using bash scripts that work with mysql, how to configure Asterisk RealTime, dhcp, tftp, as well as the process of generating and signing an ssl certificate signing request for https provisioning from Cysco - welcome to the cat !
')
In the next article, a little about setting up the KX-NS1000 and KX-NS500 to communicate with Asterisk.

Terms of Reference:


  1. Automatic configuration should completely eliminate the participation of a person in the configuration of both the gateways and Asterisk itself. The configuration process should include only “binding” the ports of the gateway to a specific phone number.
  2. Setup should be done through a single web interface.
  3. At any time, any gateway can be removed, the phone number is transferred from one gateway to another, or the port on the gateway is reassigned.
  4. Ability to expand the list of supported equipment

Choice of voice gateways:
The main criterion when choosing a gateway was the correct operation of the automatic setup. there will be many gateways, they need to be configured. At any time, any gateway can fail and a new gateway will come in its place or, even worse, the gateway can move from one store to another, the internal telephone number on the gateway itself or the port number used can change.

Naturally, we tested all gateways on test store mice for several months, fought with codecs, providers and qos parameters, set up our own border gateways, etc.

We chose several gateways from Eltex, and Cisco.
** Eltex: **
Pros - I used to work with him, good gateways, a lot of things they can do, there is a wonderful OpenWRT on board,
true sense of this greatness zero
The manufacturer does not post the source code for the drivers as required by the OpenWRT license and, accordingly, it will not work to assemble your firmware version, as well as install some third-party package.

Disadvantages: extremely inconvenient automatic configuration, for example, you cannot make one common file for all gateways and put individual settings into separate files.

On different firmware versions, gateways request the configuration file in different ways. somewhere poppy is written with dots, where together. Eight port TAU-8.IP pulls the configuration file only from the server tftp root. For this reason, a full-fledged automatic configuration for these gateways did not work out; you have to go to the gateway's WEB interface and specify the path to the tftp server. Moreover, TAU-2M.IP perfectly understands the variables in the path to the file:
tftp://10.0.15.9/Eltex/$PN/config/$MA.tar.gz can not be said about TAU-8.IP where you have to write the MAC address of the gateway on the way.

In the WEB interface, autotuning added the generation of this path after configuration. I understand perfectly well that if you specify all the parameters for 43 options in the DHCP server, then everything will work, but this option is busy with us.

** Cisco **
Pros - I also took off before working, great gateways, with a great automatic configuration system.

Cons: no QOS settings.

In the end, we decided to use the SPA112, because we were offered a very tasty price tag plus we use equipment from Cisco almost everywhere.

The mechanism for creating configuration files


Asterisk takes a list of sip users in the mysql database, this mechanism is called Asterisk RealTime . This allows you to create / delete / edit accounts for SIP clients on the fly without forcing Asterisk to re-read sip.conf or users.conf.

The table from which Asterisk takes sip accounts is called " sip_users " and it is almost standard for Asterisk RealTime.

The tables in which the user binds the voice gateway to the sip account are called “redaction_gateway_and_phone” and “gateway_and_phone_info”:

redaction_gateway_and_phone
download in sql format
- “mac” - poppy gateway address
- “name” - sip username is the same phone number
- “port_id” - physical port number on the VoIp-gateway. i.e. the port where the analog telephone will be connected
- “actual” - a flag indicating whether it is necessary to create / rewrite the configuration file and an entry in the sip_users table
And the date of adding, editing:
- “data” - the date of record creation / editing is automatically added

gateway_and_phone_info
download in sql format
gateway_and_phone_info is needed to add information on which gateway is configured, it also indicates the region where the gateway is located, the identification number of the store and the account name from AD of the person who configured / edited.
- “region” - for the convenience of determining which region the outgoing call is from (see below for details).
- “model” is the gateway model, all models are stored in the other table
- “cfu” is the identification number of the store
- “region” is the region where the gateway is located city, village, etc.
- “last_modified” - Before entering the WEB interface, Apache asks for a name / pass, which then checks with the group in AD.
- “mac” - poppy

If desired, you can combine both tables into one. I divided them so as not to produce a bunch of records with the same fields in the same table.

That is, the first table describes the accounts for adding sip_users to the table, and the second describes which gateway model will be configured and where it stands.

The whole logic of setting up gateways is that the user enters the mac-address of the voice gateway, the port number used and the desired phone number in both these tables, or rather via the web interface, and the two scripts create the corresponding entry in the “sip_users” table and generate configuration files. Thus, at the same time, a sip account is created for Asterisk and the corresponding configuration file in the server's tftp directory for the hardware with the given mac-address.



The database processing logic and the configuration file creation logic are divided into two parts. They are independent of each other and allow, by creating new scripts, to auto-tune for any other gateways / phones.

The script AutoProvision_all.sh is responsible for working with the database. First of all, the script polls the gateway_and_phone table for records with a unique mac-address and with the No flag in the actual field. That is, it finds gateways / phones that are not relevant from the point of view of Asterisk, where one account will be configured for one voice gateway (or VoIp-phone). Next, the script changes the flag from No to Yes in the actual field, and in turn outputs data for each record while simultaneously creating an entry in the sip_users table.

Then the script polls the gateway_and_phone_info table for the presence of records with the No flag in the actual field and duplicate mac-addresses. That is, it finds gateways / phones that are not relevant from the point of view of Asterisk, where several accounts will be configured for one voice gateway (or VoIp-phone). And it also changes the flag, creates a record and displays the data.

At the output, the script produces the lines of the form: duplication flag ; mac-address ; username ; port number ; password

eg:

duble;00da55b729e8;8888;1;2DJKjH3XTx1osjI1
no_duble;00da55b729e8;8888;1;d5xfDwKG3UNdywgY


The password is generated by a separate passGen.sh script.

AutoProvision_all.sh
 #!/bin/bash #echo START ROOT_PATH=$(cd $(dirname $0) && pwd) #  ,   . #  "sql"        . sql='mysql -uprovisioning -pxkYyNFuyc3nEKsFj -Dasterisk -e' sql_gateway='redaction_gateway_and_phone' #   ,    - sql_sip_users='sip_users' #    asterisk test="" test=no_test #          .. #    "actual"     Asterisk. #del_old_user="" #del_old_user=delete #         ################################################################################### ################################################################################### ### ### ###          : ### ### ### ################################################################################### ################################################################################### insert_update_sql () { local f_name local d_name for f_name in "$@" do #    : #      , ..    . #        #echo f_name $f_name if [ "$f_name" != "no_duble" ] && [ "$f_name" != "duble" ] then #           "array_users" array_users=($($sql "SELECT mac,name,port_id FROM $sql_gateway WHERE name like '$f_name'"| awk 'NR>1')) mac=${array_users[0]} name=${array_users[1]} secret=$(${ROOT_PATH}/passGen.sh) port_id=${array_users[2]} region=($($sql "SELECT region FROM gateway_and_phone_info WHERE mac like \ (SELECT mac FROM $sql_gateway WHERE name like '$name')"| awk 'NR>1')) model=($($sql "SELECT model FROM gateway_and_phone_info WHERE mac like '$mac'"| awk 'NR>1')) #    "no"  "yes" if [ -n "$test" ]; then $sql "UPDATE $sql_gateway SET actual = 'yes' WHERE name like '$name'" | awk 'NR>1';fi #         "sip_users". #   "REPLACE INTO"   UNIQUE   "name"      . #   ,         .     . #d_name=($(echo -n $@|sed -r 's/[^0-9 ]//gi')) #echo d_name $d_name #$sql "DELETE FROM sip_users WHERE sip_users.name not in ($d_name) and sip_users.name like '${name%?}%'" | awk 'NR>1' if [ -n "$test" ]; then $sql "REPLACE INTO $sql_sip_users\ (id,name,defaultuser,context,secret,region)\ VALUES\ (NULL, '$name', '$name', 'default', '$secret', '$region')" | awk 'NR>1'; fi #        ,  -    . # if [ "${!#}" == "no_duble" ]; then echo no_duble\;$mac\;$name\;$port_id\;$secret; fi if [ "${!#}" == "no_duble" ]; then echo "no_duble"\;"$mac"\;"$name"\;"$port_id"\;"$secret"\;"$model"; fi # if [ "${!#}" == "duble" ]; then echo duble\;$mac\;$name\;$port_id\;$secret; fi if [ "${!#}" == "duble" ]; then echo "duble"\;"$mac"\;"$name"\;"$port_id"\;"$secret"\;"$model"; fi fi done } #################################################################################### #################################################################################### ### *** *** ### #################################################################################### #################################################################################### #        c  "no" array_no_duble_user=($($sql "SELECT name FROM $sql_gateway WHERE mac IN \ (SELECT mac FROM $sql_gateway GROUP BY mac HAVING count(*)=1) and actual like 'no'" | awk 'NR>1')) #echo       - ${#array_no_duble_mac[@]} #echo       -- ${array_no_duble_mac[@]} #     insert_update_sql "${array_no_duble_user[@]}" "no_duble" #     . #   "array_duble_mac"       "no" array_duble_mac=($($sql "SELECT distinct mac FROM $sql_gateway WHERE mac IN \ (SELECT mac FROM $sql_gateway GROUP BY mac HAVING count(*)>1) and actual like 'no'" | awk 'NR>1')) # echo    - ${#array_duble_mac[@]} # echo    -- ${array_duble_mac[@]} #         : for (( duble_mac_num=0; $duble_mac_num<${#array_duble_mac[@]}; duble_mac_num++ )) do #   "array_duble_user"      . array_duble_user=($($sql "SELECT name FROM $sql_gateway WHERE mac like '${array_duble_mac[$duble_mac_num]}'"| awk 'NR>1')) # echo      - ${#array_duble_mac_user[@]} # echo      -- ${array_duble_mac_user[@]} #         "insert_update_sql". insert_update_sql "${array_duble_user[@]}" "duble" done exit 0 

The second script is called “gen_prov.sh”. It launches AutoProvision_all.sh, receives a list of all entries from it, and generates configuration files for SPA112, TAU2-2M.IP and TAU-8.IP. Unfortunately for Elteks, I did not implement the configuration of individual ports. all ports are configured on them and in the WEB interface this is checked.

Also this script is suitable for generating files for PAP2T-na, SPA8000 and theoretically for any voice gateways from Cisco and Linksys.

gen_prov.sh
 #!/bin/bash ROOT_PATH=$(cd $(dirname $0) && pwd) #   .      , #         !!! declare -A array_duble_file_out declare -A array_duble_out_eltex declare -A array_duble_out_eltex_tau8 tmp_dir=`/bin/mktemp -d` #   "array_users"          "flag,mac,name,port,password": array_users=($(${ROOT_PATH}/AutoProvision_all.sh)) #echo array_users--- ${#array_users[@]} #echo   array_users--- ${array_users[@]} #       for (( array_users_num=0; array_users_num<${#array_users[@]}; array_users_num++ )) do #   "array_user_data"    "flag;mac;name;port;password;model": array_user_data=($(echo -n ${array_users[$array_users_num]}|tr -s ';' '\ ' )) # #      : # for (( array_usersc_data_num=0; array_usersc_data_num<${#array_users_data[@]}; array_usersc_data_num++ )) # do flag=${array_user_data[0]} mac=${array_user_data[1]} name=${array_user_data[2]} port=${array_user_data[3]} password=${array_user_data[4]} model=${array_user_data[5]} if [ "$model" == "TAU-2M.IP" ] then #      array_duble_out_eltex[$mac]=$(echo -n "${array_duble_out_eltex[$mac]} $name;$password ") fi ########################################################################### if [ "$model" == "TAU-8.IP" ] then #      array_duble_out_eltex_tau8[$mac]=$(echo -n "${array_duble_out_eltex_tau8[$mac]} $name;$password ") fi ########################################################################### if [ "$model" == "SPA112" ] then mac=$(echo -n ${array_user_data[1]}|sed 's/.*/\L&/') rm -f /srv/tftp/Cisco/config/*$mac\* #         . array_duble_file_out[$mac]=$(echo -en "${array_duble_file_out[$mac]}\n\n<Line_Enable_${port}_ ua=\"na\">Yes</Line_Enable_${port}_> <Display_Name_${port}_ ua=\"na\">$name</Display_Name_${port}_> <User_ID_${port}_>$name</User_ID_${port}_> <Password_${port}_>$password</Password_${port}_>") fi ########################################################################### done #         #    TAU-8.IP for array_duble_out_key_eltex_tau8 in ${!array_duble_out_eltex_tau8[@]}; do cp -R $ROOT_PATH/template/TAU-8.IP/tmp/ $tmp_dir # echo !array_duble_out_eltex_tau8  ..  ${!array_duble_out_eltex_tau8[@]} # echo array_duble_out_eltex_tau8    ${array_duble_out_eltex_tau8[$array_duble_out_key_eltex_tau8]} # echo array_duble_out_key_eltex_tau8    $mac -- $array_duble_out_key_eltex_tau8 mac_tau8=$array_duble_out_key_eltex_tau8 array_users_eltex_tau8=($(echo ${array_duble_out_eltex_tau8[$array_duble_out_key_eltex_tau8]}|tr -s ';' '\ ' )) ######################################################### # FXS1 name_eltex_tau8_1=${array_users_eltex_tau8[0]} password_eltex_tau8_1=${array_users_eltex_tau8[1]} # FXS2 name_eltex_tau8_2=${array_users_eltex_tau8[2]} password_eltex_tau8_2=${array_users_eltex_tau8[3]} # FXS3 name_eltex_tau8_3=${array_users_eltex_tau8[4]} password_eltex_tau8_3=${array_users_eltex_tau8[5]} # FXS4 name_eltex_tau8_4=${array_users_eltex_tau8[6]} password_eltex_tau8_4=${array_users_eltex_tau8[7]} # FXS5 name_eltex_tau8_5=${array_users_eltex_tau8[8]} password_eltex_tau8_5=${array_users_eltex_tau8[9]} # FXS6 name_eltex_tau8_6=${array_users_eltex_tau8[10]} password_eltex_tau8_6=${array_users_eltex_tau8[11]} # FXS7 name_eltex_tau8_7=${array_users_eltex_tau8[12]} password_eltex_tau8_7=${array_users_eltex_tau8[13]} # FXS8 name_eltex_tau8_8=${array_users_eltex_tau8[14]} password_eltex_tau8_8=${array_users_eltex_tau8[15]} ######################################################### /bin/sed "\ # FXS_1 s/%fxs1_phone%/$name_eltex_tau8_1/g;\ s/%fxs1_username%/$name_eltex_tau8_1/g;\ s/%fxs1_auth_name%/$name_eltex_tau8_1/g;\ s/%fxs1_auth_pass%/$password_eltex_tau8_1/g;\ # FXS_2 s/%fxs2_phone%/$name_eltex_tau8_2/g;\ s/%fxs2_username%/$name_eltex_tau8_2/g;\ s/%fxs2_auth_name%/$name_eltex_tau8_2/g;\ s/%fxs2_auth_pass%/$password_eltex_tau8_2/g;\ # FXS_3 s/%fxs3_phone%/$name_eltex_tau8_3/g;\ s/%fxs3_username%/$name_eltex_tau8_3/g;\ s/%fxs3_auth_name%/$name_eltex_tau8_3/g;\ s/%fxs3_auth_pass%/$password_eltex_tau8_3/g;\ # FXS_4 s/%fxs4_phone%/$name_eltex_tau8_4/g;\ s/%fxs4_username%/$name_eltex_tau8_4/g;\ s/%fxs4_auth_name%/$name_eltex_tau8_4/g;\ s/%fxs4_auth_pass%/$password_eltex_tau8_4/g;\ # FXS_5 s/%fxs5_phone%/$name_eltex_tau8_5/g;\ s/%fxs5_username%/$name_eltex_tau8_5/g;\ s/%fxs5_auth_name%/$name_eltex_tau8_5/g;\ s/%fxs5_auth_pass%/$password_eltex_tau8_5/g;\ # FXS_6 s/%fxs6_phone%/$name_eltex_tau8_6/g;\ s/%fxs6_username%/$name_eltex_tau8_6/g;\ s/%fxs6_auth_name%/$name_eltex_tau8_6/g;\ s/%fxs6_auth_pass%/$password_eltex_tau8_6/g;\ # FXS_7 s/%fxs7_phone%/$name_eltex_tau8_7/g;\ s/%fxs7_username%/$name_eltex_tau8_7/g;\ s/%fxs7_auth_name%/$name_eltex_tau8_7/g;\ s/%fxs7_auth_pass%/$password_eltex_tau8_7/g;\ # FXS_8 s/%fxs8_phone%/$name_eltex_tau8_8/g;\ s/%fxs8_username%/$name_eltex_tau8_8/g;\ s/%fxs8_auth_name%/$name_eltex_tau8_8/g;\ s/%fxs8_auth_pass%/$password_eltex_tau8_8/g"\ $ROOT_PATH/template/TAU-8.IP/tmp/etc/config/pbx > $tmp_dir/tmp/etc/config/pbx; sed "s/%mac%/$mac_tau8/g" $ROOT_PATH/template/TAU-8.IP/tmp/etc/config/system > $tmp_dir/tmp/etc/config/system cd $tmp_dir/; tar zcf $mac_tau8.tar.gz tmp; rm -rf /srv/tftp/Eltex/TAU-8.IP/config/$mac_tau8.tar.gz mv $tmp_dir/$mac_tau8.tar.gz /srv/tftp/Eltex/TAU-8.IP/config/ done # end TAU-8.IP #    TAU-2M.IP for array_duble_out_key_eltex in ${!array_duble_out_eltex[@]}; do # echo array_duble_out_eltex  ..  ${!array_duble_out_eltex[@]} # echo array_duble_out_eltex    ${array_duble_out_eltex[$array_duble_out_key_eltex]} mac_tau2=$array_duble_out_key_eltex # 13  mac_tau2_now=$(echo -n $mac | sed -r 's/(..)/\1./gi ; s/.$//gi') #  14  array_users_eltex=($(echo ${array_duble_out_eltex[$array_duble_out_key_eltex]}|tr -s ';' '\ ' )) name_eltex_1=${array_users_eltex[0]} password_eltex_1=${array_users_eltex[1]} name_eltex_2=${array_users_eltex[2]} password_eltex_2=${array_users_eltex[3]} /bin/sed "\ s/%fxs0_Enable%/1/g;\ s/%fxs0_Number%/$name_eltex_1/g;\ s/%fxs0_AuthUsername%/$name_eltex_1/g;\ s/%fxs0_AuthPassword%/$password_eltex_1/g;\ s/%fxs1_Enable%/1/g;\ s/%fxs1_Number%/$name_eltex_2/g;\ s/%fxs1_AuthUsername%/$name_eltex_2/g;\ s/%fxs1_AuthPassword%/$password_eltex_2/g"\ $ROOT_PATH/template/TAU-2M.IP/cfg.yaml > $tmp_dir/cfg.yaml; cd $tmp_dir tar zcf $mac_tau2.tar.gz cfg.yaml; # echo tmp tau2 $mac_tau2 $tmp_dir rm -rf /srv/tftp/Eltex/TAU-2M.IP/config/$mac_tau2.tar.gz cp $tmp_dir/$mac_tau2.tar.gz /srv/tftp/Eltex/TAU-2M.IP/config/ rm -rf /srv/tftp/Eltex/TAU-2M.IP/config/$mac_tau2_now.tar.gz cp $tmp_dir/$mac_tau2.tar.gz /srv/tftp/Eltex/TAU-2M.IP/config/$mac_tau2_now.tar.gz #echo $tmp_dir #echo $mac_tau2 #echo $mac_tau2_now #   TAU-2M.IP" done #    SPA112 for array_duble_file_out_key in ${!array_duble_file_out[@]}; do echo -e "<flat-profile>${array_duble_file_out[$array_duble_file_out_key]}\n\n</flat-profile>" > /srv/tftp/Cisco/config/spa$array_duble_file_out_key.xml done trap "rm -rf $tmp_dir" EXIT INT QUIT ABRT TERM exit 0 

It is not an elegant solution with TAU8, it is asking for a looping cycle. As I wrote above, TAU-8.IP has to be configured with handles, prescribing the path to the configuration file. Two-port TAU-2M.IP also have to be configured with handles, but this is due to the fact that we have 150 and 66 options configured and 43 options on the DHCP server are not configured.

All scripts are in the directory /etc/asterisk/scripts , in the same place you need to create a directory " template " in which should be the configuration file templates for gateways, and for TAU-8.IP also the directory tree. This gateway has several configuration files that are each in its own directory and the whole kitchen is collected into one archive.
gen_prov.sh runs every 1 minute on crown.

The SPA112 gateways out of the box try to download the configuration file from the HTTPS server, respectively, the server should have a valid ssl certificate.

How to generate a request for an SSL certificate is well described here , if anyone is too lazy to read, I will describe everything here:

1. We generate the key 1 time, save it.

 openssl genrsa -out server.key 2048 

2. Generate the certificate request file.

 openssl req -new -key srv-shop-aster.key -out srv-shop-aster.csr -subj "/C=RU/ST=Novosibirskaya Oblast/L=Kolcovo/O=Roga_and_Kopita_Company Ltd./OU=IT Department/CN=srv-shop-aster/emailAddress=admin_user@RogKop.ru/" 

3. We take the server srv-shop-aster.csr file, you will of course have another name in general the file "server_name.csr"

4. Go to the certificate signing service. Select the product, the certificate lifetime and sign it by clicking on the “Sign Certificate Request” button, then save the already signed certificate to the disk. By the way, you need to register on the Cisco site.

5. Configure HTTPS in Apache, well written here .

I have a certificate and the key is in the /etc/apache2/ssl/ directory in the /etc/apache2/sites-enabled/default-ssl.conf file, the path to the certificate and key are specified:

SSLCertificateFile /etc/apache2/ssl/srv-shop-aster.crt
SSLCertificateKeyFile /etc/apache2/ssl/srv-shop-aster.key


And I did not prohibit the use of the obsolete SSLv2 protocol, i.e. # SSLProtocol all -SSLv2
Do not forget to put the key with the signed certificate in the appropriate directory and restart Apache.

Asterisk KX-NS1000, WEB- DHCP .

Source: https://habr.com/ru/post/331214/


All Articles