Install Jitsi-Meet alongside ejabberd

Image of an office conference room with empty chairs

Since the corona virus is forcing many of us into home office there is a high demand for video conference solutions. A popular free and open source tool for creating video conferences similar to Google’s hangouts is Jitsi Meet. It enables you to create a conference room from within your browser for which you can then share a link to your coworkers. No client software is needed at all (except mobile devices).

The installation of Jitsi Meet is super straight forward – if you have a dedicated server sitting around. Simply add the jitsi repository to your package manager and (in case of debian based systems) type

sudo apt-get install jitsi-meet

The installer will guide you through most of the process (setting up nginx / apache, installing dependencies, even do the letsencrypt setup) and in the end you can start video calling! The quick start guide does a better job explaining this than I do.

Jitsi Meet is a suite of different components that all play together (see Jitsi Meet manual). Part of the mix is a prosody XMPP server that is used for signalling. That means if you want to have the simple easy setup experience, your server must not already run another XMPP server. Otherwise you’ll have to do some manual configuration ahead of you.

I did that.

Since I already run a personal ejabberd XMPP server and don’t have any virtualization tools at hands, I wanted to make jitsi-meet use ejabberd instead of prosody. In the end both should be equally suited for the job.

Looking at the prosody configuration file that comes with Jitsi’s bundled prosody we can see that Jitsi Meet requires the XMPP server to serve two different virtual hosts.
The file is located under /etc/prosody/conf.d/meet.example.org.cfg.lua

VirtualHost "meet.example.org"
        authentication = "anonymous"
        ssl = {
                ...
        }
        modules_enabled = {
            "bosh";
            "pubsub";
            "ping";
        }
        c2s_require_encryption = false

Component "conference.meet.example.org" "muc"
    storage = "memory"
admins = { "focus@auth.meet.example.org" }

Component "jitsi-videobridge.meet.example.org"
    component_secret = "SECRET1"

VirtualHost "auth.meet.example.org"
    ssl = {
        ...
    }
    authentication = "internal_plain"

Component "focus.meet.example.org"
    component_secret = "SECRET2"

Remember to replace SECRET1 and SECRET2 with secure secrets! There are also some external components that need to be configured. This is where Jitsi Meet plugs into the XMPP server.

In my case I don’t want to server 3 virtual hosts with my ejabberd, so I decided to replace auth.meet.jabberhead.tk with my already existing main domain jabberhead.tk which already uses internal authentication. So all I had to do is to add the virtual host meet.jabberhead.tk to my ejabberd.yml and configure it to use anonymous authentication.
The ejabberd config file is located under /etc/ejabberd/ejabberd.yml or /opt/ejabberd/conf/ejabberd.yml depending on your ejabberd distribution.

hosts:
    ## serves as main host, as well as auth.meet.jabberhead.tk for focus user
  - "jabberhead.tk"
    ## serves as anonymous authentication host for meet.jabberhead.tk
  - "meet.jabberhead.tk"
...
host_config:
  meet.jabberhead.tk:
    auth_method: anonymous
    allow_multiple_connections: true
    anonymous_protocol: both

The syntax for external components is quite different for ejabberd than it is for prosody, so it took me some time to get it working.

listen:
 -
    port: 5280
    ip: "::"
    module: ejabberd_http
    request_handlers:
      ## Not sure if this is needed, but by default jitsi-meet uses http-bind for bosh
      "/http-bind": mod_bosh
      "/bosh": mod_bosh
    tls: true
    protocol_options: 'TLS_OPTIONS'
  -
    port: 5275
    ip: "::"
    module: ejabberd_service
    access: all
    shaper: fast
    hosts:
      "jitsi-videobridge.jabberhead.tk":
        password: "SECRET1"
  -
    port: 5347
    module: ejabberd_service
    hosts:
      "focus.jabberhead.tk":
        password: "SECRET2"

By re-reading the config files now, I wonder why I ended up placing the focus component under the host focus.jabberhead.tk and not focus.meet.jabberhead.tk, but hey – it works and I’m too scared to touch it again 😛

The configuration of the modules was a bit trickier on ejabberd, as the ejabberd config syntax seems to disallow duplicate entries. In this case I had to configure mod_muc for my main domain different than for the meet.jabberhead.tk domain, so I had to move the original mod_muc and mod_muc_admin configuration out of the modules: block and into an append_host_config: block with different settings per domain.

append_host_config:
  jabberhead.tk:
    modules:
        ## This is the original muc configuration I used before
        mod_muc:
          access:
            - allow
          access_admin:
            - allow: admin
          access_create: muc_create
          access_persistent: muc_create
          access_mam:
            - allow
          default_room_options:
            allow_private_messages: true
            mam: true
            persistent: true
        mod_muc_admin: {}
  meet.jabberhead.tk:
    modules:
      ## This is the config only for meet.jabberhead.tk
      mod_muc:
        host: conference.meet.jabberhead.tk
      mod_muc_admin: {}

mod_pubsub, mod_ping and mod_bosh all have to be enabled, but can stay in the global modules: block.

Last but not least we have to add the focus user as an admin and also generate (not discussed here) and add certificates for the meet.jabberhead.tk subdomain.

certfiles:
  - ...
  - "/etc/ssl/meet.jabberhead.tk/cert.pem"
  - "/etc/ssl/meet.jabberhead.tk/fullchain.pem"
  - "/etc/ssl/meet.jabberhead.tk/privkey.pem"
...
acl:
  admin:
    user:
      - "focus@jabberhead.tk"

That’s it for the ejabberd configuration. Now we have to configure the other Jitsi Meet components. Lets start with jicofo, the Jitsi Conference Focus component.

My /etc/jitsi/jicofo/config file looks as follows.

JICOFO_HOST=jabberhead.tk
JICOFO_HOSTNAME=jabberhead.tk
JICOFO_SECRET=SECRET2
JICOFO_PORT=5347
JICOFO_AUTH_DOMAIN=jabberhead.tk
JICOFO_AUTH_USER=focus
JICOFO_AUTH_PASSWORD=SECRET3
JICOFO_OPTS=""
# Below can be left as is.
JAVA_SYS_PROPS=...

Respectively the videobridge configuration (/etc/jitsi/videobridge/config) looks like this:

JVB_HOSTNAME=jabberhead.tk
JVB_HOST=localhost
JVB_PORT=5275
JVB_SECRET=SECRET1
## Leave below as originally was
JAVA_SYS_PROPS=...

Some changes had to be made to /etc/jitsi/videobridge/sip-communicator.properties:

org.jitsi.videobridge.AUTHORIZED_SOURCE_REGEXP=focus@jabberhead.tk/.*
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<IP-OF-YOUR-SERVER>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<IP-OF-YOUR-SERVER>
org.jitsi.videobridge.TCP_HARVESTER_PORT=4443

Now we can wire it all together by modifying the Jitsi Meet config file found under /etc/jitsi/meet/meet.example.org-config.js:

var config = {
    hosts: {
        domain: 'jabberhead.tk',
        anonymousdomain: 'meet.jabberhead.tk',
        authdomain: 'jabberhead.tk',
        bridge: 'jitsi-videobridge.meet.jabberhead.tk',
        focus: 'focus.jabberhead.tk',
        muc: 'conference.meet.jabberhead.tk'
    },
    bosh: '//meet.jabberhead.tk/http-bind',
    clientNode: 'http://jitsi.org/jitsimeet',
    focusUserJid: 'focus@jabberhead.tk',

    testing: {
    ...
    }
...
}

Last but not least my nginx host configuration (/etc/nginx/sites-available/meet.example.org.conf) where all I changed was the bosh configuration (went from http to https, I heard people say this is not necessary though).

server {
    listen 80;
    server_name meet.jabberhead.tk;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    server_name meet.jabberhead.tk;
    ...
    # BOSH
    location = /http-bind {
        proxy_pass      https://localhost:5280/http-bind;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $http_host;
    }
    ...
}

Finally of course, I also had to register the focus user as an XMPP account:

ejabberdctl register focus jabberhead.tk SECRET3

Remember to use a safe password instead of SECRET3 and also stop and disable the bundled prosody! That’s it!

I hope this lowers the bar for some to deploy Jitsi Meet next to their already existing ejabberd. Lastly please do not ask me for support, as I barely managed to get this working for myself 😛

OMEMO Specification Sprint

Photo of an orange and white clown fish inside an anemone

The past weekend some members of the XMPP community gathered in Düsseldorf to work on the next iteration of the OMEMO Encryption Specification. All of us agree that the result – version 0.4 of XEP-0384 – is a huge step forward and better than ever!

On Saturday morning we met up at the Chaosdorf, a local Hacker Space who’s members kindly hosted the sprint. Huge thanks to them for having us!

Prior to the sprint we had collected a list of bullet points of topics we wanted to discuss. Among the more urging topics was proper specification of OMEMO for group chats, support for encrypting extension elements other than the body, as well as clarification on how to implement OMEMO without having to use libsignal. While the latter was technically already possible having a clear written documentation on how to do it is very important.

We spent most of the first day discussing several changes, features and problems and later started writing down the solutions we found. In between – in true Düsseldorf fashion – we snacked on some Onigiri and later went for some nice Ramen together. Saturday afternoon we started working in smaller groups on different parts of the specification. I’m amazed by the know-how and technical understanding that people brought to the table!

On the second day I had to leave relatively early after lunchtime due to family commitments, so I could only follow the remaining development of the XEP via git commits on the train.

Apart from further clarification, the updated spec now contains some additional features and tweaks. It is now possible to encrypt near arbitrary contents of messages with the help of Stanza Content Encryption. OMEMO now defines its own SCE profile. This enables workflows like fully end-to-end encrypted read markers and reactions. Thanks to Marvin and Klaus, the specification now also contains a section about how to opt-out of OMEMO encryption, both completely as well as on a per-conversation basis. Now you no longer have to manually disable OMEMO for that one contact on EVERY device you own.

The biggest part of the discussions went into properly specifying the cryptographic primitives for use with the Double Ratchet Algorithm. Tim and Andy did a great job of describing how to use hash functions and cipher algorithms to keep be able to re-implement OMEMO without having to rely on libsignal alone. Klaus and Marvin figured out some sane rules that help to decide when a device becomes active / inactive / stale. This should preserve the cryptographic guarantees of the encryption even if you don’t use one of your devices for a longer time.

Daniel properly described the workflow of recovering from broken sessions. This should improve OMEMO session stability. He also defined the exact form of OMEMO related XML elements. One notable feature from a users perspective are human readable labels for identity keys. This should make it easier for you to distinguish keys from another.

I’m really excited about the changes and can’t wait to see the first implementations in the real world!

One thing that’s left to do for now is to determine a smooth upgrade path. Clients will probably have to use both the new and old OMEMO in parallel for some time, as the changes are not backwards compatible. This would mean that we cannot immediately benefit from stanza content encryption and are bound to body-only encryption for some more time.