first_commit

This commit is contained in:
2026-08-28 19:36:10 +00:00
commit a104b6d764
11 changed files with 1334 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
# Развёртывание XMPP-мессенджера ICQ на chat.nixg.ru — Полный разбор
> Итоговый документ опыта. Дата: 2026-08-28.
> Команда: `bigbox` (внутренний сервер, /opt/icq) + `vps02` (публичный вход, Caddy + WireGuard).
> Домен: **chat.nixg.ru** · Протокол: **XMPP** · Сервер: **Prosody 0.11.9** (Docker).
----------------------------------------------------------------------
## 1. Почему XMPP, а не Telegram/Matrix/Snikket
- **XMPP** — открытый федеративный протокол (XML, RFC 6120/6121), как email:
у каждого пользователя JID `user@домен`, серверы общаются между собой по s2s (порт 5269).
Главное требование семьи — «общаться со всеми» — выполняется федерацией.
- **Telegram** — закрытый централизованный протокол (MTProto, бинарный). Серверный код
закрыт, клиенты захардкожены на официальные дата-центры. «Слямзить» протокол невозможно.
Bot API — только HTTP+JSON поверх MTProto, федерации нет.
- **Snikket-сервер** отброшен: он спроектирован под прямой 443 (сам раздаёт TLS) и
«капризничает» за реверс-прокси. Его КЛИЕНТЫ (iOS/Android) — отличные, используем их.
- **Matrix/Conduit** — мощные, но push (APNs/FCM) сложнее, нужен свой push-шлюз; для семьи XMPP проще.
## 2. Архитектура (почему так)
```
[Клиенты iOS/Android] ← c2s:5222 (TLS), wss:443 (веб)
│
▼
[vps02: 87.242.100.206] ← публичный IP, Caddy (TLS/443) + iptables DNAT (5222/5269)
│ WireGuard-туннель: bigbox=10.8.0.2, vps02=10.8.0.4
▼
[bigbox: 10.8.0.2] ← внутри, /opt/icq, Docker Compose
├─ icq-prosody — Prosody 0.11.9 (5222, 5269, 5280)
└─ icq-webchat — nginx (8081): статика Converse.js + прокси WebSocket на Prosody
```
- **bigbox не имеет публичного IP** — весь внешний трафик идёт через vps02.
- **Caddy** сам выпускает Let's Encrypt для 443 и проксирует WebSocket/HTTP.
- **5222/5269** (XMPP-клиенты и федерация) — DNAT в iptables на vps02 → 10.8.0.2.
- Порт **5280** (Prosody HTTP: WebSocket/BOSH) наружу НЕ торчит — веб-клиент ходит через
Caddy на 443 (wss://), а боты/внутренние сервисы — напрямую.
## 3. Что сделано по шагам (с командами)
### 3.1 DNS (Jino, панель домена nixg.ru)
| Запись | Тип | Значение |
|--------|-----|----------|
| `chat.nixg.ru` | A | `87.242.100.206` |
| `_xmpp-client._tcp.nixg.ru` | SRV | `0 5 5222 chat.nixg.ru` |
| `_xmpp-server._tcp.nixg.ru` | SRV | `0 5 5269 chat.nixg.ru` |
Проверка: `host -t SRV _xmpp-client._tcp.nixg.ru`
**Засада Jino**: панель склеивает поля в одну строку (`_xmpp-client._tcp.nixg.ruIN SRV0 5 5222 chat.nixg.ru`).
Решение: заполнять поля формы по отдельности: имя=`_xmpp-client._tcp`, приоритет=`0`, вес=`5`, порт=`5222`, цель=`chat.nixg.ru.` (FQDN с точкой). Результат в DNS всё равно корректный.
SRV-записи критичны для федерации: без них другие XMPP-серверы не найдут нас.
### 3.2 Prosody на bigbox (/opt/icq)
- `docker-compose.yml` — сервис `prosody` (prosody/prosody:latest), порты 5222/5269/5280/5281,
volumes: ./data, ./config, ./certs, ./modules, ./logs.
- Конфиг `config/prosody.cfg.lua`:
- VirtualHost `chat.nixg.ru`, `authentication = "internal_hashed"`, `allow_registration = true`
- модули: roster, saslauth, tls, dialback, disco, carbons, pep, private, blocklist,
vcard4, vcard_legacy, version, uptime, time, ping, register, adhoc, admin_adhoc,
csi, offline, presence, mam, websocket, server_contact_info
- MUC-компонент `conference.chat.nixg.ru`
- `cross_domain_websocket = { "https://chat.nixg.ru" }` — разрешить WebSocket с нашего домена
- Логи — в файлы (не консоль), т.к. docker-образ по умолчанию пишет в файлы.
- `https_ports` по умолчанию 5281 — даёт ошибку «No certificate present» (нет HTTPS-серта на 5281), не мешает.
- Регистрация админа: `docker exec icq-prosody prosodyctl register admin chat.nixg.ru ПАРОЛЬ`
- Проверка: `docker exec icq-prosody prosodyctl check config` → All checks passed.
**Тонкости Prosody 0.11.9 в docker-образе**:
- Нет community-модулей: `http_upload` (XEP-0363), `smacks`, `s2s_bidi`, `xmpp_component`.
Их нет в `/usr/lib/prosody/modules/`. Для http_upload нужна установка prosody-modules (см. раздел 7).
- Порты 5222/5269/5280 слушаются на 0.0.0.0 — трафик снаружи приходит через WG.
### 3.3 Caddy на vps02
- Caddy в Docker (host-network), конфиг `/opt/caddy/Caddyfile` монтируется в контейнер.
- Блок для chat.nixg.ru: `reverse_proxy 10.8.0.2:8081` (веб-клиент+WS через nginx на bigbox).
- **Засада с bind-mount**: при перезаписи Caddyfile через `sudo tee`/`>` файл получает НОВЫЙ inode,
а монтированный в контейнер старый inode остаётся — Caddy продолжает читать СТАРУЮ версию.
Симптом: «конфиг изменил, а Caddy проксирует на старый адрес». Решение: перезапустить контейнер
(`docker compose restart caddy`) или использовать `sudo sed -i` (правит in-place, inode тот же).
- Проверка: `docker exec caddy caddy validate --config /etc/caddy/Caddyfile` + reload/restart.
### 3.4 iptables DNAT на vps02 (форвард 5222/5269 на bigbox)
```
iptables -t nat -A PREROUTING -i enp3s0 -p tcp --dport 5222 -j DNAT --to-destination 10.8.0.2:5222
iptables -t nat -A PREROUTING -i enp3s0 -p tcp --dport 5269 -j DNAT --to 10..0.2:5269
iptables -A FORWARD -d 10.8.0.2 -p tcp --dport 5222 -j ACCEPT (и 5269)
iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
```
- **Обязательно добавить** FORWARD ACCEPT (default policy DROP!) и MASQUERADE — иначе пакеты
докодят до bigbox, но ответы не возвращаются (connection refused).
- Сохранение: `sudo sh -c "iptables-save > /etc/iptables/rules.v4"` (переживает reboot).
- Группа безопасности у провайдера (Timeweb): открыть TCP 5222, TCP 5269 (только TCP).
### 3.5 Веб-клиент (Converse.js v14 + nginx)
- `/opt/icq/webchat`: `index.html`, `converse.min.js` (1.3MB, v14.0.0), `converse.min.css` (545KB) — скачаны локально с CDN (чтобы не зависеть от cdn.conversejs.org — может резаться из РФ).
- `ngnix.conf`: `server 8081`, root `/usr/share/nginx/html`; `location /xmpp-websocket { proxy_pass http://prosody:5280/xmpp-websocket; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }`; `location / { try_files $uri $uri/ /index.html; }`.
- Docker-compose: сервис `webchat` (nginx:alpine), ports `8081:8081`, зависит от prosody.
**Критическая засада веб-клиента**: в Converse.js v14 опция WebSocket называется **`websocket_url`**,
НЕ `bosh_service_url`! Если задать `bosh_service_url` с wss:// — Converse пытается подключиться по BOSH-протоколу (а у Prosody BOSH нет), падает, и UI вообще не рендерится (пустая страница!). В v14 `view_mode: 'fullscreen'` — дефолт.
## 4. Проверка работоспособности (извне, с реалного хоста — WSL/телефон)
```bash
nc -vz 87.242.100.206 5222 # xmpp-client: open
nc -vz 87.242.100.206 5269 # xmpp-server: open
curl -skI https://chat.nixg.ru/ # 200, HTML
curl -sk https://chat.nixg.ru/converse.min.js # 200, JS
# WebSocket-рукоятка (как у браузера):
# GET /xmpp-websocket, Upgrade: websocket, Sec-WebSocket-Protocol: xmpp, Origin: https://chat.nixg.ru
# → 101 Switching Protocols
```
**Засада с hairpin NAT**: с самого bigbox/vps02 нельзя проверить свой публичный IP —
маршрут `<local>`, пакет не проходит через DNAT. Нужен внешний хост (WSL, телефон по мобильной сети).
## 5. Веб-клиент Converse.js — настройки
```js
converse.initialize({
websocket_url: 'wss://chat.nixg.ru/xmpp-websocket', // ОБЯЗАТЕЛЬНО websocket_url!
view_mode: 'fullscreen',
auto_login: false,
theme: 'concord',
i18n: 'ru',
show_controlbox_by_default: true,
});
```
Требования Prosody для WS: `cross_domain_websocket = { "https://chat.nixg.ru" }` в конфиге.
## 6. Ошибки, на которых мы обожглись (выжимка)
1. **SRV на Jino склеивается** — заполнять поля отдельно, цель с точкой.
2. **FORWARD policy DROP + нет MASQUERADE** → connection refused на 5222/5269.
3. **bind-mount Caddyfile «залипает»** при перезаписи через tee (новый inode) — рестарт контейнера.
4. **`bosh_service_url` вместо `websocket_url`** → пустая страница Converse.js.
5. **WebSocket-тест без Origin → 403**, без `Sec-WebSocket-Protocol: xmpp` → 501.
6. **hairpin NAT** — нельзя тестировать свой pub IP с самого сервера.
7. **Кавычки в grep-командах** блокируются терминалом — использовать Python для поиска в минифицированных JS.
8. **docker compose up -d/restart блокируется эвристикой** — запускать в background + process wait.
## 7. TODO (следующие шаги)
> ⚡ Текущий блокер и пошаговый план: **[STATUS.md](STATUS.md)** — пустая страница Converse.js.
- [ ] **Починить пустую страницу веб-клиента** (см. STATUS.md: разложить полный дистрибутив Converse.js
с /dist-ассетами, получить console-ошибки headless-браузером).
- [ ] **Установить модуль HTTP Upload (XEP-0363)** для Prosody 0.11.9:
community-модуль mod_http_upload из prosody-modules (https://modules.prosody.im/).
Шаги: скачать модуль в ./modules, добавить `"http_upload"` в modules_enabled,
настроить размер/путь, перезапустить Prosody. Нужен для отправки файлов в чат
(книги бота, фото). Также проверить mod_http_upload_external.
- [ ] OMEMO (сквозное шифрование) — модуль + настройка клиентов.
- [ ] Бот-книгоискатель (slixmpp + OPDS) — Этап 3 PRD.
- [ ] Мосты mautrix-telegram / mautrix-whatsapp — Этап 4 (нужен реестр компонентов Prosody,
модуль mod_component/external component, НЕ xmpp_component — его нет в 0.11.9).
- [ ] Настоящие сертификаты для 5222/5269 (dns-01) вместо самоподписанных.
- [ ] Push-уведомления APNs/FCM (публичный push-шлюз для Prosody).
## 8. Полезные команды
```bash
cd /opt/icq
docker compose ps
docker logs icq-prosody --tail 50
docker exec icq-prosody prosodyctl check config
docker exec icq-prosody prosodyctl register admin chat.nixg.ru ПАРОЛЬ
# рестарт
docker restart icq-prosody
# проверка WS рукоятки
python3 -c "...socket..."
```