Jabogram
XMPP-сервер для мессенджера с поддержкой голосовых/видеозвонков и хранением медиафайлов в S3.
Стек:
- Snikket (Prosody) — XMPP-сервер
- PostgreSQL 17 — хранение данных Prosody
- rclone — монтирование S3-бакета как локальной директории
- Docker Compose — оркестрация сервисов
Структура репозитория
jabogram/
├── server/ # Docker Compose + конфиги Snikket/Prosody
│ ├── scripts/ # Скрипты управления пользователями
│ └── ...
└── s3-mount/ # Скрипты монтирования S3 через rclone
Установка с нуля
1. Требования к серверу
- Ubuntu 22.04+ (или Debian 12+)
- Docker + Docker Compose plugin
- Публичный IP-адрес
- Домен, указывающий на этот IP
2. DNS-записи
Создайте три записи для вашего домена (замените chat.example.org на свой):
| Тип | Имя | Значение |
|---|---|---|
| A | chat.example.org |
IP-адрес сервера |
| CNAME | groups.chat.example.org |
chat.example.org |
| CNAME | share.chat.example.org |
chat.example.org |
Записи должны быть активны до первого запуска — они нужны для получения TLS-сертификатов.
3. Настройка файрвола
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 5222/tcp # XMPP client-to-server
ufw allow 5269/tcp # XMPP server-to-server (федерация)
ufw allow 5000/tcp # Proxy65 (передача файлов)
ufw allow 3478/udp
ufw allow 3478/tcp # STUN/TURN
ufw allow 5349/tcp
ufw allow 5349/udp # TURNS (TURN over TLS)
ufw allow 49152:65535/udp # TURN relay (аудио/видео)
4. Монтирование S3 через rclone
S3-бакет монтируется в /mnt/snikket-files и пробрасывается в контейнер Snikket — именно туда сохраняются файлы, загруженные через XMPP (XEP-0363 HTTP File Upload).
4.1. Установка rclone и fuse3
cd s3-mount
sudo bash install-rclone.sh
Скрипт установит fuse3, скачает и установит rclone, после чего запустит rclone config для интерактивной настройки удалённого хранилища.
При конфигурации создайте remote с именем jabogram-vk (или измените имя в rclone-snikket.service), выбрав тип s3 и указав ключи доступа к вашему S3-совместимому хранилищу.
4.2. Настройка systemd-сервиса
Отредактируйте s3-mount/rclone-snikket.service при необходимости — проверьте путь к бакету:
ExecStart=/usr/bin/rclone mount \
jabogram-vk:jabogram/http_file_share \ # <remote>:<bucket>/<path>
/mnt/snikket-files \
...
Установите сервис:
cd s3-mount
sudo bash install-service.sh
Сервис запустится автоматически и будет стартовать при загрузке системы раньше Docker.
Проверьте монтирование:
systemctl status rclone-snikket
ls /mnt/snikket-files
5. Настройка сервера
cd server
5.1. snikket.conf
cp snikket.conf.example snikket.conf
Обязательно задайте:
SNIKKET_DOMAIN=chat.example.org # ваш домен (нельзя изменить после первого запуска!)
SNIKKET_ADMIN_EMAIL=you@example.org # email для Let's Encrypt
5.2. prosody.cfg.lua
cp prosody.cfg.lua.example prosody.cfg.lua
Замените chat.example.org на свой домен в строке:
http_upload_external_base_url = "http://chat.example.org:5050/upload/"
Файл монтируется в контейнер Snikket как
/etc/prosody/conf.d/custom.cfg.luaи задаёт PostgreSQL-бэкенд и внешний обработчик загрузки файлов.
5.3. secrets.env
cp secrets.env.example secrets.env
Заполните реальными значениями:
UPLOAD_SECRET=случайная-строка-32-символа
AWS_ACCESS_KEY_ID=ваш-ключ
AWS_SECRET_ACCESS_KEY=ваш-секретный-ключ
POSTGRES_PASSWORD=надёжный-пароль
UPLOAD_SECRETдолжен совпадать сhttp_upload_external_secretвprosody.cfg.lua— они уже связаны черезos.getenv("UPLOAD_SECRET").
5.4. docker-compose.yml — путь монтирования файлов
В docker-compose.yml найдите строку вида:
- /mnt/snikket-files:/snikket/prosody/share%2ejbr%2ebvn13%2eme/http_file_share
Часть share%2ejbr%2ebvn13%2eme — это URL-encoded путь вида share.<домен>. Замените на свой домен:
- /mnt/snikket-files:/snikket/prosody/share%2echat%2eexample%2eorg/http_file_share
Правило кодирования: каждая точка . заменяется на %2e.
6. Первый запуск
cd server
docker compose up -d
Подождите ~30 секунд, пока Snikket получит TLS-сертификаты. Затем создайте администратора:
docker exec snikket create-invite --admin --group default
Команда выведет инвайт-ссылку вида https://chat.example.org/invite/... — откройте её в браузере для регистрации первого администратора.
Управление пользователями
Все скрипты находятся в server/scripts/ и читают SNIKKET_DOMAIN из snikket.conf.
| Скрипт | Действие |
|---|---|
invite-new.sh |
Создать инвайт для обычного пользователя |
invite-new-admin.sh |
Создать инвайт для администратора |
invite-list.sh |
Показать список инвайтов |
invite-revoke.sh |
Отозвать инвайт |
user-delete.sh |
Удалить пользователя |
cmd.sh |
Выполнить произвольную команду prosodyctl |
Обновление
cd server
docker compose pull
docker compose up -d
Бэкап
cd server
# Данные Snikket (аккаунты, сертификаты, настройки)
docker run --rm \
-v jabogram_snikket_data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/snikket-backup.tar.gz -C /data .
# База данных PostgreSQL
docker exec snikket-postgres pg_dump -U snikket snikket \
| gzip > snikket-pg-backup.sql.gz
Медиафайлы хранятся в S3-бакете и бэкапируются отдельно средствами вашего S3-провайдера.
Архитектура хранения файлов
XMPP-клиент
│
│ 1. Запрос слота (XEP-0363)
▼
Prosody (mod_http_upload_external)
│
│ 2. Выдаёт подписанный URL → http://chat.example.org:5050/upload/...
▼
s3_upload_handler (порт 5050)
│
│ 3. Проверяет HMAC, сохраняет файл в S3
│ (или через /mnt/snikket-files, смонтированный rclone)
▼
S3-бакет
Открытые порты
| Порт | Протокол | Назначение |
|---|---|---|
| 80 | TCP | HTTP (ACME, редирект на HTTPS) |
| 443 | TCP | HTTPS (веб-портал, файлы) |
| 5222 | TCP | XMPP client-to-server |
| 5269 | TCP | XMPP server-to-server |
| 5000 | TCP | Proxy65 (передача файлов) |
| 5050 | TCP | S3 upload handler |
| 3478, 5349 | TCP+UDP | STUN/TURN (звонки) |
| 49152–65535 | UDP | TURN relay (аудио/видео) |