Readers will learn which clients are suitable for Android, iOS, Linux, Windows, and web interfaces, how to properly organize user administration and ensure privacy. This is a step-by-step guide that will help you create your own messenger for a family, team, or small organization, while maintaining the maximum level of privacy and stability.
Today, many people are starting to think about setting up their own messaging server. The problem with “homemade” servers and unpopular protocols is that it is difficult to drag all your friends or colleagues into a new system. But if it is only about communicating within the family, a small group of like-minded people or within the company, this can be a great solution.
Today, Matrix is often mentioned as a self-hosted alternative to popular messengers, for example, with the Element client. I tried it and I didn’t like it. The clients are slow, due to the unstable Internet channel everything works just disgustingly, and the server itself is simply clumsy and takes up not too much of the processor and memory of a VPS even with a couple of clients.
And then I remembered XMPP, also known as Jabber. It comes from the times when people used 200-300 megahertz processors and connected to the Internet via dial-up modems – that is, it is very undemanding to resources. Meanwhile, its development has not stood still, and today it can do almost everything that is needed from a modern messenger: history storage, file transfer, audio-video calls, end-to-end encryption, and so on.
I will configure the Prosody XMPP server. I have the system on an Ubuntu 22.04 server
We connect the official deb repositories from the Prosody project. Prosody itself is available in regular Debian and Ubuntu repos, but there are old versions, and I advise you to use a server version no lower than 0.12, and ideally version 13.x, because older versions lack some useful features, and some modules are not included in the delivery and need to be installed.
Therefore, we connect the repos from the developers to have the latest versions:
wget https://prosody.im/downloads/repos/$(lsb_release -sc)/prosody.sources -O/etc/apt/sources.list.d/prosody.sources apt update apt install prosody
I have version 13.0.2 installed. It requires a fairly new Lua interpreter (Prosody is written in Lua), and if when you start the service it swears at the interpreter, you need to install it and choose the correct one:
update-alternatives --config lua-interpreter # у списку вибираємо версію 5.4
Next, we need a domain for the server to work, XMPP works over TLS with all the outgoing ones. In principle, you can use a free domain from some dynu.com, freedns.afraid.org, or sslip.io, but here everything rests on trust in the service – whoever owns the domain has access to everything (unless you do certificate pinning). Paranoid people can use self-generated certificates without domains, manually adding them to the list of trusted ones – in prosody, generating self-signed certificates is very easy with the command prosodyctl cert generate hackyourmom.com (or use the direct IP address of the server instead of the domain).
Okay, let’s say we use the hackyourmom.com domain. We will need to point hackyourmom.com itself to the IP address of your server, and also create a subdomain like upload.hackyourmom.com (I’ll tell you why a little later), which will also point to the server IP.
Next, we obtain certificates via LetsEncrypt for your domain (if it already exists, you can skip it):
apt install certbot certbot certonly --standalone -d hackyourmom.com -d upload.hackyourmom.com
It will receive certificates from LetsEncrypt and configure their automatic renewal.
Now it’s time to edit the server config, it is usually located in /etc/prosody/prosody.cfg.lua.
At the beginning there is a list of modules activated on the server. I will give what I got as a result, and with a comment – ! I will mark those that were disabled in the default config and which I enabled specifically:
modules_enabled = {
"disco";
"roster";
"saslauth";
"tls";
"blocklist";
"bookmarks";
"carbons";
"dialback";
"limits";
"pep";
"private";
"smacks";
"vcard4";
"vcard_legacy";
"account_activity";
"cloud_notify";
"csi_simple";
"invites";
"invites_adhoc";
"invites_register";
"ping";
"register";
"time";
"uptime";
"version";
"cloud_notify_extensions"; -- ! додав
"turn_external"; -- ! додав
"mam"; -- ! додав
"muc_mam"; ! -- теж
"csi_simple"; -- ! додав
"pubsub"; -- ! може бути корисним
-- "http_file_share"; -- ! ось цей використовується, але закоментований, трохи згодом розповім чому
"admin_adhoc";
"admin_shell";
}
The mod_cloud_notify_extensions module is not included in the standard delivery, so it will need to be installed separately:
apt install liblua5.4-dev prosodyctl install --server=https://modules.prosody.im/rocks/ mod_cloud_notify_extensions
XMPP works on the principle of federation – that is, users of your server can communicate with users connected to other servers, and vice versa. If you need to make an isolated server so that no one can access it from the side, disable the s2s module:
modules_disabled = {
"s2s"; -- Handle server-to-server connections
}
Prosody also allows you to choose how user data (accounts, messages, etc.) will be stored. By default, it uses a simple file storage. You can choose to use the SQLite database, which will be more efficient. For servers with a large number of users and messages, you can use MySQL and PostgreSQL.
Next, scroll down to the VirtualHost section. By default, localhost is registered there, replace it with our main domain:
VirtualHost "hackyourmom.com"
Save the config, run:
prosodyctl --root cert import /etc/letsencrypt/live
Prosody will automatically find certificates for your domains and copy them to itself for use. To have them renew automatically, you can add a hook by creating a file type /etc/letsencrypt/renewal-hooks/deploy/prosody.sh with the following content:
#!/bin/sh /usr/bin/prosodyctl --root cert import /etc/letsencrypt/live
And make it executable:chmod +x /etc/letsencrypt/renewal-hooks/deploy/prosody.sh
In principle, everything is almost ready. We have configured the server with all the necessary modules, certificates and other things. And even E2E encryption (OMEMO) will work out of the box, if it is supported by clients.
But there are a few more nuances.
The first is file transfer. XMPP itself involves only peer to peer file transfer without server participation. This requires that both clients be online at the time of transfer, and in our time of ubiquitous NAT it practically does not work.
There is a solution – to use the http.file_share module. Then when sending a file via chat, the client will upload it to the HTTP server, and the interlocutors will receive a link to the downloaded file. The URL is a huge UUID, so it will not work to collect files on the server by brute force.
Let’s add the following text to the config right below the description of your VirtualHost:
VirtualHost "hackyourmom.com"
disco_items = {
{ "upload.hackyourmom.com", "file sharing service" },
}
Component "upload.hackyourmom.com" "http_file_share"
http_file_share_expires_after = 31 * 24 * 60 * 60 -- скільки повинні зберігатися файли на сервері, у моєму прикладі 31 день
http_file_share_global_quota = 1024*1024*1024*10 -- обмеження об'єму сховища на сервері, в моєму випадку 10 гігабайт
Since you explicitly declared “component” here, you don’t need to list “http_file_share” in the list of modules at the beginning of the config (I have it commented out there), otherwise the server will fight.
If for some reason you don’t like the idea of HTTP links, there is also the proxy_65 module (https://prosody.im/doc/modules/mod_proxy65), which allows you to transfer files through the server (using it as a proxy to bypass the problem of direct connections through NAT), but without storing them on the server.
The second is audio/video calls. In principle, what we have in the config is already enough for them. To establish connections between clients for audio/video calls in conditions where direct connections through NAT are impossible, the clients will use public STUN/TURN servers.
However, if you are paranoid, or want to not depend on anyone, you can raise your own TURN server:
apt install coturn
Next, edit /etc/turnserver.conf:
realm=hackyourmom.com use-auth-secret static-auth-secret=verysecretsecret
Restart coturn: systemctl restart coturn and add the following lines to the Prosody config above your VirtualHost description:
turn_external_host = "hackyourmom.com" turn_external_port = 3478 turn_external_secret = "verysecretsecret"
Of course, you can host coturn on a separate server, for example, by creating a domain for it turn.hackyourmom.com. And one more small touch – set up multi-user rooms (like “groups” in Teleza).
Let’s add:
Component "conference.hackyourmom.com" "muc"
Under your VirtualHost. By default, rooms can be created by anyone, but there is an option:
restrict_room_creation = false
with the options “false” (available to everyone), “true” (available only to admins) or “local” (available only to users of your server, strangers who came via server2server on the fly). There is an option there:
component_admins_as_room_owners = true
Automatic gives server admins owner rights in all rooms, and:
max_history_messages = 2000
Determines how many messages to keep in history. Now everything is ready. You can run a config check with the prosodyctl check command – the utility will check that everything is configured correctly and give advice if something can be improved. Don’t forget to restart the server after correcting the config: systemctl restart prosody and you can connect.
How to create new accounts on the server? In the config, you can allow registration for everyone through clients (this is more accurate), or you can add someone manually:
prosodyctl adduser [email protected]
The added user can also be added to the server admin list in the config, which will give him more powers (user management, if the client supports it, ad-hoc commands, viewing statistics, etc.):
admins = { "[email protected]" }
With mobile clients, everything is pretty good. For Android, the best of them is Conversations. It costs a couple of dollars on Google Play, but you can install it for free from F-Droid. Of the alternatives, I also liked Blabber.im. Video calls work both there and there.
On iOS, traditionally good clients are Siskin IM and its fork Snikket (I honestly didn’t notice the difference). Video calls work. Sometimes they also mention the ChatSecure client, but be careful with it – it seems to be in a half-dead state, at least the domain used there for push notifications no longer exists.
For desktop… Well, there are some. There are many old “classic” clients like Gajim, Pidjin, Psi, but they have an interface from the 90s and often don’t support modern features. If you are not allergic to Electron, I recommend ConverseJS – a stylish and convenient interface, it can do everything you need (except audio calls), can be run as an Electron application, or you can put it on your server and connect from a browser.
Of the clients that claim support for audio and video calls – Movim (also web), and someone praised Dino (Linux, but there is an unofficial build for Windows), and BeagleIM under MacOS. Kaidan under Linux looks very similar to Wiz, but it doesn’t seem to be able to do video calls.
And there is also the proprietary AstraChat, which, as stated, can do everything, but you can’t just download it from the developer’s website (you need to register with a corporate email), someone is posting binaries on GitHub, but that’s at your own risk.
And finally, the question is – how resistant is this whole thing to blocking, if the RKN decides to somehow calculate and block your server for some reason? XMPP by default works on the standard port 5222. You can put something like sslh on port 443, behind which there will also be a harmless web server, and connect clients to port 443 as if it were a regular HTTPS.
The web server for files in Prosody also hangs on a non-standard port and returns the prosody page when requested with the URL /. You can hide it behind a web server like Nginx or Caddy, so that they return their page on /, and in case of a 404 error – too (and of course listen on port 443, in the http_file_share module there is a parameter that tells users where to go).
As an even more efficient solution, there are XMPP extensions to work over pure HTTP (bosh module) and web sockets (websocket module), which in theory allows you to completely disguise yourself as a regular web server and even work via a CDN. But I haven’t come across any clients that can do this yet, if anyone knows – write in the comments.