Asterisk WebRTC with PJSip from Scratch

VitalPBX Asterisk WebRTC with PJSip
Share on facebook
Share on twitter
Share on whatsapp
Share on telegram
Share on linkedin
Share on email

1.- Introduction

WebRTC (Web Real-Time Communication) is a free, open-source, project providing web browsers and mobile applications with real-time communications (RTC) via simple application programming interfaces (APIs). It allows audio and video communication to work inside web pages by allowing direct peer-to-peer communication, eliminating the need to install plugins or download native apps. Supported by Apple, Google, Microsoft, Mozilla, and Opera, WebRTC specifications have been published by the World Wide Web Consortium (W3C) and the Internet Engineering Task Force (IETF).

This tutorial will walk you through configuring Asterisk to service WebRTC clients.

  • Modify or create an Asterisk HTTPS TLS server.
  • Create a PJSIP WebSocket transport.
  • Create PJSIP Endpoint, AOR and Authentication objects that represent a WebRTC client.

2.- Installation

2.1.- Preparing our server

First, we update our CentOS 7 installation and install some dependencies.

  • [root@localhost ~]# yum install wget nano git epel-release -y

Disable SELinux on CentOS

  • [root@localhost ~]# sed -i ‘s/\(^SELINUX=\).*/\SELINUX=permissive/’ /etc/selinux/config
  • [root@localhost ~]# reboot

Next, Install the Firewall

  • [root@localhost ~]# yum install firewalld -y
  • [root@localhost ~]# systemctl enable firewalld
  • [root@localhost ~]# systemctl start firewalld

Enable required ports

  • [root@localhost ~]# firewall-cmd –zone=public –add-port=10000-20000/udp –permanent
  • [root@localhost ~]# firewall-cmd –zone=public –add-port=10000-20000/tcp –permanent
  • [root@localhost ~]# firewall-cmd –zone=public –add-port=8089/tcp –permanent
  • [root@localhost ~]# firewall-cmd –zone=public –add-port=443/tcp –permanent
  • [root@localhost ~]# firewall-cmd –zone=public –add-port=80/tcp –permanent
  • [root@localhost ~]# firewall-cmd –zone=public –add-port=22/tcp –permanent
  • [root@localhost ~]# firewall-cmd –reload

Now, we enable extra security for ssh access. We change the port by doing the following steps. (Optional)

  • [root@localhost ~]# nano /etc/ssh/sshd_config
  • # $OpenBSD: sshd_config,v 1.100 2016/08/15 12:32:04 naddy Exp $
  • # This is the sshd server system-wide configuration file. See
  • # sshd_config(5) for more information.
  • # This sshd was compiled with PATH=/usr/local/bin:/usr/bin
  • # The strategy used for options in the default sshd_config shipped with
  • # OpenSSH is to specify options with their default value where
  • # possible, but leave them commented. Uncommented options override the
  • # default value.
  • # If you want to change the port on a SELinux system, you have to tell
  • # SELinux about this change.
  • # semanage port -a -t ssh_port_t -p tcp #PORTNUMBER
  • #
  • Port 2235
  • #AddressFamily any
  • #ListenAddress 0.0.0.0
  • #ListenAddress ::

Uncomment the line #Port 22 and change the port to one of your preference.

Now add the new rule with the new port and restart the sshd service and log in with the new port.

  • [root@localhost ~]# firewall-cmd –zone=public –add-port=2235/tcp –permanent
  • [root@localhost ~]# firewall-cmd –reload
  • [root@localhost ~]# systemctl restart sshd

2.2.- Installing Asterisk 18

Now we start the install process of Asterisk 18

  • [root@localhost ~]# cd /usr/src/
  • [root@localhost src]# wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-18-current.tar.gz
  • [root@localhost src]# tar -zxvf asterisk-18-current.tar.gz
  • [root@localhost src]# cd asterisk-18.*
  • [root@localhost asterisk-18.4.0]# yum install svn -y
  • [root@localhost asterisk-18.4.0]# ./contrib/scripts/get_mp3_source.sh
  • [root@localhost asterisk-18.4.0]# contrib/scripts/install_prereq install
  • [root@localhost asterisk-18.4.0]# ./configure –libdir=/usr/lib64 –with-jansson-bundled –with-pjproject-bundled

At the end of the Asterisk compilation the following appears

VitalPBX Asterisk Install

Then, we proceed to make menuselect

  • [root@localhost asterisk-18.4.0]# make menuselect

We make sure that on the codec “codec_opus” is selected. After selecting everything we need, we proceed to Save & Exit.

VitalPBX Asterisk Install Menu Select

Now we will proceed to install Asterisk, wait about 10 minutes. s pasos

  • [root@localhost asterisk-18.4.0]# make && make install
  • [root@localhost asterisk-18.4.0]# make samples
  • [root@localhost asterisk-18.4.0]# make config

Create a separate user and group to run asterisk services, and assign correct permissions: s pasos

  • [root@localhost asterisk-18.4.0]# groupadd asterisk
  • [root@localhost asterisk-18.4.0]# useradd -r -d /var/lib/asterisk -g asterisk asterisk
  • [root@localhost asterisk-18.4.0]# usermod -aG audio,dialout asterisk
  • [root@localhost asterisk-18.4.0]# chown asterisk. -R /etc/asterisk
  • [root@localhost asterisk-18.4.0]# chown asterisk. -R /var/{lib,log,spool}/asterisk
  • [root@localhost asterisk-18.4.0]# chown -R asterisk.asterisk /usr/lib64/asterisk

Set Asterisk default user to asterisk:

  • [root@localhost asterisk-18.4.0]# nano /etc/sysconfig/asterisk
  • AST_USER=”asterisk”
  • AST_GROUP=”asterisk”

Restart asterisk service after making the changes:

  • [root@localhost asterisk-18.4.0]# chkconfig asterisk on
  • [root@localhost asterisk-18.4.0]# systemctl restart asterisk
  • [root@localhost asterisk-18.4.0]# asterisk -rvvvvvvvvvvvvvvvvvvv
  • Asterisk 18.4.0, Copyright (C) 1999 – 2021, Digium, Inc. and others.
  • Created by Mark Spencer markster@digium.com
  • Asterisk comes with ABSOLUTELY NO WARRANTY; type ‘core show warranty’ for details.
  • This is free software, with components licensed under the GNU General Public
  • License version 2 and other licenses; you are welcome to redistribute it under
  • certain conditions. Type ‘core show license’ for details.
  • =========================================================================
  • Connected to Asterisk 18.4.0 currently running on localhost (pid = 91658)
  • localhost*CLI>

CONGRATULATIONS! You have successfully installed Asterisk 18.

2.3.- Installing Apache

Install Apache to enable web access to our servers.

  • [root@localhost ~]# yum install httpd -y
  • [root@localhost ~]# systemctl enable httpd
  • [root@localhost ~]# systemctl start httpd

We create a temporary website with our domain (Our example is: wrtc.vitalpbx.org).

Throughout this tutorial you should substitute the domain wrtc.vitalpbx.org for yours.

  • [root@localhost ~]# mkdir -p /var/www/html/mydomain.com/{public_html,logs}
  • [root@localhost ~]# nano /etc/httpd/conf.d/ mydomain.com.conf
  • NameVirtualHost *:80
  • <VirtualHost *:80>
  •        ServerAdmin webmaster@mydomain.com
  •        ServerName mydomain.com
  •        ServerAlias mydomain.com
  •        DocumentRoot /var/www/html/mydomain.com/public_html/
  •        ErrorLog /var/www/html/mydomain.com/logs/error.log
  •        CustomLog /var/www/html/mydomain.com/logs/access.log combined
  • </VirtualHost>

We restart our Apache

  • [root@localhost ~]# systemctl restart httpd

2.4.- Creating our Certificate

First, we have to ensure that our domain or subdomain points to the IP address of our server, for this we go to the configuration of our DNS and add a type A record.

Now we will proceed to install our certificate by first installing the dependencies.

  • [root@localhost ~]# yum install certbot python2-certbot-apache mod_ssl -y

Now we create our LetsEncrypt certificate.

  • [root@localhost ~]# certbot –apache -d mydomain.com
  • Saving debug log to /var/log/letsencrypt/letsencrypt.log
  • Plugins selected: Authenticator apache, Installer apache
  • Enter email address (used for urgent renewal and security notices)
  •  (Enter ‘c’ to cancel):

Provide an email to be notified of the expiration of the certificate.

  • Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
  • – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
  • Please read the Terms of Service at
  • https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
  • agree in order to register with the ACME server. Do you agree?
  • – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
  • (Y)es/(N)o:

Answer Y to proceed

  • – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
  • Would you be willing, once your first certificate is successfully issued, to
  • share your email address with the Electronic Frontier Foundation, a founding
  • partner of the Let’s Encrypt project and the non-profit organization that
  • develops Certbot? We’d like to send you email about our work encrypting the web,
  • EFF news, campaigns, and ways to support digital freedom.
  • – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
  • (Y)es/(N)o:

Answer Y to proceed

At the end we see the following message confirming that all is correct.

  • Account registered.
  • Requesting a certificate for mydomain.com
  • Performing the following challenges:
  • http-01 challenge for mydomain.com
  • Waiting for verification…
  • Cleaning up challenges
  • Created an SSL vhost at /etc/httpd/conf.d/mydomain.com-le-ssl.conf
  • Deploying Certificate to VirtualHost /etc/httpd/conf.d/ mydomain.com-le-ssl.conf
  • Redirecting vhost in /etc/httpd/conf.d/mydomain.com.conf to ssl vhost in /etc/httpd/conf.d/ mydomain.com-le-ssl.conf
  • – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
  • Congratulations! You have successfully enabled https://mydomain.com
  • – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
  • Subscribe to the EFF mailing list (email: support@mydomain.com).
  • Starting new HTTPS connection (1): supporters.eff.org
  • We were unable to subscribe you the EFF mailing list because your e-mail address appears to be
  • invalid. You can try again later by visiting https://act.eff.org.
  • IMPORTANT NOTES:
  • – Congratulations! Your certificate and chain have been saved at:
  •  /etc/letsencrypt/live/mydomain.com/fullchain.pem
  •  Your key file has been saved at:
  •  /etc/letsencrypt/live/mydomain.com/privkey.pem
  •  Your certificate will expire on 2021-09-14. To obtain a new or
  •  tweaked version of this certificate in the future, simply run
  •  certbot again with the “certonly” option. To non-interactively
  •  renew *all* of your certificates, run “certbot renew”
  • – If you like Certbot, please consider supporting our work by:
  •  Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
  •  Donating to EFF: https://eff.org/donate-le
  • [root@localhost ~]#

We write down the path of both certificates, since we are going to use them later.

We modify the file /etc/httpd/conf.d/ssl.conf

  • [root@localhost ~]# mv /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.bak
  • [root@localhost ~]# nano /etc/httpd/conf.d/ssl.conf
  • Listen 443 https
  • SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog
  • SSLSessionCache shmcb:/run/httpd/sslcache(512000)
  • SSLSessionCacheTimeout 300
  • SSLRandomSeed startup file:/dev/urandom 256
  • SSLRandomSeed connect builtin
  • [root@localhost ~]# mv /etc/httpd/conf.d/welcome.conf /etc/httpd/conf.d/welcome.conf.bak
  • [root@localhost ~]# mv /etc/httpd/conf.d/mydomain.com.conf /etc/httpd/conf.d/mydomain.com.conf.bak

Disable other sites

Reload Apache service

  • [root@localhost ~]# systemctl reload-or-restart httpd

Set Up Auto-Renewal

Now that certbot is installed and working, we need to have it check for expiring certificates automatically. As root, we first open the crontab for our server:

  • [root@localhost ~]# crontab -e

Press Insert

In this instance, I’ve added a cron to our example server that looks like this:

  • 45 3 * * 6 /usr/local/letsencrypt/certbot-auto renew && systemctl reload httpd

Save with Esc, :wq

This cron will, at 3:45 AM every Saturday, run the certbot renew function to renew any already-installed certificates that are due to expire, and then reload the Apache configuration. Save the crontab after you add this line, and it will be in effect immediately.

2.5.- Asterisk Configuration

You should have a working chan_pjsip based Asterisk installation. Either install Asterisk from your distribution’s packages or, preferably, install Asterisk from source. Either way, there are a few modules over and above the standard ones that must be present for WebSockets and WebRTC to work:

  • res_crypto
  • res_http_websocket
  • res_pjsip_transport_websocket
  • codec_opus (optional but highly recommended for high quality audio)

We recommend installing Asterisk from source because it’s easy to make sure these modules are built and installed.

2.5.1.- Certificates

Technically, a client can use WebRTC over an insecure WebSocket to connect to Asterisk. In practice though, most browsers will require a TLS based WebSocket to be used. You can use self-signed certificates to set up the Asterisk TLS server but getting browsers to accept them is tricky. So if you’re able, we highly recommend getting trusted certificates from an organization such as LetsEncrypt.

As the objective of this presentation is not to teach how to install a certificate with LetsEncrypt, we recommend the following link to do it yourself.

https://www.tecmint.com/install-lets-encrypt-ssl-certificate-to-secure-apache-on-rhel-centos/

2.5.2.- Configure Asterisk’s built-in HTTP server

To communicate with WebSocket clients, Asterisk uses its built-in HTTP server. So we configure the following:

  • [root@localhost ~] mv /etc/asterisk/http.conf /etc/asterisk/http.conf.bak
  • [root@localhost ~] nano /etc/asterisk/http.conf
  • [general]
  • servername=Asterisk
  • tlsbindaddr=0.0.0.0:8089
  • bindaddr=0.0.0.0
  • bindport=8088
  • enabled=yes
  • tlsenable=yes
  • tlscertfile=/etc/letsencrypt/live/mydomian.com/fullchain.pem
  • tlsprivatekey=/etc/letsencrypt/live/mydomain.com/privkey.pem

2.5.3.- PJSIP WSS Transport

Although the HTTP server does the heavy lifting for WebSockets, we still need to define a basic PJSIP Transport:

  • [root@localhost ~] mv /etc/asterisk/pjsip.conf /etc/asterisk/pjsip.conf.bak
  • [root@localhost ~] nano /etc/asterisk/pjsip.conf
  • [system]
  • type=system
  • timer_t1=500
  • timer_b=32000
  • disable_tcp_switch=yes
  • [global]
  • type=global
  • max_initial_qualify_time=0
  • keep_alive_interval=90
  • contact_expiration_check_interval=30
  • default_voicemail_extension=*97
  • unidentified_request_count=3
  • unidentified_request_period=5
  • unidentified_request_prune_interval=30
  • mwi_tps_queue_high=500
  • mwi_tps_queue_low=-1
  • mwi_disable_initial_unsolicited=yes
  • send_contact_status_on_update_registration=yes
  • [transport-wss]
  • type=transport
  • protocol=wss
  • bind=0.0.0.0:8089
  • local_net=10.10.0.9/16
  • local_net=10.116.0.6/20
  • external_media_address=178.128.149.185
  • external_signaling_address=178.128.149.185
  • allow_reload=yes

Replace local_net, external_media_address and external_signaling_address with their respective IPs.

2.5.4.- PJSIP Endpoint, AOR and Auth

We now need to create the basic PJSIP objects that represent the client. In this example, we’ll call the client webrtc_client, but you can use any name you like, such as an extension number. Only the minimum options needed for a working configuration are shown. NOTE: It’s normal for multiple objects in pjsip.conf to have the same name as long as the types differ. Add the following content to the end of the file.

  • [root@localhost ~] nano /etc/asterisk/pjsip.conf
  • [webrtc-phones](!)
  • context=main-context
  • transport=transport-wss
  • allow=!all,opus,ulaw,alaw,vp8,vp9
  • webrtc=yes
  • [User1](webrtc-phones)
  • type=endpoint
  • callerid=”User One” <100>
  • auth=User1
  • aors=User1
  • [User1]
  • type=aor
  • max_contacts=3
  • [User1]
  • type=auth
  • auth_type=userpass
  • username=User1
  • password=1234
  • [User2](webrtc-phones)
  • type=endpoint
  • callerid=”User Two” <101>
  • auth=User2
  • aors=User2
  • [User2]
  • type=aor
  • max_contacts=3
  • [User2]
  • type=auth
  • auth_type=userpass
  • username=User2
  • password=1234
  • [User3](webrtc-phones)
  • type=endpoint
  • callerid=”User Three” <102>
  • auth=User3
  • aors=User3
  • [User3]
  • type=aor
  • max_contacts=3
  • [User3]
  • type=auth
  • auth_type=userpass
  • username=User3
  • password=1234

We change the owner of the file to be asterisk

  • [root@localhost ~] chown asterisk. /etc/asterisk/pjsip.conf

2.5.5.- Configure extensions.conf

Update the /etc/asterisk/extensions.conf to the following:

  • [root@localhost ~] mv /etc/asterisk/extensions.conf /etc/asterisk/extensions.conf.bak
  • [root@localhost ~] nano /etc/asterisk/extensions.conf
  • [general]
  • static=yes
  • writeprotect=yes
  • priorityjumping=no
  • autofallthrough=no
  • [globals]
  • ATTENDED_TRANSFER_COMPLETE_SOUND=beep
  • [main-context]
  • include => from-extensions
  • include => subscriptions
  • include => textmessages
  • include => echo-test
  • include => speak-exte-nnum
  • [echo-test]
  • exten => 777,1,NoOp(FEATURE: ECHO TEST)
  •  same => n,Answer
  •  same => n,Wait(1)
  •  same => n,Playback(demo-echotest)
  •  same => n,Echo()
  •  same => n,Playback(demo-echodone)
  •  same => n,Hangup()
  • ;END of [echo-test]
  • [speak-exte-nnum]
  • exten => 888,1,NoOp(FEATURE: SPEAK MY EXTENSION NUMBER)
  •  same => n,Answer
  •  same => n,Wait(1)
  •  same => n,Playback(extension)
  •  same => n,Wait(1)
  •  same => n,SayDigits(${CALLERID(num)})
  •  same => n,Wait(2)
  •  same => n,Hangup()
  • ;END of [speak-exte-nnum]
  • [textmessages]
  • exten => 100,1,Gosub(send-text,s,1,(User1))
  • exten => 101,1,Gosub(send-text,s,1,(User2))
  • exten => 102,1,Gosub(send-text,s,1,(User3))
  • [subscriptions]
  • exten => 100,hint,PJSIP/User1
  • exten => 101,hint,PJSIP/User2
  • exten => 102,hint,PJSIP/User3
  • [from-extensions]
  • ; Feature Codes:
  • exten => *65,1,Gosub(moh,s,1)
  • ; Extensions
  • exten => 100,1,Gosub(dial-extension,s,1,(User1))
  • exten => 101,1,Gosub(dial-extension,s,1,(User2))
  • exten => 102,1,Gosub(dial-extension,s,1,(User3))
  • exten => e,1,Hangup()
  • [moh]
  • exten => s,1,NoOp(Music On Hold)
  • exten => s,n,Ringing()
  • exten => s,n,Wait(2)
  • exten => s,n,Answer()
  • exten => s,n,Wait(1)
  • exten => s,n,MusicOnHold()
  • [dial-extension]
  • exten => s,1,NoOp(Calling: ${ARG1})
  • exten => s,n,Set(JITTERBUFFER(adaptive)=default)
  • exten => s,n,Dial(PJSIP/${ARG1},30)
  • exten => s,n,Hangup()
  • exten => e,1,Hangup()
  • [send-text]
  • exten => s,1,NoOp(Sending Text To: ${ARG1})
  • exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)})
  • exten => s,n,Set(FROM=${SHELL(asterisk -rx ‘pjsip show endpoint ${PEER}’ | grep ‘callerid ‘ | cut -d’:’ -f2- | sed ‘s/^\ *//’ | tr -d ‘\n’)})
  • exten => s,n,Set(CALLERID_NUM=${CUT(CUT(FROM,>,1),<,2)})
  • exten => s,n,Set(FROM_SIP=${STRREPLACE(MESSAGE(from),
  • exten => s,n,MessageSend(pjsip:${ARG1},${FROM_SIP})
  • exten => s,n,Hangup()

We change the owner of the file to be asterisk

  • [root@localhost ~] chown asterisk. /etc/asterisk/extensions.conf

Restart Asterisk to pick up the changes and if you have a firewall, don’t forget to allow TCP port 8089 through so your client can connect.

  • [root@localhost ~] asterisk -rvvvvvvvvvvvvvvvvvv
  • localhost*CLI> module reload res_pjsip.so
  • localhost*CLI> dialplan reload

Verify that the certificate is properly applied

  • localhost*CLI> module reload http
  • localhost*CLI> http show status
  • HTTP Server Status:
  • Prefix:
  • Server: Asterisk
  • Server Enabled and Bound to 0.0.0.0:8088
  • HTTPS Server Enabled and Bound to 0.0.0.0:8089
  • Enabled URI’s:
  • /httpstatus => Asterisk HTTP General Status
  • /phoneprov/… => Asterisk HTTP Phone Provisioning Tool
  • /metrics/… => Prometheus Metrics URI
  • /ari/… => Asterisk RESTful API
  • /ws => Asterisk HTTP WebSocket
  • Enabled Redirects:
  •  None.

2.5.6.- Wrap Up

At this point, your WebRTC client should be able to register and make calls. If you’ve used self-signed certificates however, your browser may not allow the connection and because the attempt is not from a normal URI supplied by the user, the user might not even be notified that there’s an issue.  You may be able to get the browser to accept the certificate by visiting “https://pbx.example.com:8089/ws” directly.  This will usually result in a warning from the browser and may give you the opportunity to accept the self-signed certificate and/or create an exception. If you generated your certificate from a pre-existing local Certificate Authority, you could also import that Certificate Authority’s certificate into your trusted store but that procedure is beyond the scope of this document.

**Information Source:

https://wiki.asterisk.org/wiki/display/AST/Configuring+Asterisk+for+WebRTC+Clients

3.- WebRTC Client

For this presentation we decided to use Browser Phone that our criteria is one of the best and most complete open source projects of WebRTC Client.

3.1.- Browser Phone

This web application is designed to work with Asterisk PBX (v13, v16 & v18). Once loaded application will connect to Asterisk PBX on its web socket, and register an extension. Calls are made between contacts, and a full call detail is saved. Audio Calls can be recorded. Video Calls can be recorded, and can be saved with 5 different recording layouts and 3 different quality settings. This application does not use any cloud systems or services, and is designed to be stand-alone. Additional libraries will be downloaded at run time (but can also be saved to the web server for a complete off-line solution).

3.2.- Server Requires

Asterisk PBX version 13|16|17|18 (with Websockets and Text Messaging, chan_sip or chan_pjsip).

Copy the project to the previously created folder.

  • [root@localhost ~] cd /var/www/html/wrtc.vitalpbx.org/public_html/
  • [root@localhost ~] git clone https://github.com/InnovateAsterisk/Browser-Phone.git

Give the permissions

  • [root@localhost ~] chown -R apache:apache /var/www/html/wrtc.vitalpbx.org/public_html/

3.3.- Enter the Browser with the following url:

https://mydomain/Browser-Phone/Phone/

We enter the following data and we give it Save. Make sure to write all the data including the /ws value in WebSocket Path.

VitalPBX Browser Phone Account

After pressing Save, your registered account should appear at the top left.

Below we show an image of how extension 100 registered with a call in progress should look like.

VitalPBX Browser Phone Gui

Our Latest Post

VitalPBX Asterisk WebRTC with PJSip
Uncategorized
Rodrigo Cuadra

Asterisk WebRTC with PJSip from Scratch

1.- Introduction WebRTC (Web Real-Time Communication) is a free, open-source, project providing web browsers and mobile applications with real-time communications (RTC) via simple application programming

Read More »

2 Comments

  1. Thanks, I have VitalPBX 3.0.9-5 and Asterisk 18.3.0-1, can i use it on existing server or i have to follow all steps as mentioned.


Add a Comment

VitalPBX Logo

66 West Flagler Street
Suite 900 – #1957
Miami, FL 33130, USA

Phone: +1(305) 560-5776
Email: sales@vitalpbx.org

SUBSCRIBE

Subscribe to our newsletter to receive updates and the latest news from VitalPBX

Copyright @ 2020 VitalPBX,  All rights reserved.