Post

Installation of Prosody XMPP server

Choosing “the” right messenger is not an easy task as you (or at least I do) want to pick one with a good balance of usability on the one side and security/privacy on the other. And of course your friends and family need to agree on it as well - otherwise it’s a pretty lonesome experience… ;-)

Probably messengers you could easily agree on would be Signal or Threema. Both are easy to use and come with strong end-2-end encryption. However both are not federated: there is one central server which you need to rely on. Also both lack real multi-device support and Signal even requires your mobile number. Nevertheless, both are pretty good and I’d recommend them.

However, my personal preference would be either a Matrix or XMPP (formerly “Jabber”) based messenger. Both come with end-2-end encryption (for the latter make sure to enable OMEMO), are federated and no personal information is required to sign on. Also both feature real multi-device support. As I enjoy playing around with stuff like this I decided to install an XMPP server, Prosody in particular. See the official documentation here.

Prerequisites

I’m assuming Ubuntu 20.04 as operating system and that you’ve done a basic setup/hardening of your server. From what I see Prosody does not have very huge hardware requirements but of course this will depend on the amount of users you’re going to expect. For a small instance 1GB RAM and probably 1 or 2 CPU cores will be sufficient.

DNS records

Depending on which services you’d like to offer, you will have to create a couple of DNS records. Let’s go with example.com. Assuming this will be your XMPP domain, you will create user names like <username>@example.com. Let example.com point to your server IP (A-record) and then create some C-names all pointing to example.com:

conference.example.com

pubsub.example.com

proxy.example.com

uploads.example.com

turn.example.com

Also make sure to create some srv records:

serviceprotocolpriorityweightporttarget
xmpps-clienttcp11443example.net
xmpp-servertcp115269example.net
xmpp-clienttcp515222example.net

If port 443 is not available because there’s a web server already running on that port: don’t worry, see below.

Software

As I’d install the latest version of Prosody let’s add its repository (example for Ubuntu 20.04):

1
2
3
$ echo "deb [signed-by=/usr/share/keyrings/prosody-keyring.gpg] https://packages.prosody.im/debian focal main" > /etc/apt/sources.list.d/prosody.list
$ wget https://prosody.im/files/prosody-debian-packages.key -O /usr/share/keyrings/prosody-keyring.gpg
$ apt get update

…and actually install everything:

1
2
$ apt install prosody lua-dbi-postgresql postgresql snapd fuse coturn nginx
$ systemctl stop nginx

Configuration

Prosody should now be running already, however some configuration changes need to be applied of course.

Postgresql

First I’d set password_encryption = scram-sha-256 in /etc/postgresql/12/main/postgresql.conf and also set scram-sha-256 in /etc/postgresql/12/main/pg_hba.conf:

1
2
3
4
# IPv4 local connections:
host    all             all             127.0.0.1/32            scram-sha-256
# IPv6 local connections:
host    all             all             ::1/128                 scram-sha-256

Restart Postgresql and create a user and a database for Prosody:

1
2
3
4
$ systemctl restart postgresql
$ su - postgresql
$ createuser --pwprompt <DB_USER>
$ createdb --owner=<DB_USER> <DB_NAME>

Take a note of the password you choose.

Certificates

Install certbot and get required certificates (open ports 80 and 443):

1
2
3
4
5
6
$ snap install core; snap refresh core
$ snap install --classic certbot
$ ufw allow 80/tcp
$ ufw allow 443/tcp
$ certbot certonly --standalone -d example.com -d conference.example.com -d pubsub.example.com -d proxy.example.com -d uploads.example.com
$ certbot certonly --standalone -d turn.example.com

Coturn

Make sure coturn does not run as root. I think with Ubuntu 20.04 it’s not default anymore, but check it anyway: proc-user= and proc-group= should both be set to turnserver in /etc/turnserver.conf. With coturn not running as privileged user it cannot read the certificates generated by certbot. I’d copy the “turn.example.com” certificates to a new location and make them readable for group turnserver. (Don’t forget to copy them each time you’ll update the certificates with certbot!) Add the correct path in /etc/turnserver.conf to cert= and pkey=. Also make sure lt-cred-mech and use-auth-secret are not commented, set a secret for static-auth-secret=. realm= should point to turn.example.com. Tweak the rest of the settings to your likings. Then restart coturn:

1
$ systemctl restart coturn

Prosody

Taking care of the certificates is quite easy (s. also here):

1
$ prosodyctl --root cert import /etc/letsencrypt/live

Make sure certificates = "certs" is not commented in /etc/prosody/prosody.cfg.lua.

There are a few additional settings to change in /etc/prosody/prosody.cfg.lua:

  • add an admin user (we’ll actually create it later): admins = { "<USERNAME>@example.com" }
  • enable required modules by removing “--”, I’d keep/enable all in sections “Generally required”, “Not essential, but recommended” and “Nice to have” with exception maybe of "version"; and "register"; (the latter depending if you would like to let users register)
  • also probably enable:
    • "announce";
    • "groups";
    • "motd";
    • "proxy65";
  • enable Postgresql as storage:
    • storage = "sql"
    • sql = { driver = "PostgreSQL", database = "<DB_NAME>", username = "<DB_USER>", password = "<DB_PASSWORD>", host = "localhost" }
  • configure turnsever:
    • turn_external_host = "turn.example.com"
    • turn_external_secret = "<YOUR_SECRET>"
  • set ports/encryption handling:
    • c2s_require_encryption = true
    • c2s_direct_tls_ports = 5223
    • s2s_require_encryption = true
    • s2s_direct_tls_ports = 5270
    • s2s_secure_auth = true
  • add a new virtual host: VirtualHost "example.com"
  • below that virtual host enable/configure a few modules:
    • modules_enabled = {"cloud_notify";}
    • modules_enabled = {"muc_mam";}
    • Component "uploads.example.com" "http_file_share"
    • Component "proxy.example.com" "proxy65"
    • Component "pubsub.example.com" "pubsub"
  • finally remove world readability: chmod 640 /etc/prosody/prosody.cfg.lua

We already enabled one module which actually is not installed yet: cloud_notify. To install the additional module follow this documentation: https://prosody.im/doc/installing_modules

As you might have noticed we have created an srv record for encrypted client connections (xmpps-client) pointing to port 443, however the Prosody configuration above is pointing to port 5223. This does of course not add up. As Prosody is not running as root (which it shouldn’t!) it cannot open a privileged port anyway. So we will leave that to nginx by redirecting XMMP traffic to port 5223. If you already have a webserver listening on port 443 this solution comes handy as well. To do that we will need the following:

  • make sure that “include /etc/nginx/conf.d/*.conf;” in /etc/nginx/nginx.conf is outside of “http { }
  • reconfigure your webserver to listen to a different port i.e. 9443 (if you don’t have a webserver listening on port 443 simply skip corresponding settings)
  • create a new configuration file in /etc/nginx/conf.d/ with following content (s. nginx documentation here):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    stream {
      map $ssl_preread_alpn_protocols $proxy {
          ~\bhttp/1.1\b     <SERVER_IP>:9443;
          ~\bxmpp-client\b  <SERVER_IP>:5223;
      }
    
      server {
          listen 443;
          ssl_preread on;
          proxy_pass $proxy;
      }
    }
    

    HTTP traffic will be redirected to “SERVER_IP:9443” and XMPP traffic will go to “SERVER_IP:5223”.

Now restart Prosody and nginx:

1
2
$ systemctl restart prosody
$ systemctl restart nginx

Let’s finally create the first user:

1
$ prosodyctl adduser <USERNAME>@example.com

You should choose the same USERNAME as the one you configured as “admin” above. Of course you can add additional users the same way. Only users listed as “admins” in the configuration file will actually have administrative privileges.

Firewall ports

At last we need to open some additional ports on our firewall:

1
2
3
4
5
6
7
8
9
10
$ ufw allow 3478 #turnserver
$ ufw allow 5349 #turnserver
$ ufw allow 49152:65535/udp #turnserver
$ ufw allow 5000/tcp #prosody file transfer proxy
$ ufw allow 5222/tcp #prosody client connections
$ ufw allow 5269/tcp #prosody server-to-server connections
$ ufw allow 5281/tcp #prosody HTTPS
$ ufw allow 5270/tcp #prosody server-to-server direct TLS connection
$ ufw allow 5223/tcp #prosody client direct TLS connections
$ ufw allow 9443/tcp #webserver

Compliance test

You might want to register your server with https://compliance.conversations.im/ to check your server’s compliance score (create an unprivileged test user for this matter). If you followed the instructions above you should gain at least 95%. The only test failing is the one for “XEP-0153”. However, according to Prosody’s documentation “XEP-0153” should be enabled anyway. Not sure why that check is failing.

Finally…

…create users for your friends and family or simply enable registration via register module and install a client. There are several clients out there for nearly every OS. (However, make sure to enable OMEMO for your chats to have them end-2-end encrypted!) Just to list a few:

This post is licensed under CC BY-SA 4.0 by the author.