# Snikket Server XMPP-сервер на базе [Snikket](https://snikket.org/) (обёртка над Prosody с предварительной конфигурацией). ## Состав сервисов | Сервис | Образ | Назначение | |--------|-------|------------| | `snikket_server` | `snikket/snikket-server:stable` | Ядро — Prosody XMPP-сервер | | `snikket_proxy` | `snikket/snikket-web-proxy:stable` | Веб-прокси (nginx) | | `snikket_certs` | `snikket/snikket-cert-manager:stable` | Автоматическое получение TLS-сертификатов (Let's Encrypt) | | `snikket_portal` | `snikket/snikket-web-portal:stable` | Веб-портал для управления пользователями и инвайтами | | `snikket_turn` | `coturn/coturn:latest` | TURN/STUN-сервер для голосовых и видеозвонков | | `s3_upload_handler` | собирается из `./s3-upload-handler` | Обработчик загрузки файлов — принимает файлы от XMPP-клиентов и сохраняет в S3 | | `postgres` | `postgres:15` | База данных PostgreSQL для Prosody | Все сервисы работают в режиме `network_mode: host`. ## Требования ### DNS-записи Для домена `chat.example.org` необходимо создать 3 записи: | Тип | Имя | Значение | |-----|-----|----------| | A | `chat.example.org` | IP-адрес сервера | | CNAME | `groups.chat.example.org` | `chat.example.org` | | CNAME | `share.chat.example.org` | `chat.example.org` | Опционально: AAAA-запись для IPv6, SRV-запись `_xmpps-client._tcp` для подключения через порт 443. ### Порты | Порт | Протокол | Назначение | |------|----------|------------| | 80 | TCP | HTTP (ACME challenges, редирект на HTTPS) | | 443 | TCP | HTTPS (веб-портал, file sharing) | | 5222 | TCP | XMPP client-to-server | | 5269 | TCP | XMPP server-to-server (федерация) | | 5000 | TCP | Proxy65 (передача файлов) | | 5050 | TCP | S3 upload handler (HTTP Upload External) | | 5432 | TCP | PostgreSQL | | 3478 | TCP+UDP | STUN/TURN | | 5349 | TCP+UDP | TURNS (TURN over TLS) | | 49152–65535 | UDP | TURN relay (аудио/видео данные) | ### Настройка файрвола (UFW) ```bash # XMPP и веб ufw allow 80/tcp ufw allow 443/tcp ufw allow 5222/tcp ufw allow 5269/tcp ufw allow 5000/tcp # STUN/TURN (голосовые и видеозвонки) ufw allow 3478/udp ufw allow 3478/tcp ufw allow 5349/tcp ufw allow 5349/udp ufw allow 49152:65535/udp ``` ## Конфигурация ### prosody.cfg.lua Кастомная конфигурация Prosody, монтируется в контейнер `snikket_server` как `/etc/prosody/conf.d/custom.cfg.lua`. Содержит: - **PostgreSQL** — `storage = "sql"` с драйвером PostgreSQL (подключение к `127.0.0.1:5432`) - **HTTP Upload External** — `mod_http_upload_external` для загрузки файлов через внешний S3 upload handler ### secrets.env Файл с секретами, **не коммитится в git** (добавлен в `.gitignore`). Перед первым запуском необходимо создать его и заполнить реальными значениями: ```bash cp secrets.env.example secrets.env # отредактировать secrets.env ``` | Переменная | Описание | |------------|----------| | `UPLOAD_SECRET` | Shared secret (должен совпадать с `http_upload_external_secret` в `prosody.cfg.lua`) | | `AWS_ACCESS_KEY_ID` | Ключ доступа AWS/S3 | | `AWS_SECRET_ACCESS_KEY` | Секретный ключ AWS/S3 | | `POSTGRES_PASSWORD` | Пароль PostgreSQL | | `TURN_SECRET` | Shared secret для TURN-аутентификации (должен совпадать между coturn и Prosody). Генерация: `openssl rand -hex 32` | ### S3 Upload Handler (environment) Несекретные параметры задаются в `docker-compose.yml` в секции `environment` сервиса `s3_upload_handler`: | Переменная | Описание | |------------|----------| | `S3_BUCKET` | Имя S3-бакета | | `S3_REGION` | Регион S3 | | `S3_ENDPOINT` | URL S3-совместимого хранилища (MinIO и др.), не задавать для AWS | | `PRESIGN_EXPIRE` | Время жизни presigned URL для скачивания (секунды, по умолчанию `3600`) | ### snikket.conf ### Обязательные параметры | Переменная | Описание | |------------|----------| | `SNIKKET_DOMAIN` | Домен сервера. **Нельзя изменить после первого запуска.** | | `SNIKKET_ADMIN_EMAIL` | Email администратора (используется для Let's Encrypt) | ### Основные параметры | Переменная | По умолчанию | Описание | |------------|-------------|----------| | `SNIKKET_SITE_NAME` | значение `SNIKKET_DOMAIN` | Человекочитаемое имя сервера | | `SNIKKET_RETENTION_DAYS` | `7` | Сколько дней хранить сообщения (MAM) и загруженные файлы | | `SNIKKET_UPLOAD_STORAGE_GB` | без лимита | Лимит дискового пространства для загруженных файлов (напр. `1.5`) | | `SNIKKET_LOGLEVEL` | `info` | Уровень логирования: `error`, `warn`, `info`, `debug` | | `SNIKKET_ABUSE_EMAIL` | — | Публичный email для жалоб | | `SNIKKET_SECURITY_EMAIL` | — | Публичный email для сообщений о безопасности | | `SNIKKET_UPDATE_CHECK` | `1` | Установить `0` чтобы отключить проверку обновлений | ### Параметры reverse proxy | Переменная | По умолчанию | Описание | |------------|-------------|----------| | `SNIKKET_TWEAK_HTTP_PORT` | `80` | HTTP-порт (изменить при работе за reverse proxy) | | `SNIKKET_TWEAK_HTTPS_PORT` | `443` | HTTPS-порт (изменить при работе за reverse proxy) | ### Параметры TURN-сервера | Переменная | По умолчанию | Описание | |------------|-------------|----------| | `SNIKKET_TWEAK_TURNSERVER` | `1` | Установить `0` для отключения встроенного TURN | | `SNIKKET_TWEAK_TURNSERVER_DOMAIN` | значение `SNIKKET_DOMAIN` | Домен внешнего TURN-сервера | | `SNIKKET_TWEAK_TURNSERVER_PORT` | `3478` | Порт TURN-сервера | | `SNIKKET_TWEAK_TURNSERVER_SECRET` | автогенерация | Shared secret для аутентификации TURN | ### Прочие параметры | Переменная | По умолчанию | Описание | |------------|-------------|----------| | `SNIKKET_TWEAK_IPV6` | `1` | Установить `0` для отключения IPv6 | | `SNIKKET_TWEAK_STORAGE` | `files` | Бэкенд хранения: `files` или `sqlite` (превью) | | `SNIKKET_TWEAK_EXTRA_CONFIG` | — | Путь/glob к дополнительным файлам конфигурации Prosody | | `SNIKKET_PROXY65_PORT` | `5000` | Порт proxy для передачи файлов | | `SNIKKET_TWEAK_DNSSEC` | — | Включить поддержку DNSSEC и DANE | ## Запуск ```bash cd server # 1. Настроить конфигурацию # отредактировать snikket.conf значениями вашего домена # 2. Заполнить секреты cp secrets.env.example secrets.env # отредактировать secrets.env реальными значениями # 3. Запустить docker compose up -d # 3. Создать admin-аккаунт docker exec snikket create-invite --admin --group default ``` Инвайт-ссылку открыть в браузере для регистрации администратора. ## Обновление ```bash cd server docker compose pull docker compose up -d ``` ## Хранение данных Данные хранятся в Docker volumes: | Volume | Содержимое | |--------|-----------| | `snikket_data` | Данные сервера: аккаунты, сообщения, TLS-сертификаты | | `acme_challenges` | Временные файлы ACME (Let's Encrypt) | | `postgres_data` | Данные PostgreSQL | Загруженные файлы хранятся в S3-бакете (не в volumes). ### Бэкап Необходимо сохранить volume `snikket_data` и базу PostgreSQL: ```bash # Snikket data 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) Загруженные файлы (HTTP File Upload, XEP-0363) хранятся в **S3-бакете** через кастомный `s3_upload_handler`. Схема работы: 1. XMPP-клиент запрашивает у Prosody слот для загрузки файла 2. Prosody (`mod_http_upload_external`) генерирует подписанный URL, указывающий на `s3_upload_handler` 3. Клиент загружает файл по этому URL 4. `s3_upload_handler` проверяет HMAC-подпись и сохраняет файл в S3 5. При скачивании — handler генерирует presigned S3 URL и делает redirect