↓ Перейти к основному содержимому
  1. Posts/
  2. Self-Hosting/

Matrix Synapse - разворачиваем свой сервер для децентрализованного чата

·2121 слово·11 минут· loading · loading · ·
Stilicho2011
Автор
Stilicho2011
Пишу о homelab, self-hosting, автоматизации и open-source решениях
Оглавление
Self-Hosting - This article is part of a series.
Part : This Article

Что такое Matrix и зачем свой сервер
#

Matrix - открытый децентрализованный протокол для обмена сообщениями, похожий по духу на работу email. У каждого сервера свой домен, а серверы федерируются между собой, так что пользователи разных серверов могут общаться друг с другом напрямую, без единой центральной компании посередине. Synapse - эталонная (и самая распространенная) реализация сервера этого протокола, разработанная Matrix.org.

Для homelab свой Matrix-сервер решает ту же самую проблему, что и любой другой self-hosted мессенджер: чаты, голосовые и видеозвонки, обмен файлами - но без привязки к чужой инфраструктуре и с полным контролем над данными.

При этом, в отличие от полностью изолированных решений, например Nextcloud Talk, федерация по желанию оставляет возможность общаться и с пользователями других Matrix-серверов, включая крупную публичную сеть matrix.org.

Сам протокол появился в 2014 году - его придумали Мэттью Ходжсон (Matthew Hodgson) и Амандин Ле Пап (Amandine Le Pape), выходцы из корпоративного проекта унифицированных коммуникаций Vector, которым они занимались еще в Amdocs (отсюда и первое название клиента - Vector/Riot, переименованного в Element только в 2018 году). В 2019 году появился отдельный некоммерческий Matrix.org Foundation, который следит за спецификацией протокола отдельно от коммерческой разработки клиента. Самый известный кейс этой истории - ЦЕРН, который в 2019 году начал переводить около 18 000 сотрудников со Skype for Business на свой Matrix/Element именно из-за открытости протокола и возможности полностью держать коммуникации на собственной инфраструктуре. Поэтому это не какой-то вайбкод-проект любителя, а самая что ни на есть энтерпрайзная технология, которая изначально была разработано с учетом корпоративной специфики, но которую мы можем использовать дома.

Note

Synapse - не единственная реализация сервера Matrix (есть более легковесные Dendrite и Conduit, которые возможно для дома были бы более предпочтительны), но именно Synapse самая зрелая реализация и лучше всего документирована, поэтому в статье используется Synapse.

Чем Matrix отличается от Telegram и WhatsApp
#

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

  • Централизация против федерации. У Telegram и WhatsApp все сообщения в конечном счете проходят через серверы одной компании (Telegram и Meta1 соответственно) - других серверов в этих сетях просто не существует.

Matrix - федеративный протокол: сервер может поднять кто угодно, и такие серверы общаются друг с другом напрямую, как почтовые серверы в email. Ваш homelab-сервер - равноправный участник сети, а не клиент чужой инфраструктуры.

  • Кому принадлежат данные. В Telegram и WhatsApp сообщения (даже если они шифруются на транспорте) физически лежат на серверах вендора. В self-hosted Matrix вся история переписки, медиафайлы и метаданные хранятся там, где вы сами развернули Synapse - в вашей базе Postgres и на вашем диске.

  • Сквозное шифрование по умолчанию. WhatsApp включает E2E-шифрование для всех чатов по умолчанию. В Telegram оно есть только в отдельных “секретных чатах”, если не ошибаюсь, - обычные чаты и группы шифруются лишь на уровне транспорта до сервера, и сама компания технически может прочитать их содержимое. Хотя тут я не уверен в достоверности моей информации.

В Matrix сквозное шифрование (протокол Olm/Megolm) можно включить для любой комнаты, и на практике клиенты давно включают его по умолчанию для приватных чатов.

  • Открытый протокол и открытый код. Спецификация Matrix открыта и стандартизирована Matrix.org Foundation, серверная часть (Synapse, Dendrite, Conduit) и клиентская (Element и другие) части - open source, их может проверить и форкнуть кто угодно. Протоколы Telegram и WhatsApp закрытые: клиенты и API контролируют сами компании, а совместимость сторонних реализаций держится на неофициальном реверс-инжиниринге и может быть отключена в любой момент.

  • Устойчивость к блокировкам. Заблокировать Telegram или WhatsApp - это заблокировать конкретную компанию и ее домены/IP-диапазоны, что уже не раз пытались делать в разных странах. Заблокировать Matrix целиком технически почти невозможно: серверов тысячи, у каждого свой домен, и блокировка одного сервера (даже публичного matrix.org) не трогает остальные - в том числе ваш собственный.

Из этого следует практический вывод: если решающий фактор для вас - удобство и большое количество контактов, ни Telegram, ни WhatsApp Matrix не заменит. Но если важны контроль над данными, независимость от решений одной компании и устойчивость к блокировкам конкретного сервиса - именно для этого нужен собственный Matrix-сервер. При этом вы можете его реализовать как у себя в компании для своих сотрудников, так и просто для своих родственников и близких.

Что понадобится
#

  • хост с Docker;
  • домен и настроенный обратный прокси (в моем случае Traefik) - в статье он используется. Можно обойтись и без него, опубликовав порт напрямую, но это самое плохое решение, которое может быть;
  • немного терпения и времени на этапе генерации конфига и секретов - шагов много, но каждый сам по себе достаточно простой.

Для базы данных Synapse будем использовать PostgreSQL, а не встроенный SQLite. Это не вопрос вкуса или удобства. Разработчики прямо предупреждают, что SQLite подходит только для тестов и не годится для реальной эксплуатации из-за ограничений по производительности при параллельном доступе. В переводе на русский, больше двух-трех человек на сервере и целостность SQlite под угрозой.

Шаг 1. Генерируем первоначальный конфиг
#

Synapse может сам сгенерировать рабочий homeserver.yaml . Используем один и тот же образ, но с командой generate. Это одноразовый запуск образа только для генерации рабочего конфига, а не постоянный сервис.

Создаем структуру папок:

mkdir -p /home/stilicho/docker/synapse/data
cd /home/stilicho/docker/synapse

Создаем временный файл командой sudo nano docker-compose.yaml только для генерации конфига:

services:
  synapse:
    image: docker.io/matrixdotorg/synapse:latest
    command: generate
    environment:
      - SYNAPSE_SERVER_NAME=matrix.stilicho.ru
      - SYNAPSE_REPORT_STATS=no
    volumes:
      - ./data:/data
Warning

SYNAPSE_SERVER_NAME - это не просто домен, на котором крутится сервис, а имя сервера, которое навсегда будет ассоциироваться с идентификаторами всех ваших пользователей (@user:matrix.stilicho.ru) и адресов комнат. Сменить его после первого запуска нельзя - точнее можно, но только через создание нового сервера. Так что, нет, нельзя. Или да, нельзя. Если сомневаетесь, выбрать домен надо заранее и раз и навсегда, включая то, будет ли это отдельный поддомен (как в этой статье) или голый домен без поддомена, определенным способом связанным через .well-known-делегирование - об этом ниже.

Запускаем генерацию:

docker compose run --rm synapse

В папке data появится готовый homeserver.yaml, а также автоматически сгенерированные matrix.stilicho.ru.log.config и matrix.stilicho.ru.signing.key - последний файл - это криптографический ключ, которым сервер подписывает исходящие федеративные события. Его надо беречь как зеницу ока .

После генерации временный compose-файл больше не нужен. Позже создадим рабочий.

Tip

Если, предположим, сервер должен откликаться на домен второго уровня (@user:stilicho.ru), а не на поддомен, технически Synapse все равно должен физически работать на отдельном поддомене (например, на том же matrix.stilicho.ru) - а голый домен нужно “делегировать” ему через два файла: https://stilicho.ru/.well-known/matrix/server и https://stilicho.ru/.well-known/matrix/client, отдающие JSON с адресом реального сервера. Это отдельная тема, в настоящей статье я ее не разбираю - для первого сервера проще и надежнее сразу использовать поддомен как есть.

Шаг 2. Правим сгенерированный homeserver.yaml
#

Открываем data/homeserver.yaml и вносим несколько правок.

Переключаемся на PostgreSQL . Как я писал выше, по умолчанию сгенерированный конфиг использует SQLite, поэтому удаляем упоминание sqlite и вставляем указанную ниже часть:

database:
  name: psycopg2
  txn_limit: 10000
  args:
    user: synapse
    password: "замените_на_пароль_из_env"
    dbname: synapse
    host: synapse_db
    port: 5432
    cp_min: 5
    cp_max: 10
  • host: synapse_db - это имя сервиса Postgres из compose-файла (см. шаг 3), Docker DNS резолвит его автоматически внутри общей сети, никакого дополнительного IP прописывать не нужно.
  • password - должен буквально совпадать со значением POSTGRES_PASSWORD из .env файла compose-стека - это два независимых места, где хранится один и тот же пароль, и их рассинхронизация - самая частая причина, по которой Synapse не может подключиться к базе после первого запуска.

Отключаем открытую регистрацию - по умолчанию в сгенерированном файле ключ вообще отсутствует, поэтому это надо прописать явно:

enable_registration: false

Без этого значения любой человек, узнавший адрес вашего сервера, сможет сам зарегистрировать себе аккаунт на вашем сервере. Пользователей на домашнем сервере будем создавать вручную, отдельной командой (см. шаг 6).

Caution

В сгенерированном файле уже есть три секрета: registration_shared_secret, macaroon_secret_key и form_secret. Synapse генерирует их случайным образом, при первом запуске - в отличие от готовых демо-конфигов, которые иногда гуляют по интернету (например, в чужих репозиториях с примерами docker-compose), где эти значения могут быть одними и теми же у всех, кто скопировал файл не глядя. Если вы все же переносите конфиг из чужого примера - обязательно сгенерируйте свои значения заново, например командой openssl rand -hex 32, и не используйте то, что было написано в примере изначально.

Остальные ключи (server_name, pid_file, listeners, media_store_path, signing_key_path, trusted_key_servers) сгенерированы верно и трогать их не нужно.

Шаг 3. Финальный docker-compose.yaml
#

services:
  synapse:
    image: docker.io/matrixdotorg/synapse:latest
    container_name: matrix-synapse
    restart: unless-stopped
    environment:
      - TZ=${TZ}
      - SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
    volumes:
      - ./data:/data
    depends_on:
      - synapse_db
    networks:
      - synapse
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.synapse.entrypoints=web"
      - "traefik.http.routers.synapse.rule=Host(`matrix.stilicho.ru`)"
      - "traefik.http.middlewares.synapse-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.synapse.middlewares=synapse-https-redirect"
      - "traefik.http.routers.synapse-secure.entrypoints=websecure"
      - "traefik.http.routers.synapse-secure.rule=Host(`matrix.stilicho.ru`)"
      - "traefik.http.routers.synapse-secure.tls=true"
      - "traefik.http.routers.synapse-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.synapse-secure.service=synapse"
      - "traefik.http.services.synapse.loadbalancer.server.port=8008"
      - "traefik.docker.network=proxy"

  synapse_db:
    image: docker.io/postgres:16-alpine
    container_name: matrix-postgres
    restart: unless-stopped
    environment:
      - TZ=${TZ}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
    volumes:
      - ./postgres:/var/lib/postgresql/data
    networks:
      - synapse

networks:
  synapse:
  proxy:
    external: true

Давайте разберем отличия от того, что генерирует сам Synapse “из коробки” в примерах из интернета:

  • networks: synapse и proxy - synapse - внутренняя сеть только для связи с Postgres, создается автоматически; proxy - внешняя сеть Traefik (external: true), должна существовать заранее (docker network create proxy, если еще не создана под другие сервисы). База synapse_db сидит только в сети synapse - снаружи она никому, кроме самого Synapse, не нужна.
  • labels - тот же паттерн, что и в остальных статьях сайта с использованием Traefik в качестве обраного прокси: HTTP-роутер с редиректом на HTTPS, HTTPS-роутер с TLS-резолвером cloudflare (замените на имя своего резолвера, если оно другое) и балансировщик на внутренний порт 8008 контейнера - именно на нем Synapse слушает HTTP по умолчанию.
  • POSTGRES_INITDB_ARGS - принудительно задает кодировку и сортировку базы в C/UTF-8 при первой инициализации. Это важно сделать именно на старте.
  • ./postgres:/var/lib/postgresql/data - bind mount вместо именованного volume, по тому же принципу, что и в остальных мои примерах развертывания сервисов. Путь на хосте прописан явно, так проще делать бэкапы.

.env-файл рядом с compose-файлом:

TZ=Europe/Moscow
POSTGRES_USER=synapse
POSTGRES_PASSWORD=замените_на_свой_пароль
Warning

Значение POSTGRES_PASSWORD здесь должно быть точно таким же, как то, что вы прописали в password внутри database.args в homeserver.yaml на шаге 2. Не коммитьте .env в git - если ведете конфиги через GitOps (см. статью про Forgejo и Komodo), добавьте .env в .gitignore.

Поднимаем стек:

docker compose up -d

Первый запуск Postgres инициализирует структуру данных, Synapse может перезапуститься один-два раза, пока база не станет доступна - это нормально.

Шаг 4. Проверяем, что сервер поднялся
#

Открываем https://matrix.stilicho.ru в браузере - Synapse отдает простую страницу с текстом вроде “Welcome to the Matrix”, это и есть признак того, что сервер работает и Traefik успешно до него достучался.

Более наглядная проверка - через федеративный tester от самого проекта Matrix.org: https://federationtester.matrix.org/, вводим туда matrix.stilicho.ru - если все настроено верно (включая DNS и сертификат), тест покажет зеленый статус федерации, даже если вы пока не планируете общаться с внешними серверами.

Шаг 5. Ротация логов
#

По умолчанию log_config, сгенерированный на шаге 1, настроен на ротацию по размеру, но всегда полезно свериться с реальным содержимым файла data/matrix.stilicho.ru.log.config - там задается, помимо прочего, уровень логирования (INFO по умолчанию - для домашнего сервера более чем достаточно, DEBUG быстро разрастется в гигабайты бесполезных для домашнего пользователя данных).

Шаг 6. Создаем первого пользователя
#

Раз открытая регистрация отключена (шаг 2), пользователей создаем вручную, встроенным скриптом внутри контейнера:

docker exec -it matrix-synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008

Скрипт спросит логин, пароль и - отдельным вопросом - права администратора. Права администратора имеет смысл давать только себе; остальным пользователям они не нужны для обычного общения.

Note

Под капотом скрипт использует тот самый registration_shared_secret из homeserver.yaml - именно поэтому регистрация через эту команду работает, даже когда строка конфигаenable_registration: false запрещает регистрацию всем остальным.

Шаг 7. Подключаем клиент
#

Официальный клиент - Element (веб, десктоп для Windows/macOS/Linux, мобильные приложения для Android/iOS). При первом входе в любой версии клиента:

  1. Выбираем Sign in.
  2. Меняем домен сервера по умолчанию (matrix.org) на свой - обычно есть кнопка вроде Edit рядом с полем адреса сервера.
  3. Указываем https://matrix.stilicho.ru.
  4. Вводим логин и пароль, созданные на шаге 6.

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

Что дальше
#

Показанная здесь схема - минимальная рабочая установка. Дальше есть куда расти:

  • Голосовые и видеозвонки. Тема оказалась настолько объемной, что я вынес ее в отдельную статью - «Голосовые и видеозвонки в Matrix - разворачиваем LiveKit и Element Call». Спойлер: обычный TURN-сервер (coturn) уже не главный путь к звонкам - современные клиенты вроде Element X перешли на архитектуру MatrixRTC с LiveKit-бэкендом;
  • Мосты (bridges) в другие мессенджеры (Telegram, WhatsApp, Discord) - позволяют читать и писать в чужие сети прямо из Matrix-клиента;
  • Media repository отдельным сервисом, если начнет расти объем пересылаемых файлов и изображений;
  • Резервное копирование - как минимум папки data (конфиг, ключи, медиа) и дампов Postgres; про сами инструменты бэкапа - в отдельных статьях на сайте.

Но для первого знакомства с собственным Matrix-сервером всего показанного выше, с моей точки зрения, достаточно. Рабочий сервер за Traefik, безопасные секреты, отключенная открытая регистрация и подключенный клиент.

Ссылки
#


  1. Организация признана экстремистской и ее деятельность запрещена на территории РФ. ↩︎

Self-Hosting - This article is part of a series.
Part : This Article

Related

Grimmory - self-hosted библиотека и читалка для вашей коллекции книг

··1183 слов·6 минут· loading · loading
Подробная инструкция по установке и настройке Grimmory: развертывание в Docker, добавление книг, организация библиотеки и использование встроенной веб-читалки.

BookLore - self-hosted библиотека и читалка для вашей коллекции книг

··1422 слов·7 минут· loading · loading
Подробная инструкция по установке и настройке BookLore: развертывание в Docker, добавление книг, организация библиотеки и использование встроенной веб-читалки.

GitOps для homelab - разворачиваем Forgejo и Komodo с нуля

··5744 слов·28 минут· loading · loading
Подробный туториал по GitOps для начинающих: разворачиваем Forgejo как собственный git-сервер, Komodo для управления Docker-стеками и настраиваем автодеплой по push, с разбором каждого параметра конфигурации.