Jitsi Meet /w Call-In and Active Directory


Jitsi Meet is a video conferencing service that allows users to spin up video conference rooms instantly, which include screen sharing and the ability to call-in from a phone. This document will explain how to set it up from start to finish.


This Jitsi Meet implementation guide makes the following assumptions:

  • You have preconfigured a base installation of Debian 9 on the server to be used for installation
  • You have preinstalled NGINX web server with a default configuration on your Debian 9 webserver
  • You have created a static DNS record for the server to be used for installation, using the FQDN that you want to use for Jitsi Meet
  • You have a functioning production FreePBX Asterisk Server on the same network as the server to be used for installation
  • You have a webserver that is available to all users of the service, configured for SSL with a valid certificate, and also visible to the server to be used, on which you can host and serve a PHP file
  • You have configured a Microsoft Active Directory environment on the network that houses the server to be used
  • You have created an Active Directory account to use as a service account for Jitsi Meet

Setup Procedure

Install Jitsi Meet

Add the Jitsi repository to your sources list.

echo 'deb https://download.jitsi.org stable/; >> /etc/apt/sources.list.d/jitsi-stable.list
wget -qO - https://download.jitsi.org/jitsi-key.gpg.key | apt-key add -

Update the package lists

apt-get update

Verify that NGINX is installed

root@vm-jitsi-prod:~# nginx -v
nginx version: nginx/1.10.3

If this command returns "-bash: nginx: command not found", then install NGINX before moving to the next step.

Install the Jitsi Meet packages using Apt

Note: For your reference and understanding of the moving parts, Jitsi Meet's backend consists of three parts

  • Jitsi-Videobridge – Video Stream SFU
  • Jicofo – Jitsi COnference FOcus, server side component that manages media sessions between each of the participants and the Videobridge
  • Prosody – Jabber/XMPP server

During the installation, you will be asked to enter the hostname of the Jitsi Meet instance.

If you have an FQDN hostname for the instance already set up in DNS, enter it there.

If for some reason you don't have a resolvable hostname, you can enter the IP address of the machine (if it is static or doesn't change).

This hostname (or IP address) will be used for virtualhost configuration inside NGINX and Prosody, and also you and your correspondents will be using it to access the web conferences.

Also important is that you cannot use a wildcard certificate, because the CN of the certificate must match your FQDN, so during installation select to use a self-signed certificate, and we will later generate the needed certificates using LetsEncrypt

apt-get -y install jitsi-meet

Generate a Let's Encrypt certificate

Jitsi meet includes a script that will automagically generate your Let's Encrypt certificate and install it to your NGINX virtualhost file that it created during setup.

Simply run the following script:


Edit Jitsi Videobridge sip-communicator.properties

Because I am behind a NAT due to my Cisco ASA, ice4j (a component of Videobridge) must be told that it has separate local and public IP addresses.
To do so, the following extra lines need to be added the file /etc/jitsi/videobridge/sip-communicator.properties:


Open Required Ports

Edit your firewall to open up the required ports to Jitsi Meet.
Jitsi Meet will require ports 80, 443, and 4443 on TCP, and port 10000 on UDP

Below is an example of code to do this if your firewall is a Cisco ASA.

object network PUBLIC_IPhost XXX.XXX.XXX.XXX

object network JITSI_MEET_SERVER

object service HTTPS_443
service tcp source eq https

object service PORT_4443
service tcp source eq 4443

object service WWW_80
service tcp source eq www

object service PORT_10000
service tcp source eq 10000

object service PORT_10000_UDP
service udp source eq 10000

object-group service JITSI_MEET_PORTS_TCP tcp
port-object eq https 
port-object eq 4443
port-object eq 10000
port-object eq www

object-group service JITSI_MEET_PORTS_UDP udp
port-object eq 10000

access-list outside_access_in extended permit tcp any object JITSI_MEET_SERVER object-group JITSI_MEET_PORTS_TCP
access-list outside_access_in extended permit udp any object JITSI_MEET_SERVER object-group JITSI_MEET_PORTS_UDP

nat (inside,ATT) source static JITSI_MEET_SERVER PUBLIC_IP service HTTPS_443 HTTPS_443 description JITSI MEET
nat (inside,ATT) source static JITSI_MEET_SERVER PUBLIC_IP service PORT_4443 PORT_4443 description JITSI MEET
nat (inside,ATT) source static JITSI_MEET_SERVER PUBLIC_IP service PORT_10000 PORT_10000 description JITSI MEET
nat (inside,ATT) source static JITSI_MEET_SERVER PUBLIC_IP service PORT_10000_UDP PORT_10000_UDP description MEET
nat (inside,ATT) source static JITSI_MEET_SERVER PUBLIC_IP service WWW_80 WWW_80 description JITSI MEET




Create Extension for Jitsi-Meet  in FreePBX

Navigate in a browser to your FreePBX server

Select the Extensions module from Applications

Select 'Generic CHAN SIP Device' from the Device drop down and click Submit

Populate the 'User Extension' and 'Display Name' fields and click 'Submit'


Apply your changes

Make a note of the extension and the secret, because you will need it in order to…

Setup Jitsi Meet's SIP gateway

Install the Jitsi Meet SIP Gateway
NOTE: More moving parts.

  • Jigasi – JItsi GAteway to SIP: A server-side application that provides a link between regular SIP clients and Jitsi Videobridge conferences.

apt-get -y install jigasi

During installation, you will be asked for the SIP account and password for the extension you created above.

The username will be 'XXXX@freepbx.server.com' where 'XXXX' is your extension created above, and 'freepbx.server.com' is the FQDN or IP address of your FreePBX server

Your password will be the 'secret' for the extension you created.

Host Phone Number .PHP and JSON

On a webserver that is NOT running on the Jitsi Meet server we are setting up, you will need to host two files.

First, the JSON file that will display your dial-in phone numbers:

{"message":"Phone numbers available.","numbers":"US":["+1.555.555.1212"],"Germany":["+49.5551212"]},"numbersEnabled":true}

And Second, the PHP file that will serve the JSON and Content-Type Headers to Jitsi Meet:

    header('Content-Type: application/json');

You must configure your web server to serve this PHP file over SSL
Since this is not hosted on your Jitsi Meet server, you must also enable Cross Origin Scripting (CORS) support.
You can use any web server, but here is an example of a VirtualHost entry if your web server is Apache:

<VirtualHost *:443>
        ServerAdmin you@domain.com
        ServerName assetbin.domain.com
        DocumentRoot /var/www/assetbin
        SSLEngine on
        SSLProtocol all -SSLv2 -SSLv3
        SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA
        SSLCertificateFile /etc/httpd/certs/domain.crt
        SSLCertificateKeyFile /etc/httpd/certs/domain.key
        SSLCertificateChainFile /etc/httpd/certs/domain.chain
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        <Directory /var/www/assetbin>
                Options Indexes FollowSymLinks MultiViews
                DirectoryIndex index.html
                AllowOverride All
                Order allow,deny
                allow from all
                Header set Access-Control-Allow-Origin "*"
        ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
        <Directory "usr/lib/cgi-bin">
                AllowOverride None
                Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
                Order allow,deny
                Allow from all
        ErrorLog logs/assetbin_error.log
        LogLevel warn
        CustomLog logs/assetbin_access.log combined

You should navigate to both of these in a web browser to make sure they are functional and accessible.

Enable Call-In Number Display in Jitsi Meet

The options to display the Phone Numbers list you just created, and the Meeting ID number, are available in Jitsi-Meet but undocumented.
NOTE: The Meeting ID display uses Jitsi Meet's hosted ConferenceMapper API

Add the following lines to your Jitsi Meet configuration file at /etc/jitsi/meet/meet.domain.com-config.js where 'meet.domain.com' is the FQDN of your Jitsi Meet server:

// To enable sending statistics to callstats.io you must provide the
// Application ID and Secret.
// callStatsID: '',
// callStatsSecret: '',
// Call In Numbers and Codes   
dialInNumbersUrl: 'https://assetbin.domain.com/jitsiNumberList.php',
dialInConfCodeUrl: 'https://jitsi-api.jitsi.net/conferenceMapper',   
// Don't change dialInConfCodeURL, it is the one that points at Jitsi's conferenceMapper API
// enables callstatsUsername to be reported as statsId and used
// by callstats as repoted remote id
// enableStatsID: false

Configure FreePBX Extension To Use conferenceMapper API

In this step, we will be creating a custom dialplan for your extension that has a few extra steps in it in order to map you to the conference.
Its order of operations for receiving phone calls will be as follows:

  • Play recording asking for a user to dial the room number followed by '#'
  • Save that dialed PIN to a variable to be used later
  • Pass that PIN via AGI to a bash script, that will use it and CURL to pull the Room Name from the API response
  • Pass that Room Name via AGI back to the Dialplan
  • Use the Room Name variable to add a SIP Header to the SIP INVITE so that Jigasi knows what room to put you in
  • Continues with the call to your Jigasi extension, with your new SIP Header

So first step is to create a system recording to use.
Navigate to your FreePBX server in a browser
Navigate to the System Recording module under Admin

Create a system recording from your phone, or upload one.
NOTE: I've found its easier to do it from your phone by following the directions because then you know the format is correct.

Name the recording and click 'Save'

Remember what you named the recording, because you will need it for the dialplan.

Next, SSH into your FreePBX server.

Before we override your extension dialplan, we will need to put the AGI Bash script in place.

Create the script in /var/lib/asterisk/agi-bin and give it execute permissions:

[root@phone /]# touch /var/lib/asterisk/agi-bin/jitsi_curling.sh
[root@phone /]# chmod +x /var/lib/asterisk/agi-bin/jitsi_curling.sh

Next edit the file, and populate it with the following:

jit_rm=$(curl --silent https://jitsi-api.jitsi.net/conferenceMapper?id=$1 | cut -d \, -f 3 | cut -d \: -f 2 | cut -d \" -f 2 | cut -d \@ -f 1)
echo "SET VARIABLE JITSI \"${jit_rm}\" "

Now, to override your dialplan properly in FreePBX, we will need to add the new dialplan to the FreePBX Override file at /etc/asterisk/extensions_override_freepbx.conf

Edit that file and populate it with the following, where 9999 is the extension you created earlier, and my_system_recording is the name of the System Recording you created in FreePBX.

exten => 9999,1,Set(__RINGTIMER=${IF($["${DB(AMPUSER/9999/ringtimer)}" > "0"]?${DB(AMPUSER/9999/ringtimer)}:${RINGTIMER_DEFAULT})})
exten => 9999,2,Read(Pin,"custom/my_system_recording")
exten => 9999,3,Verbose(result is: ${Pin})
exten => 9999,4,AGI(jitsi_curling.sh,${Pin})
exten => 9999,5,Verbose(result is: ${JITSI})
exten => 9999,6,SIPAddHeader(Jitsi-Conference-Room: ${JITSI})
exten => 9999,7,Macro(exten-vm,novm,9999,0,0,0)
exten => 9999,8(dest),Set(__PICKUPMARK=)
exten => 9999,9,Goto(${IVR_CONTEXT},return,1)
exten => 9999,hint,SIP/9999&Custom:DND9999,CustomPresence:9999




Now, to tell Asterisk to refresh its dialplans WITHOUT reloading and bouncing all its in-progress calls, run the following command:

[root@phone /]# rasterisk -x 'dialplan reload'
Dialplan reloaded.

Configure LDAP Integration

If you want only authorized users to be able to create new meetings, you'll want to authenticate against LDAP.

First, we must install some additional prosody modules, which are all packaged together in a package called, appropriately, 'prosody-modules'

apt-get install prosody-modules

Next you'll need to create the LDAP configuration file for the Prosody ldap2 module at /etc/prosody/conf.d/ldap.cfg.lua.

touch /etc/prosody/conf.d/ldap.cfg.lua

Edit the file and populate it with the following:

-- Authentication configuration --
authentication = 'ldap2' -- Indicate that we want to use LDAP for authentication
ldap = {
    hostname      = 'your-dc.domain.com:389', -- LDAP server location
    --use_tls     = true,
    bind_dn       = 'cn=user,ou=group,dc=domain,dc=com', -- Bind DN for LDAP authentication (optional if anonymous bind is supported)
    bind_password = 'bind_user_password', -- Bind password (optional if anonymous bind is supported)
    user = {
      basedn        = 'dc=domain,dc=com', -- Base DN for valid user search
      filter        = '(&(|(memberOf=CN=Valid_Users,OU=Groups,DC=domain,DC=com)(memberOf=CN=Service Accounts,OU=Groups,DC=domain,DC=com))(objectClass=user)(!(objectClass=computer))(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'
      -- The above filter is an example. It will consider users valid if they are in the Valid_Users group OR the Service Accounts group, AND have an objectClass of user, AND DO NOT have an object class of computer, AND ARE NOT disabled.
      usernamefield = 'sAMAccountName',
      namefield     = 'cn',




Edit Prosody Virtualhost File

Next we will need to edit the Virtualhost file for your Meet server in Prosody to tell it to use LDAP authentication.
We also need to make sure your auth domain certificate path is populated. (The certificates should exist from earlier but the field may be blank)
Edit the file /etc/prosody/conf.d/meet.domain.com.cfg.lua where meet.domain.com is your Jitsi server's FQDN, and change the following:

    -- Plugins path gets uncommented during jitsi-meet-tokens package install - that's where token plugin is located
--plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
VirtualHost "meet.domain.com"
        -- enabled = false -- Remove this line to enable this host
        --authentication = "anonymous" --COMMENT OUT
        authentication = "ldap2"       --ADD ME
        -- Properties below are modified by jitsi-meet-tokens package config
        -- and authentication above is switched to "token"
        -- Assign this host a certificate for TLS, otherwise it would use the one
        -- set in the global section (if any).
        -- Note that old-style SSL on port 5223 only supports one certificate, and will always
        -- use the global one.
        ssl = {
                key = "/etc/prosody/certs/meet.domain.com.key";
                certificate = "/etc/prosody/certs/meet.domain.com.crt";
        -- we need bosh
        modules_enabled = {
            "ping"; -- Enable mod_ping
        c2s_require_encryption = false
Component "conference.meet.domain.com" "muc"
    storage = "null"
    --modules_enabled = { "token_verification" }
admins = { "focus@auth.meet.domain.com" }
Component "jitsi-videobridge.meet.domain.com"
    component_secret = "REDACTED-Should auto populate"
VirtualHost "auth.meet.domain.com" -- VERIFY THIS PART
    ssl = {
       key = "/var/lib/prosody/auth.meet.domain.com.key";          
        certificate = "/var/lib/prosody/auth.meet.domain.com.crt"; 
    authentication = "internal_plain"
VirtualHost "guest.meet.domain.com"
    authentication = "anonymous"
Component "focus.meet.domain.com"
    component_secret = "REDACTED-Should auto populate"
Component "callcontrol.meet.domain.com" component_secret = "REDACTED-should auto populate"




Edit Prosody Global Config File

Next you'll need to modify your Prosody config file at /etc/prosody/prosody.cfg.lua
Add the following line:

    -- Force clients to use encrypted connections? This option will
-- prevent clients from authenticating unless they are using encryption.
consider_bosh_secure = true  --ADD ME
c2s_require_encryption = false
-- Force certificate authentication for server-to-server connections?
-- This provides ideal security, but requires servers you communicate
-- with to support encryption AND present valid, trusted certificates.
-- NOTE: Your version of LuaSec must support certificate verification!
-- For more information see http://prosody.im/doc/s2s#security




Edit Jicofo XMPP Auth Domains

Next we need to tell Jicofo's SIP Communicator to use an Auth Domain
Edit the file /etc/jitsi/jicofo/sip-communicator.properties and add the following line (the file may be empty, that's ok)


Edit Meet Anonymous Domain

Now we need a domain for non-authenticated users to use.
Add this in your config file at /etc/jitsi/meet/meet.domain.com-config.js

hosts: {
    // XMPP domain.
    domain: 'meet.domain.com',
    // XMPP MUC domain. FIXME: use XEP-0030 to discover it.
    muc: 'conference.meet.domain.com',
    // When using authentication, domain for guest users.
    anonymousdomain: 'guest.meet.domain.com'//,




NOTE: Make sure the domain and muc lines end in commas, and that you remove or comment the comma at the end of the anonymousdomain line, or else the config file format will be wrong, and you'll see just a blank gray screen when you load conferences.

Edit Jigasi XMPP Auth Settings

Now, edit your Jigasi settings to tell it not to use anonymous auth.
We will also need to set it to 'always trust' mode since our Prosody certificates are self-signed.
This file is /etc/jitsi/jigasi/sip-communicator.properties
NOTE: You will need an LDAP Service account to use for authentication against your LDAP server.

# If you want jigasi to perform authenticated login instead of anonymous login
# to the XMPP server, you can set the following properties.

# If you want to use the SIP user part of the incoming/outgoing call SIP URI
# you can set the following property to true.
# org.jitsi.jigasi.USE_SIP_USER_AS_XMPP_RESOURCE=true
# Activate this property if you are using self-signed certificates or other
# type of non-trusted certicates. In this mode your service trust in the
# remote certificates always.




Reboot, Customize, and Enjoy

At this point, reboot your server.
When it comes back, everything should work.
From here on out its just a matter of customizing your logos, messages, toolbars, etc. to meet your needs.

13 thoughts on “Jitsi Meet /w Call-In and Active Directory”

  1. Carlos says:

    Great howto!

    Can you please upload the FreePBX images again?

    1. evilcreamsicle says:

      Can you see them now?

  2. RS says:

    Can you please reupload the freepbx screenshots?

    1. evilcreamsicle says:

      Can you see them now?

  3. evilcreamsicle says:

    Sure thing guys, sorry. Give me a few minutes.

  4. Slava says:

    Hello evilcreamsicle, thank you for this amazing article! I managed to setup jigasi and free pbx as you described, however, when I call the number it does not asks for PIN – it is just ringing and… ringing. Could you please help me, by any chance?

    I’ve created an extension named `0204` and uploaded a recording named `WelcomePin`, so I modified dialplan script as follows:

    exten => 0204,1,Set(__RINGTIMER=${IF($[“${DB(AMPUSER/0204/ringtimer)}” > “0”]?${DB(AMPUSER/0204/ringtimer)}:${RINGTIMER_DEFAULT})})
    exten => 0204,2,Read(Pin,”custom/WelcomePin”)
    exten => 0204,3,Verbose(result is: ${Pin})
    exten => 0204,4,AGI(jitsi_curling.sh,${Pin})
    exten => 0204,5,Verbose(result is: ${JITSI})
    exten => 0204,6,SIPAddHeader(Jitsi-Conference-Room: ${JITSI})
    exten => 0204,7,Macro(exten-vm,novm,0204,0,0,0)
    exten => 0204,8(dest),Set(__PICKUPMARK=)
    exten => 0204,9,Goto(${IVR_CONTEXT},return,1)
    exten => 0204,hint,SIP/0204&Custom:DND0204,CustomPresence:0204

    I also created an executable bash script `jitsi_curling.sh` and created an inbound route in PBX for the number and pointed its destination to the extension: https://cl.ly/3Z2Y0o3x1X00

    There is also an asterisk log for what happens when the number is called: https://pastebin.com/XTayN2Bu

    Am I missing something here? Thank you in advance

    1. evilcreamsicle says:

      Let me take a look at my config and see if I’ve missed something.

    2. evilcreamsicle says:

      I think this may be my fault.

      My example extensions_override_freepbx.conf accidentally has an extra “[” at the beginning of the context name.

      Yours should look like this:

      exten => 0204,1,Set(__RINGTIMER=${IF($[“${DB(AMPUSER/0204/ringtimer)}” > “0”]?${DB(AMPUSER/0204/ringtimer)}:${RINGTIMER_DEFAULT})})
      exten => 0204,2,Read(Pin,”custom/WelcomePin”)
      exten => 0204,3,Verbose(result is: ${Pin})
      exten => 0204,4,AGI(jitsi_curling.sh,${Pin})
      exten => 0204,5,Verbose(result is: ${JITSI})
      exten => 0204,6,SIPAddHeader(Jitsi-Conference-Room: ${JITSI})
      exten => 0204,7,Macro(exten-vm,novm,0204,0,0,0)
      exten => 0204,8(dest),Set(__PICKUPMARK=)
      exten => 0204,9,Goto(${IVR_CONTEXT},return,1)
      exten => 0204,hint,SIP/0204&Custom:DND0204,CustomPresence:0204

      1. Slava says:

        Thank you! Looks like that was the issue. It works now

  5. Sebastian says:

    Hi, we’re trying to follow this tutorial and dial plan is not picking up our incoming calls. It just keeps ringing. … any tips?

    1. evilcreamsicle says:

      Sorry for the delay, I’ve had a lot going on this week.
      Do you see anything in your asterisk log for this call?

    2. evilcreamsicle says:

      Hi Sebastian.

      I made an error in my extensions_override_freepbx.conf example above.

      There is an extra “[” in front of the context name.

      The example should begin with “[ext-local]” and NOT with “[[ext-local]”

  6. Sigmaz says:

    Hey man.. I’m so glad you posted this tutorial.
    I have been trying to implement this as prescribed but I’m having some issues… most likely because I have s many questions.
    My first question is, why do the .php and .json file need to be on an external web server and not on the same server as the jitsi install?
    Also, does the cloud API have the ability to query my local install instances, or do I need to figure out how to configure/setup my own api file? (I am running the manual install with the sources local)

Leave a Reply

Your email address will not be published.