WhatsApp в России депутаты обещают заблокировать уже в очень скором времени. Доверие к Telegram у многих довольно сильно было подорвано после публикации расследования одного издания об особенностях их протокола вкупе с подробностями о том, кто владеет их сетевой инфраструктурой и с кем он связан (администрация Хабра попросила меня не прикладывать ссылку, т.к. издание признано "нежелательной организацией" в РФ), да и недавнее появление Telegram в реестре РКН тоже оптимизма в их отношении не добавляет. Max - оставим для сумасшедших и безвыходных, учитывая, кто и зачем его создал. Signal - отличный мессенджер и всем хорош, но в России его тоже периодически пытаются заблокировать.

На фоне всего этого многие начали задумываться о поднятии своего сервера для обмена сообщениями. С самодельными серверами и "не-попсовыми" протоколами всегда встает проблема "а с кем там общаться?", потому что перетащить прям вот вообще всех собеседников и контрагентов на что-то им непривычное практически нереально. Но если речь идет только, например, об инструменте общения внутри семьи, небольшой группы единомышленников или внутри одной компании, то это может быть вполне неплохим вариантом.

В наше время как self-hosted альтернативу популярным мессенджерам часто упоминают Matrix, например, с клиентом Element. На Хабре есть подробные инструкции, например вот эта: https://habr.com/ru/articles/837904/. Я попробовал, и мне не понравилось. Клиенты тормозные, через нестабильный интернет-канал все работает просто отвратительно, а сам сервер просто неповоротливый и укладывает не слишком богатый на процессор и память VPS даже всего с парой клиентов.

И тут мне вспомнилось про XMPP, он же Jabber. Он родом еще из тех времен, когда люди пользовались процессорами на 200-300 мегагерц и подключались к интернету через dial-up модемы - то есть он изначально очень нетребовательный к ресурсам. Между тем, развитие его не замерло на месте, а на сегодняшний день он умеет почти все то что требуется от современного мессенджера: хранение истории, передача файлов, аудио-видео звонки, end-to-end шифрование, и другое.

Я буду настраивать XMPP-сервер Prosody. Система у меня на сервере Ubuntu 22.04

Подключаем официальные deb-репозитории от проекта Prosody. Сам prosody есть в обычных репах Debian и Ubuntu, но там старые версии, а я советую использовать версию сервера не ниже 0.12, а в идеале версию 13.x, потому что в более старых версиях не хватает некоторых полезных фич, и некоторые модули не включены в поставку и их нужно устанавливать отдельно. Поэтому подключаем репы от разработчиков, чтобы иметь самые свежие версии:

wget https://prosody.im/downloads/repos/$(lsb_release -sc)/prosody.sources -O/etc/apt/sources.list.d/prosody.sources

apt update
apt install prosody

У меня установилась версия 13.0.2. Она требует довольно свежего интерпретатора Lua (Prosody написан на Lua), и если при запуске сервиса оно ругается на интерпретатор, нужно доустановить и выбрать правильный:

update-alternatives --config lua-interpreter
# в появившемся списке выбираем версию 5.4

Далее нам нужен домен для работы сервера, XMPP работает через TLS со всеми вытекающими. В принципе, можно использовать и бесплатный домен с какого-нибудь dynu.com, freedns.afraid.org, или даже sslip.io, но тут все упирается в доверие к сервису - кто владеет доменом, тот имеет доступ ко всему (если только вы не сделаете certificate pinning). Параноики могут и без доменов использовать самосгенеренные сертификаты, вручную добавляя их в список доверенных - в prosody генерация самоподписанных сертификатов делается очень легко командой prosodyctl cert generate habr.com (либо используйте прям IP-адрес сервера вместо домена).

Окей, допустим мы используем домен habr.com. Нам нужно будет направить на IP-адрес вашего сервера сам habr.com, и еще сделать поддомен типа upload.habr.com (чуть позже расскажу зачем), который тоже будет указывать на IP сервера.

Далее делаем получение сертификатов через LetsEncrypt для вашего домена (если оно у вас уже есть, можете пропустить):

apt install certbot
certbot certonly --standalone -d habr.com -d upload.habr.com

Оно получит от LetsEncrypt сертификаты и настроит их автоматическое обновление.

Теперь настало время отредактировать конфиг сервера, он располагается обычно в /etc/prosody/prosody.cfg.lua.

В самом начале там идет список модулей, активированных на сервере. Я приведу то что в итоге получилось у меня, а комментарием -- ! отмечу те, что были отключены в конфиге по умолчанию и которые я включил специально:

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"; -- ! добавил
                "csi_simple"; -- добавил
                -- "http_file_share"; -- ! вот этот используется, но закомментирован, чуть позже расскажу почему
                "admin_adhoc";
                "admin_shell";
}

Модуль mod_cloud_notify_extensions не входит в стандартную поставку, поэтому его надо будет доустановить отдельно:

apt install liblua5.4-dev
prosodyctl install --server=https://modules.prosody.im/rocks/ mod_cloud_notify_extensions

Также Prosody позволяет выбрать, как будут храниться данные пользователей (аккаунты, сообщения, и т.д.). По умолчанию используется хранилище на простых файлах. Можно выбрать чтобы использовалась база SQLite, это будет более эффективно. Для серверов с большим количеством пользователей и сообщений можно использовать MySQL и PostgreSQL.

Дальше скроллим ниже до раздела с VirtualHost. По умолчанию там прописан localhost, заменяем его на наш основной домен:

VirtualHost "habr.com"

Сохраняем конфиг, запускаем

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

Prosody автоматически найдет сертификаты для доменов и скопирует их к себе для использования. Чтобы они обновлялись автоматически, можно добавить хук, создав файл типа
/etc/letsencrypt/renewal-hooks/deploy/prosody.sh
с содержимым

#!/bin/sh
/usr/bin/prosodyctl --root cert import /etc/letsencrypt/live

и сделать его исполняемым: chmod +x /etc/letsencrypt/renewal-hooks/deploy/prosody.sh

В принципе, все почти готово. Мы настроили сервер со всеми необходимыми модулями, сертификатами, и прочим. И даже E2E-шифрование (OMEMO) будет работать из коробки, если оно поддерживается клиентами.
Но есть еще пара нюансов.

Первое - это передача файлов. Сам по себе XMPP предусматривает передачу файлов только peer to peer без участия сервера. Это требует чтобы оба клиента в момент передачи находились онлайн, да и в наши времена повсеместного NAT'а практически не работает.

Есть решение - использовать http_file_share модуль. Тогда при отправке файла через чат, клиент зальет его на сервер по HTTP, а собеседники получат ссылку на загруженный файл. URL представялет из себя огромный UUID, так что перебором собрать файлы на сервере не получится.
Добавим в конфиг следующий текст прямо под описанием вашего VirtualHost:

VirtualHost "habr.com"
disco_items = {
    { "upload.habr.com", "file sharing service" },
}
Component "upload.habr.com" "http_file_share"
http_file_share_expires_after = 31 * 24 * 60 * 60 -- сколько должны храниться файлы на сервере, в моем примере 31 день 
http_file_share_global_quota = 1024*1024*1024*10 -- ограничение объема хранилища на сервере, в моем случае 10 гигабайт

Поскольку вы тут явно объявили "component", не надо перечислять "http_file_share" в списке модулей в начале конфига (у меня он там закомментирован), иначе сервер ругается.

Если по каким-то причинам вам не нравится идея с HTTP-ссылками, есть еще модуль proxy_65 (https://prosody.im/doc/modules/mod_proxy65), который позволяет передавать файлы через сервер (используя его как прокси чтобы обойти проблему прямых подключений через NAT), но не сохраняя их на сервере.

Второе - это аудио/видео звонки. В принципе, того, что мы имеем в конфиге, уже достаточно для них. Для установления соединений между клиентами для аудио-видеозвноков в условиях невозможности прямых подключений из-за NAT'а клиенты будут использовать публичные STUN/TURN сервера.
Однако если вы параноик, или хочется ни от кого не зависеть, можно поднять свой TURN-сервер:

apt install coturn

Далее редактируем /etc/turnserver.conf:

realm=habr.com
use-auth-secret
static-auth-secret=verysecretsecret

Перезапускаем coturn: systemctl restart coturn и добавлем в конфиг Prosody выше описания вашего VirtualHost следущие строки:

turn_external_host = "habr.com"
turn_external_port = 3478
turn_external_secret = "verysecretsecret"

Естественно, coturn вы можете разместить на отдельном сервере, например сделав для него домен turn.habr.com

Вот теперь все готово.
Можно запустить проверку конфига командой prosodyctl check - утилита проверит, что все настроено правильно и даст советы, если что-то можно улучшить.

Не забываем после правки конфига перезапустить сервер: systemctl restart prosody и можно подключаться.

Как создавать новые аккаунты на сервер? В конфиге можно разрешить регистрацию для всех желающих через клиенты (с этим аккуратнее), а можно добавлять кого надо вручную:

prosodyctl adduser user@habr.com

А теперь про клиенты.
С мобильными клиентами все довольно неплохо.
Для Android лучшим из них считается Conversations. В Google Play он стоит пару долларов, но можно бесплатно установить его из F-Droid.
Из альтернатив мне еще понравился Blabber.im
Видеозвонки работают и там и там.

На iOS традиционно хорошими клиентами считаются Siskin IM и его форк Snikket (я честно говоря, разницы не заметил). Видеозвонки работают.
Еще иногда упоминают клиент ChatSecure, с ним осторожнее - он кажется в полумертвом состоянии, по крайней мере домен, который там используется для push-уведомлений уже не существует.

Под дестктоп же... Ну, там такое. Есть много старых "классических" клиентов типа Gajim, Pidjin, Psi, но они имеют интерфейс из 90-х и часто не поддерживают современные фичи.
Если у вас нет аллергии на Electron, советую ConverseJS - стильный и удобный интерфейс, умеет все что надо (кроме аудиозвонков), может запускаться как Electron-приложение, а можно поставить его на свой сервер и подключаться из браузера.

Из клиентов, в которых заявлена поддержка аудиовидеозвонков - Movim (тоже веб), и кто-то хвалил Dino (Linux, но есть неофициальная сборка под Windows), и BeagleIM под MacOS. Kaidan под Linux выглядит очень похоже на Телегу, но видеозвонки, кажется, не умеет.

И еще есть проприетарный AstraChat, который, как заявлено, умеет все, но скачать просто так с сайта разработчика его нельзя (требуется регистрация на корп. емайл), на гитхабе кто-то выкладывает бинарники, но это уже на ваш страх и риск.

И в заключение вопрос - а насколько все это дело устойчиво к блокировкам, если РКН решит как-то вычислить и заблокировать именно ваш сервер по какой-то причине?
XMPP по умолчанию работает по стандартному порту 5222. Можно поставить на 443 порт что-то типа sslh, за которым еще будет стоять безобидный веб-сервер, и коннектиться клиентами на порт 443 как будто это обычный HTTPS.
Веб-сервер для файлов у Prosody висит тоже на нестандартном порту и отдает страничку prosody при запросе с урлом /. Можно спрятать его за веб-сервером типа Nginx или Caddy, чтобы по / они отдавали свою страницу, и при 404 ошибке - тоже (ну и само собой слушали на 443 порту, в http_file_share модуле есть параметр, который сообщает пользователям куда надо стучаться).

Как еще более эффективное решение, существуют расширения XMPP для работы через чистый HTTP (модуль bosh) и вебсокеты (модуль websocket), что в теории позволяет полностью замаскироваться под обычный веб-сервер и даже работать через CDN. Но клиентов, умеющих подобное, мне пока не встретилось, если кто-то знает - напишите в комментариях.

Комментарии (43)


  1. lv333
    26.07.2025 10:21

    Делать альтернативу и упирается вот в это:

    Далее нам нужен домен для работы сервера, XMPP работает через TLS со всеми вытекающими, без домена никак. В принципе, можно использовать и бесплатный домен с какого-нибудь dynu.com, freedns.afraid.org, или даже sslip.io, но тут все упирается в доверие к сервису - кто владеет доменом, тот имеет доступ ко всему (если только вы не сделаете certificate pinning).

    Немного неосмотрительно мягко говоря, а если доступ к домену будет по какой то причине утерян? Если нарушится связность с некоторыми сегментами интернет? Не будет доступа к ДНС и/или к серверу сертификации? Если уж желаете неубиваемую болталку, то она должна быть способна работать хоть в локальной сети на два устройства + сервер, а в идеале вообще без сервера в режиме точка-точка + меш из многих точек, но это уже разумеется уровень железа и каналов затрагивает уже. А пока это песочная "крепость" на пляже. Но это разумеется мое имхо...


    1. Uporoty Автор
      26.07.2025 10:21

      Параноики могут и без доменов использовать самосгенеренные сертификаты, вручную добавляя их в список доверенных.


      1. lv333
        26.07.2025 10:21

        Наверное это стоило написать, а ещё лучше описать настройку прямо в статье?


        1. Uporoty Автор
          26.07.2025 10:21

          добавил


          1. lv333
            26.07.2025 10:21

            И кстати зачем этим всем заниматься если вы не параноик реалист? Всегда же есть "прекрасная альтернатива" этим вашим враждебным сервисам! ;)


            1. Uporoty Автор
              26.07.2025 10:21

              Не понял вопроса, перефразируйте пожалуйста


              1. haga777
                26.07.2025 10:21

                МаКсимально пафосный троллинг просто


      1. dartraiden
        26.07.2025 10:21

        А ориентировочно в конце этого года можно будет у LE получать валидные сертификаты для IP-адресов.


        1. lv333
          26.07.2025 10:21

          Это не решает вопроса с доступностью самого LE в определенный момент с определенного сегмента сети.


      1. kenomimi
        26.07.2025 10:21

        Не параноики.

        Во-первых, выставлять что-то без крайней необходимости в интернет на публичные адреса в наше время весьма попрочная практика. Уязвимостей в таком комплексном софте настолько много, что взлом - дело времени. И хорошо, если что-то лайтовое зальют - там ведь могут начать цопе распространять, или пропаганду запрещ енки вести - а отбрехиваться от товарища майора вам потом.

        Во-вторых, сервисы "для себя и компании" часто сидят на приватном TLD типа ".lan" внутри локалки, куда доступ через ipsec (или другой впн). Это наиболее безопасное и взломостойкое решение... Там как ни крути, но серты самому делать, поскольку на приватный TLD получить нормальный серт нереально...


        1. Uporoty Автор
          26.07.2025 10:21

          Когда поднял сервер для пары друзей, требовать подключаться к нему исключительно через постоянно поднятый VPN - затея довольно сомнительная и всетаки ближе к паранойе.

          А если сервер только внутри локалки, это никак не мешает использовать LE. В LE есть челенджи, с которыми для подтверждения владения доменом не нужно иметь связность снаружи, а например достаточно добавить определенную TXT NS-запись, и после этого можно генерить сертификаты даже для доменов которые указывают на чисто локальные адреса.


          1. lv333
            26.07.2025 10:21

            VPN может быть достаточно лёгким и простым как по настройке, так и для конечного пользователя, например ssh туннель. В одном скрипте запустить туннель и клиент следом. Просто и без затей так говорится и достаточно надёжно в плане перехвата траффика. Все крутится внутри сервера, наружу только один порт и один сервис - красота же!


            1. lv333
              26.07.2025 10:21

              Правда тогда ssl для сервера вроде как и не нужен уже, шифровать внутри зашифрованного туннеля несколько излишне...


            1. Uporoty Автор
              26.07.2025 10:21

              например ssh туннель. В одном скрипте запустить туннель и клиент следом.

              На мобильнике, ага


              1. lv333
                26.07.2025 10:21

                И? Что именно вас смущает? На Андроиде к примеру с этим проблем точно нет, на яблоках возможно... Даже скорее всего! Но тут как бы я не помощник:)


                1. Uporoty Автор
                  26.07.2025 10:21

                  На андроиде это не получится одной командой. Нужно ставить отдельную аппку, настраивать роутинг (чтобы на впн шло только то что надо), а бонусом оно будет еще дополнительно батарею поджирать


                  1. lv333
                    26.07.2025 10:21

                    Ну почему же? На самом деле можно, но пользоваться этим будет не особо удобно... В 16-м Андроиде обещали завезти нативно запуск Линукс приложений, ну а для более ранних версий - termux.


  1. dreams_killer
    26.07.2025 10:21

    Jabber живее всех живых , а вот с клиентами так себе - большинство не умеет otr ( кроме как раз той классики )


    1. Uporoty Автор
      26.07.2025 10:21

      А чем OTR лучше OMEMO?

      OMEMO умели все современные клиенты из тех что я пробовал.


      1. dreams_killer
        26.07.2025 10:21

        Насколько я помню/знаю они очень похожи ( otr и omemo) не возьмусь сказать лучше или хуже. Имею в контакт листе людей общающихся исключительно с otr , по этому проблема поиска клиента с его поддержкой знакома.


        1. mrDoctorWho
          26.07.2025 10:21

          OMEMO является эталоном на текущий момент, он лучше OTR большей стабильностью, возможностью отправки сообщений в оффлайн и поддержкой множества устройств одновременно


          1. dreams_killer
            26.07.2025 10:21

            Что вы имеете ввиду под большей стабильностью?


            1. mrDoctorWho
              26.07.2025 10:21

              Нет проблем с совместимостью, обновлением ключей и внезапной потерей сессии. Дело конечно в реализации, но насколько я помню, она всегда хромала.


  1. 0x00FA7A55
    26.07.2025 10:21

    Зачем prosody-то? Буквально сейчас поднял себе сервак на ejabberd. Кластеризация из коробки, он на elixir, понятный yml в настройках, интеграция с let's encrypt, сразу умеет в stun/turn да и вообще всё что надо. Есть apt и rpm репы. Развивается активно компанией, которая им реально зарабатывает.


    1. mrDoctorWho
      26.07.2025 10:21

      Такой же вопрос — а зачем ejabberd, если на сервере будет полтора анонимуса? Ejabberd это про высокие нагрузки (Whatsapp, eve online), а prosody — про сервер для друзей и родных.


    1. ash_lm
      26.07.2025 10:21

      Ну как минимум он сложнее в настройке и нужно осваивать erlang. А если у вас на сервере десяток человек то зачем городить сложности?


      1. andreymal
        26.07.2025 10:21

        нужно осваивать erlang

        Зачем? У меня личный сервер на ejabberd уже лет пятнадцать, из настроек всего один yaml-файлик, erlang до сих пор не знаю


  1. mrDoctorWho
    26.07.2025 10:21

    Не понимаю где вы в Gajim увидели устаревший интерфейс. Давно уже выглядит как один из самых современных мессенджеров


    1. Uporoty Автор
      26.07.2025 10:21

      Я тестил версию 2.2, она прям вообще олдскульная. Сейчас проверил 2.3, там обновили интерфейс, стало поприличнее, но уж точно не "как один из самых современных" :) Думаю, это уже вопрос вкуса и личных предпочтений


      1. mrDoctorWho
        26.07.2025 10:21

        Это вы похоже не видели олдскульные клиенты — Gajim 0.6, Tkabber, QIP. Так когда-то выглядели все XMPP клиенты.


        1. Uporoty Автор
          26.07.2025 10:21

          Почему же, видел и хорошо их помню. Речь даже не про компоновку окна (олдскульные часто делали отдельное окно под ростер, и отдельное под чаты, а то и под каждый чат), а в целом про то как выглядит приложение - шрифты (и антиалиасинг), иконки, и т.д. Времена-то не стоят на месте.


  1. ash_lm
    26.07.2025 10:21

    А почему был выбор именно в пользу Prosody, а не тот же Snikket, который сразу в докере? Или Openfire, который вообще буквально в пару кликов устанавливается, насколько я помню, к тому же он с самым вменяемым web интерфейсом из всех?


    1. dartraiden
      26.07.2025 10:21

      Openfire

      Придётся втаскивать Java, да и сам по себе Openfire местами весьма творчески интерпретрует спецификациии. По крайней мере, когда к нам в багтрекер приходят с очередным "что-то не работает", чаще всего оказывается, что с ejabberd оно работает, с Prosody работает, а вот Openfire имеет своё особое мнение.


    1. Johan_Palych
      26.07.2025 10:21

      Using Prosody with Docker
      https://prosody.im/doc/docker


      1. Testman2023
        26.07.2025 10:21

        Есть(An Overview of XMPP) и что выбрать по клиентам и серверам
        Понравилась лаба(офис или организация):
        https://github.com/spanishairman/ejabberd-postgresql-debian


      1. ash_lm
        26.07.2025 10:21

        Просто Snikket это уже настроенный, готовый к использованию дочерний проект этих ребят. Сейчас уже не помню, но если мне не изменяет память там даже конфиг править не надо (есть примитивная админка на web).

        P.S. Да и гайд какой-то поверхностный. На вскидку: ничего не сказано, что оф. репозиторий использует устаревший алгоритм (dsa1024), а сейчас многие Linux-дистрибутивы больше не доверяют DSA-ключам. Не раскрыта админка (а штука важная), не раскрыто создание комнат (не менее важно). Модуль bosch почему-то не освещён. Да и про то, что такое "prosodyctl" не мешало бы рассказать.


        1. dartraiden
          26.07.2025 10:21

          На вскидку: ничего не сказано, что оф. репозиторий использует устаревший алгоритм (dsa1024), а сейчас многие Linux-дистрибутивы больше не доверяют DSA-ключам.

          Если человек взялся осваивать линукс, то уж способ сказать пакетному менеджеру "игнорируй то, что ключ ненадёжный" нагуглит.

          К тому же, в этом году планируется замена ключа на более стойкий, просто команда Prosody почему-то тривиальную задачу растягивает на целый год: в начале 2025 они писали, что сгенерируют ключ на каком-то там мероприятии (словно это какой-то торжественный подвиг - вау, смотрите, я умею генерировать ключи), теперь они его сгенерировали и грозятся начать использовать. Вместо того, чтобы просто взять и сделать, они тянут резину.


  1. aax
    26.07.2025 10:21

    Signal - отличный мессенджер и всем хорош.

    Особенно он хорош деаноном при создании аккаунта и централизованной архитектурой.

    Да и не нужно про встроенное сквозное шифрование. Без одноразового шифроблокнота передаваемого независимым от шифруемого канала способом это всего лишь маркетинг. Как и в Телеграмм к примеру.


  1. tsilia
    26.07.2025 10:21

    Как раз на неделе интересовался вопросом. Спасибо за статью, утащил в закладки!

    По поводу сертификатов: не безопаснее ли сгенерить ключ своего собственного certificate authority (CA), которым затем подписывать сертификат сервера, выданный хоть на внутренний домен, хоть на dyndns-домен, хоть на IP, хоть на домен habr.com, как в вашей статье? Если не пользоваться LE или другими публичными CA, то всё равно нужно будет либо какой-то сертификат добавлять в хранилище, либо пинить в самом клиенте или в системе. Имхо, лучше пусть это будет не самоподписанный серт (вдруг забудете запинить его — и привет MITM), а заверенный вашим CA, приватный ключ от которого есть только у вас — никто другой не сможет подписать серт вашего сервера вашим CA. А если запинить сам CA, а другие запретить нафиг, то это ещё секьюрнее, но тут нужна поддержка со стороны клиентского приложения.


    1. Uporoty Автор
      26.07.2025 10:21

      Я выше писал что это тоже вариант, да. Но тогда это потребует ручного добавления вашего корневого серта на все устройства всех ваших пользователей.

      Девайсы на Android в таком случае (когда какой-то левый CA добавлен в доверенные), например, начинают всякие страшные сообщения юзеру показывать.


  1. anzay911
    26.07.2025 10:21

    Мне чатбот предложил поковырять libsignal для локальных решений.


  1. olku
    26.07.2025 10:21

    Можно ли связать несколько серверов вместе как relay? Например, чтобы поднять mesh на OpenWRT и не выходить в Интернет вовсе?


    1. lv333
      26.07.2025 10:21

      Если вам нужно без интернета и достаточно только чата, можно присмотреться к meshtastic и lora, выйдет дешевле, более энергоэффективно, да и проще в целом чем эти все городы из роутеров на опеврт и серверов жаббер.