Matrix: децентрализованные открытые мессенджеры с E2E-шифрованием. Обзор возможностей и настройка своего сервера

в 15:58, , рубрики: docker, Element, matrix, мессенджер

В этой статье я расскажу о протоколе Matrix и мессенджерах, основанных на нем, а так же приведу инструкцию по настройке своего сервера и клиентов.

Matrix — открытый протокол мгновенного обмена сообщениями и файлами с поддержкой голосовой и видеосвязи. Почему стоит обратить на него внимание, если у нас уже есть WhatsApp и Telegram? Причин несколько.

Во-первых, в последние дни Роскомнадзор снова начал развлекаться блокировками WhatsApp и Telegram, и иногда у него это даже получается более-менее успешно. В таких условиях всегда неплохо иметь запасной вариант, и Matrix здесь очень хорошо подходит, потому что во-первых он позволяет создавать свои собственные сервера, а во-вторых его протокол со стороны выглядит как самый обычный HTTPS.

Во-вторых, все больше и больше людей не доверяют WhatsApp и Telegram. В случае с Matrix же можно поднять свой личный сервер, протокол Matrix - открытый, исходники клиентов - открыты, исходники серверов - тоже открыты, а в самом протоколе end-to-end шифрование включено для чатов по умолчанию из коробки (в отличие, например, от Telegram, где оно доступно только в "секретных чатах"). Ну и само собой, не требуется нигде вводить телефонный номер для регистрации.

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

Сервер Matrix может работать как изолированно ("только для своих"), так и в составе "федерации" - когда разные серверы общаются между собой, и пользователи, подключенные к разным серверам, могут общаться друг с другом. Есть здесь олды, которые помнят Jabber и IRC? Ну вот, здесь такой же принцип. Я бы даже сказал, что Matrix - это этакий хипстерский Jabber на стероидах.

В принципе, чтобы начать пользоваться Matrix, не нужен даже свой сервер - можно зарегистрироваться на каком-нибудь публичном сервере (самый известный из которых, неожиданно, matrix.org), установить какой-нибудь из клиентов (самый популярный - Element, он есть под Android, iOS, десктопы, и веб), и вперед.

Но мы с вами, как настоящие партизаны, поднимем свой сервер.

Установка и настройка сервера

Окей, будем считать, что у вас уже есть VPS (если нет, то обратите внимание на этот комментарий), на нем установлен какой-нибудь дистрибутив Linux (я предпочитаю Debian или Ubuntu) и произведена базовая настройка (создан пользователь, настроены ssh-ключи, или хотя бы запрещен root-доступ по ssh, настроен fail2ban и обновлены системные пакеты).

Мы будем для простоты устанавливать с помощью Docker (я уже слышу осуждающее "ууууууууу!" со стороны параноиков, но параноики могут собрать контейнеры самостоятельно, или вообще ставиться сразу из исходников). Еще нам нужен будет домен, потому что Matrix работает через TLS/HTTPS. Домен в наши дни стоит копейки, но если совсем жаба душит, можно использовать бесплатный DynDNS-поддомен, например, от dynu.com.

Из чего состоит инсталляция сервера? Из нескольких составляющих.

  1. Сам Matrix-сервер. Мы будем использовать Synapse, он самый популярный и стабильный;

  2. ACME-клиент, чтобы получать TLS-сертификаты, например, от LetsEncrypt;

  3. (опционально) База данных. Synapse может использовать SQLite, но разработчики не рекомендуют делать так на проде (видимо, будет тормозить при более-менее существенной нагрузке) и советуют использовать PostgreSQL. Так мы и поступим;

  4. (опционально) TURN-сервер, мы будем использовать coturn. Он нужен для видео- и аудио-звонков. Matrix использует WebRTC, который подразумевает прямое подключение между пользователями, однако в наши времена повсеместного NAT'а (а нередко даже двойного, один на роутере, другой у провайдера) прямые подключения зачастую невозможны. TURN-сервер работает как "ретранслятор", являясь посредником, позволяющим связаться двум клиентам. Можно использовать публичные TURN-сервера, но раз уж мы ставим все свое, до давайте делать по максимуму все свое;

  5. (опционально) Веб-морда веб-клиент для общения из браузера.

В качестве второго пункта частоприменяют certbot, но с docker его использовать, прямо скажем, неудобно. Вы, конечно, можете создать для него контейнер, пошарить volume сертификатами между контейнерами, но при этом еще придется поломать голову над обновлением сертификатов раз в три месяца и над автоматическим перезапуском контейнеров после этого. Поэтому я сделал все гораздо проще. Мы используем популярный веб-сервер Caddy, и одним выстрелом убьем нескольких зайцев:

  1. Caddy умеет сам автоматически запрашивать и обновлять сертификаты Let's Encrypt для вашего домена;

  2. Caddy у нас будет выступать не только как фронтенд для Matrix-сервера, но и обеспечивать работу веб-клиента из пункта 5 (если он вам нужен);

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

Итак, начнем. Устанавливаете docker и docker-compose (apt install docker.io docker-compose в debian-like системах). Создаете где-нибудь (хорошо подходит /opt) новую папку для всего нашего барахла. Кладем в нее сначала наш docker-compose.yml файл:

version: '3'
services:
  coturn:
    image: coturn/coturn:latest
    restart: unless-stopped
    network_mode: host
    volumes:
      - "./turnserver.conf:/etc/turnserver.conf:ro"

  caddy:
    image: caddy:latest
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 8448:443
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./html:/var/www/html/

  synapse:
    image: docker.io/matrixdotorg/synapse:latest
    restart: unless-stopped
    environment:
      - SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
      - SYNAPSE_SERVER_NAME=YOUR_DOMAIN
      - SYNAPSE_SERVER_ADDRESS=https://YOUR_DOMAIN
      - SYNAPSE_REPORT_STATS=yes
    volumes:
      - ./synapse:/data
    depends_on:
      - db

  db:
    image: docker.io/postgres:12-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_USER=synapse
      - POSTGRES_PASSWORD=changeme
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
    volumes:
      - ./postgres:/var/lib/postgresql/data

Здесь обратите внимание, важно заменить YOUR_DOMAIN на ваш домен. И давайте пройдемся, что из этого что.

coturn - TURN-сервер. В разных мануалах упоминаются разные порты, которые для него необходимо открыть (WebRTC в этом плане вообще штука сложная), я для простоты вынес его в host network. К контейнеру подмонтирован файл ./turnserver.conf с конфигом, к нему мы вернемся чуть позже.

caddy - веб-сервер. Наружу открыты порты 80, 443 и 8448 (последний нужен для федерации серверов, можно и без него, но с ним проще). Файл ./Caddyfile - конфиг веб-сервера, а дира ./html - корень сайта.

synapse - собственно, matrix-сервер. Там все говорит само за себя. И напоминаю о необходимости изменить YOUR_DOMAIN на ваш домен. Данные и конфиги будут лежать в ./synapse .

db - PostgreSQL. POSTGRES_PASSWORD лучше измените на какой-нибудь свой. При первом запуске Постгрес по умолчанию создает базу данных, совпадающую с именем пользователя (у нас synapse) - так проще для нас. Данные самой БД будут лежать в дире ./postgres.

Теперь перейдем к конфигам.

Запустим специальный скрипт, создающий конфиг Synapse:

 docker-compose run --rm synapse generate

Docker создаст контейнеры synapse и db (postgresql), и в ./synapse/homeserver.yaml появится сгенерированный конфиг. Нужно его немного отредактировать: во-первых, поменять SQLite на PostgreSQL, во-вторых настроить TURN.

Для TURN нам понадобится секретный ключ, сгенерировать его можно командой pwgen -s 64 1 .

Закомментируем в homeserver.yaml строки, относящиеся к sqlite, а вместо этого добавим относящиеся к postgresql и turn. Мой конфиг после этого выглядит примерно так:

server_name: "YOUR_DOMAIN"
pid_file: /data/homeserver.pid
listeners:
  - port: 8008
    tls: false
    type: http
    x_forwarded: true
    resources:
      - names: [client, federation]
        compress: false

# Это про SQLite, я закомментировал
#database:
#  name: sqlite3
#  args:
#    database: /data/homeserver.db

# это про PostgreSQL, я добавил
database:
    name: psycopg2
    args:
        user: synapse
        password: ваш_POSTGRES_PASSWORD
        host: db
        database: synapse
        cp_min: 5
        cp_max: 10

# это для TURN, тоже добавляем:
turn_uris: [ "turn:YOUR_DOMAIN?transport=udp", "turn:YOUR_DOMAIN?transport=tcp" ]
turn_shared_secret: "YOUR_KEY"
turn_user_lifetime: 86400000
turn_allow_guests: true

log_config: "/data/YOUR_DOMAIN.log.config"
media_store_path: /data/media_store
registration_shared_secret: "..."
report_stats: true
macaroon_secret_key: "..."
form_secret: "..."
signing_key_path: "/data/YOUR_DOMAIN.me.signing.key"
trusted_key_servers:
  - server_name: "matrix.org"

user_directory:
    enabled: true
    search_all_users: true
    prefer_local_users: true
    show_locked_users: true


# vim:ft=yaml

YOUR_DOMAIN заменяете на ваш домен, а YOUR_KEY - на ключ, который вы до этого сгенерировали pwgen.

Еще я добавил в конце конфига секцию "user_directory", о ней расскажу чуть позже.

Дальше, создаем конфиг coturn в файле ./turnserver.conf:

listening-port=3478

min-port=49152
max-port=65535

static-auth-secret=YOUR_KEY
realm=YOUR_DOMAIN

syslog

no-tcp-relay

denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=172.16.0.0-172.31.255.255

no-multicast-peers
denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255

allowed-peer-ip=YOUR_SERVER_IP

user-quota=12
total-quota=1200

Конфиг взят из примеров в документации Synapse. Тут все то же самое, YOUR_DOMAIN меняем на ваш домен, YOUR_KEY на ключ который вам сгенерировал pwgen, и YOUR_SERVER_IP на айпишник вашего сервера.

Заканчиваем настройку конфигом Caddy в файле ./Caddyfile:

YOUR_DOMAIN {
        reverse_proxy /_matrix/* synapse:8008
        reverse_proxy /_synapse/* synapse:8008
        header /.well-known/matrix/* Content-Type application/json
        header /.well-known/matrix/* Access-Control-Allow-Origin *
        respond /.well-known/matrix/server `{"m.server": "YOUR_DOMAIN:443"}`
        respond /.well-known/matrix/client `{"m.homeserver":{"base_url":"https://YOUR_DOMAIN"}}`
        respond /robots.txt "User-agent: *n Disallow: /" 200

        root /var/www/html
        file_server
        # если вместо локального сайта хотите отдавать какой-нибудь левый сайт, 
        # то вместо root и file_server используйте что-то вроде такого:
        # reverse_proxy http://lib.ru
}

Как обычно, везде меняем YOUR_DOMAIN на ваш домен (и в самом начале тоже!). Этот конфиг говорит, что все запросы с путями /_matrix/ и /_synapse/ нужно передавать на сервер Synapse. То что в .well-known - по идее не обязательно, оно нужно или для федерации если сервер не доступен на 8448 порту (у нас доступен), либо для некоторых тупых клиентов, которые почему-то пытаются туда залезть, поэтому лучше это тоже иметь в конфиге.

А все остальные запросы мы будем обрабатывать как обычный статический сайт, лежащий в локально в ./html снаружи докер-контейнера. Не забудьте создать эту папочку и положить в нее index.html с каким-нибудь содержимым.
Либо, по желанию, вместо этого можно отдавать контент какого-нибудь чужого сайта.

Все готово! Запускаем:

 docker-compose up -d

И после создания контейнеров, проверяем. В браузере по https://YOUR_DOMAIN должно открываться содержимое index.html (фейкового сайта), а при запросе https://YOUR_DOMAIN/_matrix/ выдаваться ошибка типа "Unrecognized request".

Если что-то не работает, ну либо просто ради интереса, чтобы убедиться, что все нормально, можно сделать docker ps, а потом docker logs <container_id> , чтобы посмотреть логи.

Создание пользователей

Можно разрешить регистрацию кому попало в конфиге homeserver.yaml параметром enable_registration: true (там даже можно добавить rate limit и капчу для регистрации, см. документацию Synapse). Либо же можно не разрешать регистрироваться кому попало, а создавать пользователей самому:

docker exec -it synapse_synapse_1 register_new_matrix_user https://YOUR_DOMAIN:8448 -c /data/homeserver.yaml -u USERNAME -a -p PASSWORD

где synapse_synapse_1 - имя вашего контейнера synapse (можно посмотреть его в docker ps ), YOUR_DOMAIN - ваш домен, USERNAME и PASSWORD - логин и пароль для нового пользователя соответственно, пользователь сможет поменять пароль позже из клиента.

Подключаемся клиентом

На примере Element, все очень просто. Устанавливаем его из сторов (если на телефон) или скачивая с сайта.

Жмем sign in:

Matrix: децентрализованные открытые мессенджеры с E2E-шифрованием. Обзор возможностей и настройка своего сервера - 1

Далее, предлагается подключиться к серверу matrix.org - мы же нажимаем Edit и вместо этого вводим там ваш домен. После этого вводим логин и пароль, жмем Next... и мы подключились!

Matrix: децентрализованные открытые мессенджеры с E2E-шифрованием. Обзор возможностей и настройка своего сервера - 2

После этого можно писать лично, создавать и писать в группы, отправлять файлы и звонить:

Matrix: децентрализованные открытые мессенджеры с E2E-шифрованием. Обзор возможностей и настройка своего сервера - 3
Matrix: децентрализованные открытые мессенджеры с E2E-шифрованием. Обзор возможностей и настройка своего сервера - 4

Есть только пара нюансов касательно того, как добавлять других пользователей в контакты и создавать с ними чаты. При нажатии "Start chat" нужно выбрать пользователя с которым вы хотите пообщаться (либо нескольких). По умолчанию там не показываются другие зарегистрированные на сервере пользователи - функционал "user_directory" (поиск пользователей) по умолчанию в Synapse включен, однако клиенту при поиске отдаются только пользователи, с которыми этот клиент уже пересекался - например, состоит в одних и тех же группах.
Поэтому добавлять пользователей в список контактов и создавать с ними чаты можно указывая полный юзернейм, делается это в формате @username:your_domain . После этого клиент "находит" пользователя и показывает его в списке, и вы можете отправить приглашение пообщаться, а после принятия приглашения - писать и звонить. Пользователями, которые у вас уже есть в контактах, можно делиться кнопкой "Share user" - клиент генерирует ссылку и QR-код, которые можно открыть/отсканировать у другого человека на устройстве.

Для неподготовленных пользователей это может оказаться сложновато, поэтому конфиге homeserver.yaml, что приведен выше, в секции "user_directory" я явно установил "search_all_users" как true - то есть сервер будет отдавать клиенту при поиске не только "уже знакомых" пользователей, но и вообще всех существующих. Есть, правда, в этом и побочный эффект: это будут не только пользователи этого сервера, но и соседних (из федерации), про которых сервер уже успел узнать. Впрочем, если вы хотите иметь свой изолированный сервер, можно полностью отключить федерацию, я об этом в конце статьи расскажу.

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

Можно создавать группы (групповые чаты), как личные, так и публичные. Для групп формат похожий: #groupname:your_domain .

Ну и да - если все настроено правильно, то вы со своего сервера можете коммуницировать и с людьми/группами с других серверов (например, того самого matrix.org).

Веб-клиент

Если нужен веб-клиент, то доустановить его очень просто. Я пробовал Element (он более функциональный) и Hydrogen. Для установки достаточно скачать архив со страницы Releases гитхаба и распаковать его куда-нибудь в диру ./html - и все работает :)

Дополнительно можно подправить конфиг, например, разрешить подключаться только к вашему серверу, а не к другим. Для Element содержимое config.json будет таким:

{
    "default_server_config": {
        "m.homeserver": {
            "base_url": "https://YOUR_DOMAIN",
            "server_name": "YOUR_DOMAIN"
        }
    },
    "disable_custom_urls": true,
    "disable_guests": false,
    "disable_login_language_selector": false,
    "disable_3pid_login": false
}

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

Отключение федерации

Чтобы полностью отключить функционал федерации, добавьте в конфиг параметр federation_domain_whitelist: - именно чтобы это был пустой массив.
Плюс можно отключить (удалить) в docker-compose.yaml порт 8448 для caddy, а в конфиге caddy убрать строку про /.well-known/matrix/server

Работа через CDN

Поскольку протокол Matrix основан на обычном REST API, он вполне себе успешно работает через CDN типа Cloudflare или GCore, что может быть полезно если вы, например, не хотите светить реальный IP-адрес сервера. Только имейте в виду, что TURN через CDN работать не будет, поэтому для Synapse и TURN надо использовать разные поддомены, ну и конфигурировать соответственно.

Прокси на том же сервере, что и Matrix

Если у вас на сервере уже установлен XRay, то установка Matrix - отличная возможность использовать вариант "steal-from-yourself" для XTLS-Reality. В Docker-compose замените для caddy строку - 443:443 на - 127.0.0.1:8443:443 . После этого в конфиге XRay для inbound'а с XTLS-Reality вместо чужого домена используйте ваш домен, а в dest укажите 127.0.0.1:8443.

А если вы используете OpenConnect - можно разруливать подключения на 443 порту по SNI, например с помощью haproxy.

Огораживаемся от РКН

И еще маленькая штука, которая может быть полезной для вашего сервера:
Раз - Скрипт для блокировки пауков РКН для серверов на Линуксе
Два - freemedia-tech/iptables-rugov-block (github.com)
Три - РКН - добавить все их IP в черный список — Все о VPN, прокси и свободном интернете (checkvpn.net)

Поднимаем бриджи с WhatsApp/Telegram/etc.

В Matrix можно настроить бриджи (мосты), чтобы вы из клиента Matrix могли коммуницировать с пользователями WhatsApp, Telegram, Instagram, Discord, Slack, Signal, и т.д.

Полный список бриджей можно посмотреть здесь: Matrix.org - Bridges

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

Добавляем в наш docker-compose.yaml контейнер для бриджа mautrix-whatsapp:

  mautrix-whatsapp:
    container_name: mautrix-whatsapp
    image: dock.mau.dev/mautrix/whatsapp:latest
    restart: unless-stopped
    volumes:
      - ./mautrix-whatsapp:/data

Делаем опять docker-compose up -d , после чего у нас в ./mautrix-whatsapp создастся файл config.yaml .

Далее нужно создать новую таблицу в БД PostgreSQL. Сделать это можно как-то так:

docker exec -it <id_вашего_db_контейнера> psql -U synapse -W

ввести пароль для пользователя (он у вас был в docker-compose.yaml), и далее ввести команду

CREATE DATABASE whatsapp;

После этого начинаем редактировать config.yaml. Я приведу те параметры, которые пришлось поменять мне (не забываем подставить YOUR_DOMAIN - домен вашего серверва, YOUR_PASSWORD - пароль на БД, YOUR_USER - ваш юзер, зарегистрированный на сервере, он будет админом бриджа) :

homeserver:
    address: https://YOUR_DOMAIN  # по идее здесь можно было бы сделать http://synapse:8008, но у меня почему-то не заработало
    domain: YOUR_DOMAIN

appservice:
    address: http://mautrix-whatsapp:29318 # тут имя вашего контейнера, и важно продублировать port, без него у меня не заработало
    hostname: 0.0.0.0
    port: 29318

    database:
        type: postgres
        uri: postgres://synapse:YOUR_PASSWORD@db/whatsapp?sslmode=disable

bridge:
...
    permissions:
        "*": relay
        "YOUR_DOMAIN": user
        "@YOUR_USER:YOUR_DOMAIN": admin
...

С помощью docker restart <id-вашего-mautrix-whatsapp-контейнера> перезапускаем mautrix-whatsapp. Теперь, если все правильно, у нас в ./mautrix-whatsapp/ должен был появиться файл registration.yaml . Его нам надо сделать доступным для synapse, можно это сделать в помощью монтирования в docker, а можно тупо скопировать его в ./synapse/whatsapp/ и выставить соответсвующие права.

После чего упомянем его в конфиге homeserver.yaml:

app_service_config_files:
  - /data/whatsapp/registration.yaml

И перезапустим контейнеры synapse и mautrix-whatsapp. С помощью `docker ps` и `docker logs` проверьте, что оба контейнера нормально стартанули и в логах нет ошибок.

Открываем клиент, и зовем пользователя @whatsappbot:YOUR_DOMAINна разговор.

Пишем ему "login", и он сгенерирует нам QR-код, который надо будет скормить приложению WhatsApp где вы залогинены - я не совсем понял, как это сделать, но есть альтернативный вариант, login <номер_телефона_с кодом_и_+> , тогда у вас в WhatsApp вылезет уведомление, которое надо подтвердить, и ввести там код, который вам даст бот.

Готово! Через пару минут бридж начнет создавать "комнаты" для ваших контактов из WhatsApp, подтянет историю чатов с ними со всеми картинками, и вы можете общаться с ними как обычно.

С Telegram примерно такая же история, только там в начале надо зарегистрировать App ID: Initial bridge config - mautrix-bridges На сайте mautrix-bridges много подробной документации.

Автор: UranusExplorer

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js